Squashed 'third_party/allwpilib/' changes from 66b57f032..e473a00f9
e473a00f9 [wpiutil] Base64: Add unsigned span/vector variants (#3702)
52f2d580e [wpiutil] raw_uv_ostream: Add reset() (#3701)
d7b1e3576 [wpiutil] WebSocket: move std::function (#3700)
93799fbe9 [examples] Fix description of TrapezoidProfileSubsystem (#3699)
b84644740 [wpimath] Document pose estimator states, inputs, and outputs (#3698)
2dc35c139 [wpimath] Fix classpaths for JNI class loads (#3697)
2cb171f6f [docs] Set Doxygen extract_all to true and fix Doxygen failures (#3695)
a939cd9c8 [wpimath] Print uncontrollable/unobservable models in LQR and KF (#3694)
d5270d113 [wpimath] Clean up C++ StateSpaceUtil tests (#3692)
b20903960 [wpimath] Remove redundant discretization tests from StateSpaceUtilTest (#3689)
c0cb545b4 [wpilibc] Add deprecated Doxygen attribute to SpeedController (#3691)
35c9f66a7 [wpilib] Rename PneumaticsHub to PneumaticHub (#3686)
796d03d10 [wpiutil] Remove unused LLVM header (#3688)
8723caf78 [wpilibj] Make Java TrapezoidProfile.Constraints an immutable class (#3687)
187f50a34 [wpimath] Catch incorrect parameters to state-space models earlier (#3680)
8d04606c4 Replace instances of frc-characterization with SysId (NFC) (#3681)
b82d4f6e5 [hal, cscore, ntcore] Use WPI common handle type base
87e34967e [wpiutil] Add synchronization primitives
e32499c54 [wpiutil] Add ParallelTcpConnector (#3655)
aa0b49228 [wpilib] Remove redundant "quick turn" docs for curvature drive (NFC) (#3674)
57301a7f9 [hal] REVPH: Start closed-loop compressor control on init (#3673)
d1842ea8f [wpilib] Improve interrupt docs (NFC) (#3679)
558151061 [wpiutil] Add DsClient (#3654)
181723e57 Replace `.to<double>()` and `.template to<double>()` with `.value()` (#3667)
6bc1db44b [commands] Add pointer overload of AddRequirements (artf6003) (#3669)
737b57ed5 [wpimath] Update to drake v0.35.0 (#3665)
4d287d1ae [build] Upgrade WPIJREArtifact to JRE 2022-11.0.12u5 (#3666)
f26eb5ada [hal] Fix another typo (get -> gets) (NFC) (#3663)
94ed275ba [hal] Fix misspelling (numer -> number) (NFC) (#3662)
ac2f44da3 [wpiutil] uv: use move for std::function (#3653)
75fa1fbfb [wpiutil] json::serializer: Optimize construction (#3647)
5e689faea [wpiutil] Import MessagePack implementation (mpack) (#3650)
649a50b40 [wpiutil] Add LEB128 byte-by-byte reader (#3652)
e94397a97 [wpiutil] Move json_serializer.h to public headers (#3646)
4ec58724d [wpiutil] uv::Tcp: Clarify SetNoDelay documentation (#3649)
8cb294aa4 [wpiutil] WebSocket: Make Shutdown() public (#3651)
2b3a9a52b [wpiutil] json: Fix map iterator key() for std::string_view (#3645)
138cbb94b [wpiutil] uv::Async: Add direct call for no-parameter specialization (#3648)
e56d6dea8 [ci] Update testbench pool image to ubuntu-latest (#3643)
43f30e44e [build] Enable comments in doxygen source files (#3644)
9e6db17ef [build] Enable doxygen preprocessor expansion of WPI_DEPRECATED (#3642)
0e631ad2f Add WPILib version to issue template (#3641)
6229d8d2f [build] Docs: set case_sense_names to false (#3392)
4647d09b5 [docs] Fix Doxygen warnings, add CI docs lint job (#3639)
4ad3a5402 [hal] Fix PWM allocation channel (#3637)
05e5feac4 [docs] Fix brownout docs (NFC) (#3638)
67df469c5 [examples] Remove old command-based templates and examples (#3263)
689e9ccfb [hal, wpilib] Add brownout voltage configuration (#3632)
9cd4bc407 [docs] Add useLocal to avoid using installer artifacts (#3634)
61996c2bb [cscore] Fix Java direct callback notifications (#3631)
6d3dd99eb [build] Update to newest native-utils (#3633)
f0b484892 [wpiutil] Fix StringMap iterator equality check (#3629)
8352cbb7a Update development build instructions for 2022 (#3616)
6da08b71d [examples] Fix Intermediate Vision Java Example description (#3628)
5d99059bf [wpiutil] Remove optional.h (#3627)
fa41b106a [glass, wpiutil] Add missing format args (#3626)
4e3fd7d42 [build] Enable Zc:__cplusplus for Windows (#3625)
791d8354d [build] Suppress deprecation/removal warnings for old commands (#3618)
10f19e6fc [hal, wpilib] Add REV PneumaticsHub (#3600)
4c61a1305 [ntcore] Revert to per-element copy for toNative() (#3621)
7b3f62244 [wpiutil] SendableRegistry: Print exception stacktrace (#3620)
d347928e4 [hal] Use better error for when console out is enabled while attempting to use onboard serial port (#3622)
cc31079a1 [hal] Use setcap instead of setuid for setting thread priorities (#3613)
4676648b7 [wpimath] Upgrade to Drake v0.34.0 (#3607)
c7594c911 [build] Allow building wpilibc in cmake without cscore and opencv (#3605)
173cb7359 [wpilib] Add TimesliceRobot (#3502)
af295879f [hal] Set error status for I2C port out of range (#3603)
95dd20a15 [build] Enable spotbugs (#3601)
b65fce86b [wpilib] Remove Timer lock in wpilibj and update docs (#3602)
3b8d3bbcb Remove unused and add missing deprecated.h includes (#3599)
f9e976467 [examples] Rename DriveTrain classes to Drivetrain (#3594)
118a27be2 [wpilib] Add Timer tests (#3595)
59c89428e [wpilib] Deprecate Timer::HasPeriodPassed() (#3598)
202ca5e78 Force C++17 in .clang-format (#3597)
d6f185d8e Rename tests for consistency (#3592)
54ca474db [ci] Enable asan and tsan in CI for tests that pass (#3591)
1ca383b23 Add Debouncer (#3590)
179fde3a7 [build] Update to 2022 native utils and gradle 7 (#3588)
50198ffcf [examples] Add Mechanism2d visualization to Elevator Sim (#3587)
a446c2559 [examples] Synchronize C++ and Java Mechanism2d examples (#3589)
a7fb83103 [ci] clang-tidy: Generate compilation commands DB with Gradle (#3585)
4f5e0c9f8 [examples] Update ArmSimulation example to use Mechanism2d (#3572)
8164b91dc [CI] Print CMake test output on failure (#3583)
4d5fca27e [wpilib] Impove Mechanism2D documentation (NFC) (#3584)
fe59e4b9f Make C++ test names more consistent (#3586)
5c8868549 [wpilibc] Fix C++ MechanisimRoot2D to use same NT entries as Java/Glass (#3582)
9359431ba [wpimath] Clean up Eigen usage
72716f51c [wpimath] Upgrade to Eigen 3.4
382deef75 [wpimath] Explicitly export wpimath symbols
161e21173 [ntcore] Match standard handle layout, only allow 16 instances (#3577)
263a24811 [wpimath] Use jinja for codegen (#3574)
725251d29 [wpilib] Increase tolerances of DifferentialDriveSimTest (#3581)
4dff87301 [wpimath] Make LinearFilter::Factorial() constexpr (#3579)
60ede67ab [hal, wpilib] Switch PCM to be a single object that is allowed to be duplicated (#3475)
906bfc846 [build] Add CMake build support for sanitizers (#3576)
0d4f08ad9 [hal] Simplify string copy of joystick name (#3575)
a52bf87b7 [wpiutil] Add Java function package (#3570)
40c7645d6 [wpiutil] UidVector: Return old object from erase() (#3571)
5b886a23f [wpiutil] jni_util: Add size, operator[] to JArrayRef (#3569)
65797caa7 [sim] Fix halsim_ds_socket stringop overflow warning from GCC 10 (#3568)
66abb3988 [hal] Update runtime enum to allow selecting roborio 2 (#3565)
95a12e0ee [hal] UidSetter: Don't revert euid if its already current value (#3566)
27951442b [wpimath] Use external Eigen headers only (#3564)
c42e053ae [docs] Update to doxygen 1.9.2 (#3562)
e7048c8c8 [docs] Disable doxygen linking for common words that are also classes (#3563)
d8e0b6c97 [wpilibj] Fix java async interrupts (#3559)
5e6c34c61 Update to 2022 roborio image (#3537)
828f073eb [wpiutil] Fix uv::Buffer memory leaks caught by asan (#3555)
2dd5701ac [cscore] Fix mutex use-after-free in cscore test (#3557)
531439198 [ntcore] Fix NetworkTables memory leaks caught by asan (#3556)
3d9a4d585 [wpilibc] Fix AnalogTriggerOutput memory leak reported by asan (#3554)
54eda5928 [wpiutil] Ignore ubsan vptr upcast warning in SendableHelper moves (#3553)
5a4f75c9f [wpilib] Replace Speed controller comments with motor controller (NFC) (#3551)
7810f665f [wpiutil] Fix bug in uleb128 (#3540)
697e2dd33 [wpilib] Fix errant jaguar reference in comments (NFC) (#3550)
936c64ff5 [docs] Enable -linksource for javadocs (#3549)
1ea654954 [build] Upgrade CMake build to googletest 1.11.0 (#3548)
32d9949e4 [wpimath] Move controller tests to wpimath (#3541)
01ba56a8a [hal] Replace strncpy with memcpy (#3539)
e109c4251 [build] Rename makeSim flag to forceGazebo to better describe what it does (#3535)
e4c709164 [docs] Use a doxygen theme and add logo (#3533)
960b6e589 [wpimath] Fix Javadoc warning (#3532)
82eef8d5e [hal] Remove over current fault HAL functions from REV PDH (#3526)
aa3848b2c [wpimath] Move RobotDriveBase::ApplyDeadband() to MathUtil (#3529)
3b5d0d141 [wpimath] Add LinearFilter::BackwardFiniteDifference() (#3528)
c8fc715fe [wpimath] Upgrade drake files to v0.33.0 (#3531)
e5fe3a8e1 [build] Treat javadoc warnings as errors in CI and fix warnings (#3530)
e0c6cd3dc [wpimath] Add an operator for composing two Transform2ds (#3527)
2edd510ab [sim] Add sim wrappers for sensors that use SimDevice (#3517)
2b3e2ebc1 [hal] Fix HAL Notifier thread priority setting (#3522)
ab4cb5932 [gitignore] Update gitignore to ignore bazel / clion files (#3524)
57c8615af [build] Generate spotless patch on failure (#3523)
b90317321 Replace std::cout and std::cerr with fmt::print() (#3519)
10cc8b89c [hal] [wpilib] Add initial support for the REV PDH (#3503)
5d9ae3cdb [hal] Set HAL Notifier thread as RT by default (#3482)
192d251ee [wpilibcIntegrationTests] Properly disable DMA integration tests (#3514)
031962608 [wpilib] Add PS4Controller, remove Hand from GenericHID/XboxController (#3345)
25f6f478a [wpilib] Rename DriverStation::IsOperatorControl() to IsTeleop() (#3505)
e80f09f84 [wpilibj] Add unit tests (#3501)
c159f91f0 [wpilib] Only read DS control word once in IterativeRobotBase (#3504)
eb790a74d Add rio development docs documenting myRobot deploy tasks (#3508)
e47451f5a [wpimath] Replace auto with Eigen types (#3511)
252b8c83b Remove Java formatting from build task in CI (#3507)
09666ff29 Shorten Gazebo CI build (#3506)
baf2e501d Update myRobot to use 2021 java (#3509)
5ac60f0a2 [wpilib] Remove IterativeRobotBase mode init prints (#3500)
fb2ee8ec3 [wpilib] Add TimedRobot functions for running code on mode exit (#3499)
94e0db796 [wpilibc] Add more unit tests (#3494)
b25324695 [wpilibj] Add units to parameter names (NFC) (#3497)
1ac73a247 [hal] Rename PowerDistributionPanel to PowerDistribution (#3466)
2014115bc [examples] frisbeebot: Fix typo and reflow comments (NFC) (#3498)
4a944dc39 [examples] Consistently use 0 for controller port (#3496)
3838cc4ec Use unicode characters in docs equations (#3487)
85748f2e6 [examples] Add C++ TankDrive example (#3493)
d7b8aa56d [wpilibj] Rename DriverStation In[Mode] functions to follow style guide (#3488)
16e096cf8 [build] Fix CMake Windows CI (#3490)
50af74c38 [wpimath] Clean up NumericalIntegration and add Discretization tests (#3489)
bfc209b12 Automate fmt update (#3486)
e7f9331e4 [build] Update to Doxygen 1.9.1 (#3008)
ab8e8aa2a [wpimath] Update drake with upstream (#3484)
1ef826d1d [wpimath] Fix IOException path in WPIMath JNI (#3485)
52bddaa97 [wpimath] Disable iostream support for units and enable fmtlib (#3481)
e4dc3908b [wpiutil] Upgrade to fmtlib 8.0.1 (#3483)
1daadb812 [wpimath] Implement Dormand-Prince integration method (#3476)
9c2723391 [cscore] Add [[nodiscard]] to GrabFrame functions (#3479)
7a8796414 [wpilib] Add Notifier integration tests (#3480)
f8f13c536 [wpilibcExamples] Prefix decimal numbers with 0 (#3478)
1adb69c0f [ntcore] Use "NetworkTables" instead of "Network Tables" in NT specs (#3477)
5f5830b96 Upload wpiformat diff if one exists (#3474)
9fb4f35bb [wpimath] Add tests for DARE overload with Q, R, and N matrices (#3472)
c002e6f92 Run wpiformat (#3473)
c154e5262 [wpilib] Make solenoids exclusive use, PCM act like old sendable compressor (#3464)
6ddef1cca [hal] JNI setDIO: use a boolean and not a short (#3469)
9d68d9582 Remove extra newlines after open curly braces (NFC) (#3471)
a4233e1a1 [wpimath] Add script for updating Drake (#3470)
39373c6d2 Update README.md for new GCC version requirement (#3467)
d29acc90a [wpigui] Add option to reset UI on exit (#3463)
a371235b0 [ntcore] Fix dangling pointer in logger (#3465)
53b4891a5 [wpilibcintegrationtests] Fix deprecated Preferences usage (#3461)
646ded912 [wpimath] Remove incorrect discretization in pose estimators (#3460)
ea0b8f48e Fix some deprecation warnings due to fmtlib upgrade (#3459)
2067d7e30 [wpilibjexamples] Add wpimathjni, wpiutiljni to library path (#3455)
866571ab4 [wpiutil] Upgrade to fmtlib 8.0.0 (#3457)
4e1fa0308 [build] Skip PDB copy on windows build servers (#3458)
b45572167 [build] Change CI back to 18.04 docker images (#3456)
57a160f1b [wpilibc] Fix LiveWindow deprecation warning in RobotBase skeleton template (#3454)
29ae8640d [HLT] Implement duty cycle cross connect tests (#3453)
ee6377e54 [HLT] Add relay and analog cross connects (#3452)
b0f1ae7ea [build] CMake: Build the HAL even if WITH_CSCORE=OFF (#3449)
7aae2b72d Replace std::to_string() with fmt::format() (#3451)
73fcbbd74 [HLT] Add relay digital cross connect tests (#3450)
e7bedde83 [HLT] Add PWM tests that use DMA as the back end (#3447)
7253edb1e [wpilibc] Timer: Fix deprecated warning (#3446)
efa28125c [wpilibc] Add message to RobotBase on how to read stacktrace (#3444)
9832fcfe1 [hal] Fix DIO direction getter (#3445)
49c71f9f2 [wpilibj] Clarify robot quit message (#3364)
791770cf6 [wpimath] Move controller from wpilibj to wpimath (#3439)
9ce9188ff [wpimath] Add ReportWarning to MathShared (#3441)
362066a9b [wpilib] Deprecate getInstance() in favor of static functions (#3440)
26ff9371d Initial commit of cross connect integration test project (#3434)
4a36f86c8 [hal] Add support for DMA to Java (#3158)
85144e47f [commands] Unbreak build (#3438)
b417d961e Split Sendable into NT and non-NT portions (#3432)
ef4ea84cb [commands] Change grouping decorator impl to flatten nested group structures (#3335)
b422665a3 [examples] Invert right side of drive subsystems (#3437)
186dadf14 [hal] Error if attempting to set DIO output on an input port (#3436)
04e64db94 Remove redundant C++ lambda parentheses (NFC) (#3433)
f60994ad2 [wpiutil] Rename Java package to edu.wpi.first.util (#3431)
cfa1ca96f [wpilibc] Make ShuffleboardValue non-copyable (#3430)
4d9ff7643 Fix documentation warnings generated by JavaDoc (NFC) (#3428)
9e1b7e046 [build] Fix clang-tidy and clang-format (#3429)
a77c6ff3a [build] Upgrade clang-format and clang-tidy (NFC) (#3422)
099fde97d [wpilib] Improve PDP comments (NFC) (#3427)
f8fc2463e [wpilibc, wpiutil] Clean up includes (NFC) (#3426)
e246b7884 [wpimath] Clean up member initialization in feedforward classes (#3425)
c1e128bd5 Disable frivolous PMD warnings and enable PMD in ntcore (#3419)
8284075ee Run "Lint and Format" CI job on push as well as pull request (#3412)
f7db09a12 [wpimath] Move C++ filters into filter folder to match Java (#3417)
f9c3d54bd [wpimath] Reset error covariance in pose estimator ResetPosition() (#3418)
0773f4033 [hal] Ensure HAL status variables are initialized to zero (#3421)
d068fb321 [build] Upgrade CI to use 20.04 docker images (#3420)
8d054c940 [wpiutil] Remove STLExtras.h
80f1d7921 [wpiutil] Split function_ref to a separate header
64f541325 Use wpi::span instead of wpi::ArrayRef across all libraries (#3414)
2abbbd9e7 [build] clang-tidy: Remove bugprone-exception-escape (#3415)
a5c471af7 [wpimath] Add LQR template specialization for 2x2 system
edd2f0232 [wpimath] Add DARE solver for Q, R, and N with LQR ctor overloads
b2c3b2dd8 Use std::string_view and fmtlib across all libraries (#3402)
4f1cecb8e [wpiutil] Remove Path.h (#3413)
b336eac34 [build] Publish halsim_ws_core to Maven
2a09f6fa4 [build] Also build sim modules as static libraries
0e702eb79 [hal] Add a unified PCM object (#3331)
dea841103 [wpimath] Add fmtlib formatter overloads for Eigen::Matrix and units (#3409)
82856cf81 [wpiutil] Improve wpi::circular_buffer iterators (#3410)
8aecda03e [wpilib] Fix a documentation typo (#3408)
5c817082a [wpilib] Remove InterruptableSensorBase and replace with interrupt classes (#2410)
15c521a7f [wpimath] Fix drivetrain system identification (#3406)
989de4a1b [build] Force all linker warnings to be fatal for rio builds (#3407)
d9eeb45b0 [wpilibc] Add units to Ultrasonic class API (#3403)
fe570e000 [wpiutil] Replace llvm filesystem with C++17 filesystem (#3401)
01dc0249d [wpimath] Move SlewRateLimiter from wpilib to wpimath (#3399)
93523d572 [wpilibc] Clean up integration tests (#3400)
4f7a4464d [wpiutil] Rewrite StringExtras for std::string_view (#3394)
e09293a15 [wpilibc] Transition C++ classes to units::second_t (#3396)
827b17a52 [build] Create run tasks for Glass and OutlineViewer (#3397)
a61037996 [wpiutil] Avoid MSVC warning on span include (#3393)
4e2c3051b [wpilibc] Use std::string_view instead of Twine (#3380)
50915cb7e [wpilibc] MotorSafety::GetDescription(): Return std::string (#3390)
f4e2d26d5 [wpilibc] Move NullDeleter from frc/Base.h to wpi/NullDeleter.h (#3387)
cb0051ae6 [wpilibc] SimDeviceSim: use fmtlib (#3389)
a238cec12 [wpiutil] Deprecate wpi::math constants in favor of wpi::numbers (#3383)
393bf23c0 [ntcore, cscore, wpiutil] Standardize template impl files on .inc extension (NFC) (#3124)
e7d9ba135 [sim] Disable flaky web server integration tests (#3388)
0a0003c11 [wpilibjExamples] Fix name of Java swerve drive pose estimator example (#3382)
7e1b27554 [wpilibc] Use default copies and moves when possible (#3381)
fb2a56e2d [wpilibc] Remove START_ROBOT_CLASS macro (#3384)
84218bfb4 [wpilibc] Remove frc namespace shim (#3385)
dd7824340 [wpilibc] Remove C++ compiler version static asserts (#3386)
484cf9c0e [wpimath] Suppress the -Wmaybe-uninitialized warning in Eigen (#3378)
a04d1b4f9 [wpilibc] DriverStation: Remove ReportError and ReportWarning
831c10bdf [wpilibc] Errors: Use fmtlib
87603e400 [wpiutil] Import fmtlib (#3375)
442621672 [wpiutil] Add ArrayRef/std::span/wpi::span implicit conversions
bc15b953b [wpiutil] Add std::span implementation
6d20b1204 [wpiutil] StringRef, Twine, raw_ostream: Add std::string_view support (#3373)
2385c2a43 [wpilibc] Remove Utility.h (#3376)
87384ea68 [wpilib] Fix PIDController continuous range error calculations (#3170)
04dae799a [wpimath] Add SimpleMotorFeedforward::Calculate(velocity, nextVelocity) overload (#3183)
0768c3903 [wpilib] DifferentialDrive: Remove right side inversion (#3340)
8dd8d4d2d [wpimath] Fix redundant nested math package introduced by #3316 (#3368)
49b06beed [examples] Add Field2d to RamseteController example (#3371)
4c562a445 [wpimath] Fix typo in comment of update_eigen.py (#3369)
fdbbf1188 [wpimath] Add script for updating Eigen
f1e64b349 [wpimath] Move Eigen unsupported folder into eigeninclude
224f3a05c [sim] Fix build error when building with GCC 11.1 (#3361)
ff56d6861 [wpilibj] Fix SpeedController deprecated warnings (#3360)
1873fbefb [examples] Fix Swerve and Mecanum examples (#3359)
80b479e50 [examples] Fix SwerveBot example to use unique encoder ports (#3358)
1f7c9adee [wpilibjExamples] Fix pose estimator examples (#3356)
9ebc3b058 [outlineviewer] Change default size to 600x400 (#3353)
e21b443a4 [build] Gradle: Make C++ examples runnable (#3348)
da590120c [wpilibj] Add MotorController.setVoltage default (#3347)
561d53885 [build] Update opencv to 4.5.2, imgui/implot to latest (#3344)
44ad67ca8 [wpilibj] Preferences: Add missing Deprecated annotation (#3343)
3fe8fc75a [wpilibc] Revert "Return reference from GetInstance" (#3342)
3cc2da332 Merge branch '2022'
a3cd90dd7 [wpimath] Fix classpath used by generate_numbers.py (#3339)
d6cfdd3ba [wpilib] Preferences: Deprecate Put* in favor of Set* (#3337)
ba08baabb [wpimath] Update Drake DARE solver to v0.29.0 (#3336)
497b712f6 [wpilib] Make IterativeRobotBase::m_period private with getter
f00dfed7a [wpilib] Remove IterativeRobot base class
3c0846168 [hal] Use last error reporting instead of PARAMETER_OUT_OF_RANGE (#3328)
5ef2b4fdc [wpilibj] Fix @deprecated warning for SerialPort constructor (#3329)
23d2326d1 [hal] Report previous allocation location for indexed resource duplicates (#3322)
e338f9f19 [build] Fix wpilibc runCpp task (#3327)
c8ff626fe [wpimath] Move Java classes to edu.wpi.first.math (#3316)
4e424d51f [wpilibj] DifferentialDrivetrainSim: Rename constants to match the style guide (#3312)
6b50323b0 [cscore] Use Lock2DSize if possible for Windows USB cameras (#3326)
65c148536 [wpilibc] Fix "control reaches end of non-void function" warning (#3324)
f99f62bee [wpiutil] uv Handle: Use malloc/free instead of new/delete (#3325)
365f5449c [wpimath] Fix MecanumDriveKinematics (#3266)
ff52f207c [glass, wpilib] Rewrite Mechanism2d (#3281)
ee0eed143 [wpimath] Add DCMotor factory function for Romi motors (#3319)
512738072 [hal] Add HAL_GetLastError to enable better error messages from HAL calls (#3320)
ced654880 [glass, outlineviewer] Update Mac icons to macOS 11 style (#3313)
936d3b9f8 [templates] Add Java template for educational robot (#3309)
6e31230ad [examples] Fix odometry update in SwerveControllerCommand example (#3310)
05ebe9318 Merge branch 'main' into 2022
aaf24e255 [wpilib] Fix initial heading behavior in HolonomicDriveController (#3290)
8d961dfd2 [wpilibc] Remove ErrorBase (#3306)
659b37ef9 [wpiutil] StackTrace: Include offset on Linux (#3305)
0abf6c904 [wpilib] Move motor controllers to motorcontrol package (#3302)
4630191fa [wpiutil] circular_buffer: Use value initialization instead of passing zero (#3303)
b7b178f49 [wpilib] Remove Potentiometer interface
687066af3 [wpilib] Remove GyroBase
6b168ab0c [wpilib] Remove PIDController, PIDOutput, PIDSource
948625de9 [wpimath] Document conversion from filter cutoff frequency to time constant (#3299)
3848eb8b1 [wpilibc] Fix flywhel -> flywheel typo in FlywheelSim (#3298)
3abe0b9d4 [cscore] Move java package to edu.wpi.first.cscore (#3294)
d7fabe81f [wpilib] Remove RobotDrive (#3295)
1dc81669c [wpilib] Remove GearTooth (#3293)
01d0e1260 [wpilib] Revert move of RomiGyro into main wpilibc/j (#3296)
397e569aa [ntcore] Remove "using wpi" from nt namespace
79267f9e6 [ntcore] Remove NetworkTable -> nt::NetworkTable shim
48ebe5736 [ntcore] Remove deprecated Java interfaces and classes
c2064c78b [ntcore] Remove deprecated ITable interfaces
36608a283 [ntcore] Remove deprecated C++ APIs
a1c87e1e1 [glass] LogView: Add "copy to clipboard" button (#3274)
fa7240a50 [wpimath] Fix typo in quintic spline basis matrix
ffb4d38e2 [wpimath] Add derivation for spline basis matrices
f57c188f2 [wpilib] Add AnalogEncoder(int) ctor (#3273)
8471c4fb2 [wpilib] FieldObject2d: Add setTrajectory() method (#3277)
c97acd18e [glass] Field2d enhancements (#3234)
ffb590bfc [wpilib] Fix Compressor sendable properties (#3269)
6137f98eb [hal] Rename SimValueCallback2 to SimValueCallback (#3212)
a6f653969 [hal] Move registerSimPeriodic functions to HAL package (#3211)
10c038d9b [glass] Plot: Fix window creation after removal (#3264)
2d2eaa3ef [wpigui] Ensure window will be initially visible (#3256)
4d28b1f0c [wpimath] Use JNI for trajectory serialization (#3257)
3de800a60 [wpimath] TrajectoryUtil.h: Comment formatting (NFC) (#3262)
eff592377 [glass] Plot: Don't overwrite series ID (#3260)
a79faace1 [wpilibc] Return reference from GetInstance (#3247)
9550777b9 [wpilib] PWMSpeedController: Use PWM by composition (#3248)
c8521a3c3 [glass] Plot: Set reasonable default window size (#3261)
d71eb2cf3 [glass] Plot: Show full source name as tooltip and in popup (#3255)
160fb740f [hal] Use std::lround() instead of adding 0.5 and truncating (#3012)
48e9f3951 [wpilibj] Remove wpilibj package CameraServer (#3213)
8afa596fd [wpilib] Remove deprecated Sendable functions and SendableBase (#3210)
d3e45c297 [wpimath] Make C++ geometry classes immutable (#3249)
2c98939c1 [glass] StringChooser: Don't call SameLine() at end
a18a7409f [glass] NTStringChooser: Clear value of deleted entries
2f19cf452 [glass] NetworkTablesHelper: listen to delete events
da96707dc Merge branch 'main' into 2022
c3a8bdc24 [build] Fix clang-tidy action (#3246)
21624ef27 Add ImGui OutlineViewer (#3220)
1032c9b91 [wpiutil] Unbreak wpi::Format on Windows (#3242)
2e07902d7 [glass] NTField2D: Fix name lookup (#3233)
6e23e1840 [wpilibc] Remove WPILib.h (#3235)
3e22e4506 [wpilib] Make KoP drivetrain simulation weight 60 lbs (#3228)
79d1bd6c8 [glass] NetworkTablesSetting: Allow disable of server option (#3227)
fe341a16f [examples] Use more logical elevator setpoints in GearsBot (#3198)
62abf46b3 [glass] NetworkTablesSettings: Don't block GUI (#3226)
a95a5e0d9 [glass] Move NetworkTablesSettings to libglassnt (#3224)
d6f6ceaba [build] Run Spotless formatter (NFC) (#3221)
0922f8af5 [commands] CommandScheduler.requiring(): Note return can be null (NFC) (#2934)
6812302ff [examples] Make DriveDistanceOffboard example work in sim (#3199)
f3f86b8e7 [wpimath] Add pose estimator overload for vision + std dev measurement (#3200)
1a2680b9e [wpilibj] Change CommandBase.withName() to return CommandBase (#3209)
435bbb6a8 [command] RamseteCommand: Output 0 if interrupted (#3216)
3cf44e0a5 [hal] Add function for changing HAL Notifier thread priority (#3218)
40b367513 [wpimath] Units.java: Add kg-lb conversions (#3203)
9f563d584 [glass] NT: Fix return value in StringToDoubleArray (#3208)
af4adf537 [glass] Auto-size plots to fit window (#3193)
2560146da [sim] GUI: Add option to show prefix in Other Devices (#3186)
eae3a6397 gitignore: Ignore .cache directory (#3196)
959611420 [wpilib] Require non-zero positive value for PIDController.period (#3175)
9522f2e8c [wpimath] Add methods to concatenate trajectories (#3139)
e42a0b6cf [wpimath] Rotation2d comment formatting (NFC) (#3162)
d1c7032de [wpimath] Fix order of setting gyro offset in pose estimators (#3176)
d241bc81a [sim] Add DoubleSolenoidSim and SolenoidSim classes (#3177)
cb7f39afa [wpilibc] Add RobotController::GetBatteryVoltage() to C++ (#3179)
99b5ad9eb [wpilibj] Fix warnings that are not unused variables or deprecation (#3161)
c14b23775 [build] Fixup doxygen generated include dirs to match what users would need (#3154)
d447c7dc3 [sim] Add SimDeviceSim ctor overloads (#3134)
247420c9c [build] Remove jcenter repo (#3157)
04b112e00 [build] Include debug info in plugin published artifacts (#3149)
be0ce9900 [examples] Use PWMSparkMax instead of PWMVictorSPX (#3156)
69e8d0b65 [wpilib] Move RomiGyro into main wpilibc/j (#3143)
94e685e1b [wpimath] Add custom residual support to EKF (#3148)
5899f3dd2 [sim] GUI: Make keyboard settings loading more robust (#3167)
f82aa1d56 [wpilib] Fix HolonomicDriveController atReference() behavior (#3163)
fe5c2cf4b [wpimath] Remove ControllerUtil.java (#3169)
43d40c6e9 [wpiutil] Suppress unchecked cast in CombinedRuntimeLoader (#3155)
3d44d8f79 [wpimath] Fix argument order in UKF docs (NFC) (#3147)
ba6fe8ff2 [cscore] Add USB camera change event (#3123)
533725888 [build] Tweak OpenCV cmake search paths to work better on Linux (#3144)
29bf9d6ef [cscore] Add polled support to listener
483beb636 [ntcore] Move CallbackManager to wpiutil
fdaec7759 [examples] Instantiate m_ramseteController in example (#3142)
8494a5761 Rename default branch to main (#3140)
45590eea2 [wpigui] Hardcode window scale to 1 on macOS (#3135)
834a64920 [build] Publish libglass and libglassnt to Maven (#3127)
2c2ccb361 [wpimath] Fix Rotation2d equality operator (#3128)
fb5c8c39a [wpigui] clang-tidy: readability-braces-around-statements
f7d39193a [wpigui] Fix copyright in pfd and wpigui_metal.mm
aec796b21 [ntcore] Fix conditional jump on uninitialized value (#3125)
fb13bb239 [sim] GUI: Add right click popup for keyboard joystick settings (#3119)
c517ec677 [build] Update thirdparty-imgui to 1.79-2 (#3118)
e8cbf2a71 [wpimath] Fix typo in SwerveDrivePoseEstimator doc (NFC) (#3112)
e9c86df46 [wpimath] Add tests for swerve module optimization (#3100)
6ba8c289c [examples] Remove negative of ArcadeDrive(fwd, ..) in the C++ Getting Started Example (#3102)
3f1672e89 [hal] Add SimDevice createInt() and createLong() (#3110)
15be5cbf1 [examples] Fix segfault in GearsBot C++ example (#3111)
4cf0e5e6d Add quick links to API documentation in README (#3082)
6b1898f12 Fix RT priority docs (NFC) (#3098)
b3426e9c0 [wpimath] Fix missing whitespace in pose estimator doc (#3097)
38c1a1f3e [examples] Fix feildRelative -> fieldRelative typo in XControllerCommand examples (#3104)
4488e25f1 [glass] Shorten SmartDashboard window names (#3096)
cfdb3058e [wpilibj] Update SimDeviceSimTest (#3095)
64adff5fe [examples] Fix typo in ArcadeDrive constructor parameter name (#3092)
6efc58e3d [build] Fix issues with build on windows, deprecations, and native utils (#3090)
f393989a5 [wpimath, wpiutil] Add wpi::array for compile time size checking (#3087)
d6ed20c1e [build] Set macOS deployment target to 10.14 (#3088)
7c524014c [hal] Add [[nodiscard]] to HAL_WaitForNotifierAlarm() (#3085)
406d055f0 [wpilib] Fixup wouldHitLowerLimit in elevator and arm simulation classes. (#3076)
04a90b5dd [examples] Don't continually set setpoint in PotentiometerPID Examples (#3084)
8c5bfa013 [sim] GUI: Add max value setting for keyboard joysticks (#3083)
bc80c5535 [hal] Add SimValue reset() function (#3064)
9c3b51ca0 [wpilib] Document simulation APIs (#3079)
26584ff14 [wpimath] Add model description to LinearSystemId Javadocs (#3080)
42c3d5286 [examples] Sync Java and C++ trajectories in sim example (#3081)
64e72f710 [wpilibc] Add missing function RoboRioSim::ResetData (#3073)
e95503798 [wpimath] Add optimize() to SwerveModuleState (#3065)
fb99910c2 [hal] Add SimInt and SimLong wrappers for int/long SimValue (#3066)
e620bd4d3 [doc] Add machine-readable websocket specification (#3059)
a44e761d9 [glass] Add support for plot Y axis labels
ea1974d57 [wpigui] Update imgui and implot to latest
85a0bd43c [wpimath] Add RKF45 integration (#3047)
278e0f126 [glass] Use .controllable to set widgets' read-only state (#3035)
d8652cfd4 [wpimath] Make Java DCMotor API consistent with C++ and fix motor calcs (#3046)
377b7065a [build] Add toggleOffOn to Java spotless (#3053)
1e9c79c58 [sim] Use plant output to retrieve simulated position (#3043)
78147aa34 [sim] GUI: Fix Keyboard Joystick (#3052)
cd4a2265b [ntcore] Fix NetworkTableEntry::GetRaw() (#3051)
767ac1de1 [build] Use deploy key for doc publish (#3048)
d762215d1 [build] Add publish documentation script (#3040)
1fd09593c [examples] Add missing TestInit method to GettingStarted Example (#3039)
e45a0f6ce [examples] Add RomiGyro to the Romi Reference example (#3037)
94f852572 Update imaging link and fix typo (#3038)
d73cf64e5 [examples] Update RomiReference to match motor directions (#3036)
f945462ba Bump copyright year to 2021 (#3033)
b05946175 [wpimath] Catch Drake JNI exceptions and rethrow them (#3032)
62f0f8190 [wpimath] Deduplicate angle modulus functions (#2998)
bf8c0da4b [glass] Add "About" popup with version number (#3031)
dfdd6b389 [build] Increase Gradle heap size in Gazebo build (#3028)
f5e0fc3e9 Finish clang-tidy cleanups (#3003)
d741101fe [sim] Revert accidental commit of WSProvider_PDP.h (#3027)
e1620799c [examples] Add C++ RomiReference example (#2969)
749c7adb1 [command] Fix use-after-free in CommandScheduler (#3024)
921a73391 [sim] Add WS providers for AddressableLED, PCM, and Solenoid (#3026)
26d0004fe [build] Split Actions into different yml files (#3025)
948af6d5b [wpilib] PWMSpeedController.get(): Apply Inversion (#3016)
670a187a3 [wpilibc] SuppliedValueWidget.h: Forward declare ShuffleboardContainer (#3021)
be9f72502 [ntcore] NetworkTableValue: Use std::forward instead of std::move (#3022)
daf3f4cb1 [cscore] cscore_raw_cv.h: Fix error in PutFrame() (#3019)
5acda4cc7 [wpimath] ElevatorFeedforward.h: Add time.h include
8452af606 [wpimath] units/radiation.h: Add mass.h include
630d44952 [hal] ErrorsInternal.h: Add stdint.h include
7372cf7d9 [cscore] Windows NetworkUtil.cpp: Add missing include
b7e46c558 Include .h from .inc/.inl files (NFC) (#3017)
bf8f8710e [examples] Update Romi template and example (#2996)
6ffe5b775 [glass] Ensure NetworkTableTree parent context menu has an id (#3015)
be0805b85 [build] Update to WPILibVersioningPlugin 4.1.0 (#3014)
65b2359b2 [build] Add spotless for other files (#3007)
8651aa73e [examples] Enable NT Flush in Field2d examples (#3013)
78b542737 [build] Add Gazebo build to Actions CI (#3004)
fccf86532 [sim] DriverStationGui: Fix two bugs (#3010)
185741760 [sim] WSProvider_Joystick: Fix off-by-1 in incoming buttons (#3011)
ee7114a58 [glass] Add drive class widgets (#2975)
00fa91d0d [glass] Use ImGui style for gyro widget colors (#3009)
b7a25bfc3 ThirdPartyNotices: Add portable file dialogs license (#3005)
a2e46b9a1 [glass] modernize-use-nullptr (NFC) (#3006)
a751fa22d [build] Apply spotless for java formatting (#1768)
e563a0b7d [wpimath] Make LinearSystemLoop move-constructible and move-assignable (#2967)
49085ca94 [glass] Add context menus to remove and add NetworkTables values (#2979)
560a850a2 [glass] Add NetworkTables Log window (#2997)
66782e231 [sim] Create Left/Right drivetrain current accessors (#3001)
b60eb1544 clang-tidy: bugprone-virtual-near-miss
cbe59fa3b clang-tidy: google-explicit-constructor
c97c6dc06 clang-tidy: google-readability-casting (NFC)
32fa97d68 clang-tidy: modernize-use-nullptr (NFC)
aee460326 clang-tidy: modernize-pass-by-value
29c7da5f1 clang-tidy: modernize-make-unique
6131f4e32 clang-tidy: modernize-concat-nested-namespaces (NFC)
67e03e625 clang-tidy: modernize-use-equals-default
b124f9101 clang-tidy: modernize-use-default-member-init
d11a3a638 clang-tidy: modernize-use-override (NFC)
4cc0706b0 clang-tidy: modernize-use-using (NFC)
885f5a978 [wpilibc] Speed up ScopedTracerTest (#2999)
60b596457 [wpilibj] Fix typos (NFC) (#3000)
6e1919414 [build] Bring naming checkstyle rules up to date with Google Style guide (#1781)
8c8ec5e63 [wpilibj] Suppress unchecked cast warnings (#2995)
b8413ddd5 [wpiutil] Add noexcept to timestamp static functions (#2994)
5d976b6e1 [glass] Load NetworkTableView settings on first draw (#2993)
2b4317452 Replace NOLINT(runtime/explicit) comments with NOLINT (NFC) (#2992)
1c3011ba4 [glass] Fix handling of "/" NetworkTables key (#2991)
574a42f3b [hal] Fix UnsafeManipulateDIO status check (#2987)
9005cd59e [wpilib] Clamp input voltage in sim classes (#2955)
dd494d4ab [glass] NetworkTablesModel::Update(): Avoid use-after-move (#2988)
7cca469a1 [wpimath] NormalizeAngle: Make inline, remove unnamed namespace (#2986)
2aed432b4 Add braces to C++ single-line loops and conditionals (NFC) (#2973)
0291a3ff5 [wpiutil] StringRef: Add noexcept to several constructors (#2984)
5d7315280 [wpimath] Update UnitsTest.cpp copyright (#2985)
254931b9a [wpimath] Remove LinearSystem from LinearSystemLoop (#2968)
aa89744c9 Update OtherVersions.md to include wpimath info (#2983)
1cda3f5ad [glass] Fix styleguide (#2976)
8f1f64ffb Remove year from file copyright message (NFC) (#2972)
2bc0a7795 [examples] Fix wpiformat warning about utility include (#2971)
4204da6ad [glass] Add application icon
7ac39b10f [wpigui] Add icon support
6b567e006 [wpimath] Add support for varying vision standard deviations in pose estimators (#2956)
df299d6ed [wpimath] Add UnscentedKalmanFilter::Correct() overload (#2966)
4e34f0523 [examples] Use ADXRS450_GyroSim class in simulation example (#2964)
9962f6fd7 [wpilib] Give Field2d a default Sendable name (#2953)
f9d492f4b [sim] GUI: Show "Other Devices" window by default (#2961)
a8bb2ef1c [sim] Fix ADXRS450_GyroSim and DutyCycleEncoderSim (#2963)
240c629cd [sim] Try to guess "Map Gamepad" setting (#2960)
952567dd3 [wpilibc] Add missing move constructors and assignment operators (#2959)
10b396b4c [sim] Various WebSockets fixes and enhancements (#2952)
699bbe21a [examples] Fix comments in Gearsbot to match implementation (NFC) (#2957)
27b67deca [glass] Add more widgets (#2947)
581b7ec55 [wpilib] Add option to flush NetworkTables every iterative loop
acfbb1a44 [ntcore] DispatcherBase::Flush: Use wpi::Now()
d85a6d8fe [ntcore] Reduce limit on flush and update rate to 5 ms
20fbb5c63 [sim] Fix stringop truncation warning from GCC 10 (#2945)
1051a06a7 [glass] Show NT timestamps in seconds (#2944)
98dfc2620 [glass] Fix plots (#2943)
1ba0a2ced [sim] GUI: Add keyboard virtual joystick support (#2940)
4afb13f98 [examples] Replace M_PI with wpi::math::pi (#2938)
b27d33675 [examples] Enhance Romi templates (#2931)
00b9ae77f [sim] Change default WS port number to 3300 (#2932)
65219f309 [examples] Update Field2d position in periodic() (#2928)
f78d1d434 [sim] Process WS Encoder reset internally (#2927)
941edca59 [hal] Add Java SimDeviceDataJNI.getSimDeviceName (#2924)
a699435ed [wpilibj] Fix FlywheelSim argument order in constructor (#2922)
66d641718 [examples] Add tasks to run Java examples (#2920)
558e37c41 [examples] Add simple differential drive simulation example (#2918)
4f40d991e [glass] Switch name of Glass back to glass (#2919)
549af9900 [build] Update native-utils to 2021.0.6 (#2914)
b33693009 [glass] Change basename of glass to Glass (#2915)
c9a0edfb8 [glass] Package macOS application bundle
2c5668af4 [wpigui] Add platform-specific preferences save
751dea32a [wpilibc] Try to work around ABI break introduced in #2901 (#2917)
cd8f4bfb1 [build] Package up msvc runtime into maven artifact (#2913)
a6cfcc686 [wpilibc] Move SendableChooser Doxygen comments to header (NFC) (#2911)
b8c4f603d [wpimath] Upgrade to Eigen 3.3.9 (#2910)
0075e4b39 [wpilibj] Fix NPE in Field2d (#2909)
125af556c [simulation] Fix halsim_gui ntcore and wpiutil deps (#2908)
963ad5c25 [wpilib] Add noise to Differential Drive simulator (#2903)
387f56cb7 [examples] Add Romi reference Java example and templates (#2905)
b3deda38c [examples] Zero motors on disabledInit() in sim physics examples (#2906)
2a5ca7745 [glass] Add glass: an application for display of robot data
727940d84 [wpilib] Move Field2d to SmartDashboard
8cd42478e [wpilib] SendableBuilder: Make GetTable() visible
c11d34b26 [command] Use addCommands in command group templates (#2900)
339d7445b [sim] Add HAL hooks for simulationPeriodic (#2881)
d16f05f2c [wpilib] Fix SmartDashboard update order (#2896)
5427b32a4 [wpiutil] unique_function: Restrict implicit conversion (#2899)
f73701239 [ntcore] Add missing SetDefault initializer_list functions (#2898)
f5a6fc070 [sim] Add initialized flag for all solenoids on a PCM (#2897)
bdf5ba91a [wpilibj] Fix typo in ElevatorSim (#2895)
bc8f33877 [wpilib] Add pose estimators (#2867)
3413bfc06 [wpilib] PIDController: Recompute the error in AtSetpoint() (#2822)
2056f0ce0 [wpilib] Fix bugs in Hatchbot examples (#2893)
5eb8cfd69 [wpilibc] Fix MatchDataSender (#2892)
e6a425448 [build] Delete test folders after tests execute (#2891)
d478ad00d [imgui] Allow usage of imgui_stdlib (#2889)
53eda861d [build] Add unit-testing infrastructure to examples (#2863)
cc1d86ba6 [sim] Add title to simulator GUI window (#2888)
f0528f00e [build] CMake: Use project-specific binary and source dirs (#2886)
5cd2ad124 [wpilibc] Add Color::operator!= (#2887)
6c00e7a90 [build] CI CMake: build with GUI enabled (#2884)
53170bbb5 Update roboRIO toolchain installation instructions (#2883)
467258e05 [sim] GUI: Add option to not zero disconnected joysticks (#2876)
129be23c9 Clarify JDK installation instructions in readme (#2882)
8e9290e86 [build] Add separate CMake setting for wpimath (#2885)
7cf5bebf8 [wpilibj] Cache NT writes from DriverStation (#2780)
f7f9087fb [command] Fix timing issue in RamseteCommand (#2871)
256e7904f [wpilibj] SimDeviceSim: Fix sim value changed callback (#2880)
c8ea1b6c3 [wpilib] Add function to adjust LQR controller gain for pure time delay (#2878)
2816b06c0 [sim] HAL_GetControlWord: Fully zero output (#2873)
4c695ea08 Add toolchain installation instructions to README (#2875)
a14d51806 [wpimath] DCMotor: fix doc typo (NFC) (#2868)
017097791 [build] CMake: build sim extensions as shared libs (#2866)
f61726b5a [build] Fix cmake-config files (#2865)
fc27fdac5 [wpilibc] Cache NT values from driver station (#2768)
47c59859e [sim] Make SimDevice callbacks synchronous (#2861)
6e76ab9c0 [build] Turn on WITH_GUI for Windows cmake CI
5f78b7670 [build] Set GLFW_INSTALL to OFF
5e0808c84 [wpigui] Fix Windows cmake build
508f05a47 [imgui] Fix typo in Windows CMake target sources
Change-Id: I1737b45965f31803a96676bedc7dc40e337aa321
git-subtree-dir: third_party/allwpilib
git-subtree-split: e473a00f9785f9949e5ced30901baeaf426d2fc9
Signed-off-by: Austin Schuh <austin.linux@gmail.com>
diff --git a/wpiutil/src/dev/java/edu/wpi/first/util/DevMain.java b/wpiutil/src/dev/java/edu/wpi/first/util/DevMain.java
new file mode 100644
index 0000000..3d5b134
--- /dev/null
+++ b/wpiutil/src/dev/java/edu/wpi/first/util/DevMain.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.util;
+
+public final class DevMain {
+ /** Main entry point. */
+ public static void main(String[] args) {
+ System.out.println("Hello World!");
+ System.out.println(RuntimeDetector.getPlatformPath());
+ }
+
+ private DevMain() {}
+}
diff --git a/wpiutil/src/dev/java/edu/wpi/first/wpiutil/DevMain.java b/wpiutil/src/dev/java/edu/wpi/first/wpiutil/DevMain.java
deleted file mode 100644
index c5f1754..0000000
--- a/wpiutil/src/dev/java/edu/wpi/first/wpiutil/DevMain.java
+++ /dev/null
@@ -1,21 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* 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. */
-/*----------------------------------------------------------------------------*/
-
-package edu.wpi.first.wpiutil;
-
-public final class DevMain {
- /**
- * Main entry point.
- */
- public static void main(String[] args) {
- System.out.println("Hello World!");
- System.out.println(RuntimeDetector.getPlatformPath());
- }
-
- private DevMain() {
- }
-}
diff --git a/wpiutil/src/dev/native/cpp/main.cpp b/wpiutil/src/dev/native/cpp/main.cpp
index 9ac3bab..14ddda2 100644
--- a/wpiutil/src/dev/native/cpp/main.cpp
+++ b/wpiutil/src/dev/native/cpp/main.cpp
@@ -1,17 +1,11 @@
-/*----------------------------------------------------------------------------*/
-/* 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. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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 <iostream>
-
-#include "wpi/SmallVector.h"
-#include "wpi/StringRef.h"
-#include "wpi/hostname.h"
+#include "fmt/core.h"
+#include "wpi/SmallString.h"
int main() {
- wpi::StringRef v1("Hello");
- std::cout << v1.lower() << std::endl;
+ wpi::SmallString<128> v1("Hello");
+ fmt::print("{}\n", v1.str());
}
diff --git a/wpiutil/src/main/java/edu/wpi/first/wpiutil/CircularBuffer.java b/wpiutil/src/main/java/edu/wpi/first/util/CircularBuffer.java
similarity index 84%
rename from wpiutil/src/main/java/edu/wpi/first/wpiutil/CircularBuffer.java
rename to wpiutil/src/main/java/edu/wpi/first/util/CircularBuffer.java
index 5325b26..f9129c7 100644
--- a/wpiutil/src/main/java/edu/wpi/first/wpiutil/CircularBuffer.java
+++ b/wpiutil/src/main/java/edu/wpi/first/util/CircularBuffer.java
@@ -1,15 +1,10 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2015-2019 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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.wpiutil;
+package edu.wpi.first.util;
-/**
- * This is a simple circular buffer so we don't need to "bucket brigade" copy old values.
- */
+/** This is a simple circular buffer so we don't need to "bucket brigade" copy old values. */
public class CircularBuffer {
private double[] m_data;
@@ -66,6 +61,8 @@
/**
* Push new value onto front of the buffer. The value at the back is overwritten if the buffer is
* full.
+ *
+ * @param value The value to push.
*/
public void addFirst(double value) {
if (m_data.length == 0) {
@@ -84,6 +81,8 @@
/**
* Push new value onto back of the buffer. The value at the front is overwritten if the buffer is
* full.
+ *
+ * @param value The value to push.
*/
public void addLast(double value) {
if (m_data.length == 0) {
@@ -117,9 +116,10 @@
return temp;
}
-
/**
* Pop value at back of buffer.
+ *
+ * @return value at back of buffer
*/
public double removeLast() {
// If there are no elements in the buffer, do nothing
@@ -135,6 +135,8 @@
* Resizes internal buffer to given size.
*
* <p>A new buffer is allocated because arrays are not resizable.
+ *
+ * @param size New buffer size.
*/
void resize(int size) {
double[] newBuffer = new double[size];
@@ -146,9 +148,7 @@
m_front = 0;
}
- /**
- * Sets internal buffer contents to zero.
- */
+ /** Sets internal buffer contents to zero. */
public void clear() {
for (int i = 0; i < m_data.length; i++) {
m_data[i] = 0.0;
@@ -160,6 +160,7 @@
/**
* Get the element at the provided index relative to the start of the buffer.
*
+ * @param index Index into the buffer.
* @return Element at index starting from front of buffer.
*/
public double get(int index) {
@@ -168,6 +169,8 @@
/**
* Increment an index modulo the length of the m_data buffer.
+ *
+ * @param index Index into the buffer.
*/
private int moduloInc(int index) {
return (index + 1) % m_data.length;
@@ -175,6 +178,8 @@
/**
* Decrement an index modulo the length of the m_data buffer.
+ *
+ * @param index Index into the buffer.
*/
private int moduloDec(int index) {
if (index == 0) {
diff --git a/wpiutil/src/main/java/edu/wpi/first/wpiutil/CombinedRuntimeLoader.java b/wpiutil/src/main/java/edu/wpi/first/util/CombinedRuntimeLoader.java
similarity index 75%
rename from wpiutil/src/main/java/edu/wpi/first/wpiutil/CombinedRuntimeLoader.java
rename to wpiutil/src/main/java/edu/wpi/first/util/CombinedRuntimeLoader.java
index 8f0d53e..9b23144 100644
--- a/wpiutil/src/main/java/edu/wpi/first/wpiutil/CombinedRuntimeLoader.java
+++ b/wpiutil/src/main/java/edu/wpi/first/util/CombinedRuntimeLoader.java
@@ -1,12 +1,11 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2020 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. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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.wpiutil;
+package edu.wpi.first.util;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
@@ -17,12 +16,8 @@
import java.util.Map;
import java.util.Objects;
-import com.fasterxml.jackson.core.type.TypeReference;
-import com.fasterxml.jackson.databind.ObjectMapper;
-
public final class CombinedRuntimeLoader {
- private CombinedRuntimeLoader() {
- }
+ private CombinedRuntimeLoader() {}
private static String extractionDirectory;
@@ -38,32 +33,34 @@
private static String getLoadErrorMessage(String libraryName, UnsatisfiedLinkError ule) {
StringBuilder msg = new StringBuilder(512);
- msg.append(libraryName).append(" could not be loaded from path\n"
- + "\tattempted to load for platform ")
+ msg.append(libraryName)
+ .append(" could not be loaded from path\n" + "\tattempted to load for platform ")
.append(RuntimeDetector.getPlatformPath())
.append("\nLast Load Error: \n")
.append(ule.getMessage())
.append('\n');
if (RuntimeDetector.isWindows()) {
- msg.append("A common cause of this error is missing the C++ runtime.\n"
- + "Download the latest at https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads\n");
+ msg.append(
+ "A common cause of this error is missing the C++ runtime.\n"
+ + "Download the latest at https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads\n");
}
return msg.toString();
}
/**
* Extract a list of native libraries.
- * @param <T> The class where the resources would be located
- * @param clazz The actual class object
- * @param resourceName The resource name on the classpath to use for file lookup
- * @return List of all libraries that were extracted
- * @throws IOException Thrown if resource not found or file could not be extracted
+ *
+ * @param <T> The class where the resources would be located
+ * @param clazz The actual class object
+ * @param resourceName The resource name on the classpath to use for file lookup
+ * @return List of all libraries that were extracted
+ * @throws IOException Thrown if resource not found or file could not be extracted
*/
- @SuppressWarnings({"PMD.AvoidInstantiatingObjectsInLoops", "PMD.UnnecessaryCastRule"})
+ @SuppressWarnings({"PMD.UnnecessaryCastRule", "unchecked"})
public static <T> List<String> extractLibraries(Class<T> clazz, String resourceName)
throws IOException {
- TypeReference<HashMap<String, Object>> typeRef = new TypeReference<HashMap<String, Object>>() {
- };
+ TypeReference<HashMap<String, Object>> typeRef =
+ new TypeReference<HashMap<String, Object>>() {};
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> map;
try (var stream = clazz.getResourceAsStream(resourceName)) {
@@ -103,7 +100,11 @@
if (outputFile.toFile().exists()) {
continue;
}
- outputFile.getParent().toFile().mkdirs();
+ var parent = outputFile.getParent();
+ if (parent == null) {
+ throw new IOException("Output file has no parent");
+ }
+ parent.toFile().mkdirs();
try (var os = Files.newOutputStream(outputFile)) {
int readBytes;
@@ -119,6 +120,7 @@
/**
* Load a single library from a list of extracted files.
+ *
* @param libraryName The library name to load
* @param extractedFiles The extracted files to search
* @throws IOException If library was not found
@@ -153,8 +155,8 @@
/**
* Load a list of native libraries out of a single directory.
*
- * @param <T> The class where the resources would be located
- * @param clazz The actual class object
+ * @param <T> The class where the resources would be located
+ * @param clazz The actual class object
* @param librariesToLoad List of libraries to load
* @throws IOException Throws an IOException if not found
*/
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/ErrorMessages.java b/wpiutil/src/main/java/edu/wpi/first/util/ErrorMessages.java
new file mode 100644
index 0000000..9292ed9
--- /dev/null
+++ b/wpiutil/src/main/java/edu/wpi/first/util/ErrorMessages.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.util;
+
+import static java.util.Objects.requireNonNull;
+
+/** Utility class for common WPILib error messages. */
+public final class ErrorMessages {
+ /** Utility class, so constructor is private. */
+ private ErrorMessages() {
+ throw new UnsupportedOperationException("This is a utility class!");
+ }
+
+ /**
+ * Requires that a parameter of a method not be null; prints an error message with helpful
+ * debugging instructions if the parameter is null.
+ *
+ * @param <T> Type of object.
+ * @param obj The parameter that must not be null.
+ * @param paramName The name of the parameter.
+ * @param methodName The name of the method.
+ * @return The object parameter confirmed not to be null.
+ */
+ public static <T> T requireNonNullParam(T obj, String paramName, String methodName) {
+ return requireNonNull(
+ obj,
+ "Parameter "
+ + paramName
+ + " in method "
+ + methodName
+ + " was null when it"
+ + " should not have been! Check the stacktrace to find the responsible line of code - "
+ + "usually, it is the first line of user-written code indicated in the stacktrace. "
+ + "Make sure all objects passed to the method in question were properly initialized -"
+ + " note that this may not be obvious if it is being called under "
+ + "dynamically-changing conditions! Please do not seek additional technical assistance"
+ + " without doing this first!");
+ }
+}
diff --git a/wpiutil/src/main/java/edu/wpi/first/wpiutil/RuntimeDetector.java b/wpiutil/src/main/java/edu/wpi/first/util/RuntimeDetector.java
similarity index 81%
rename from wpiutil/src/main/java/edu/wpi/first/wpiutil/RuntimeDetector.java
rename to wpiutil/src/main/java/edu/wpi/first/util/RuntimeDetector.java
index b4c07b7..320bc5b 100644
--- a/wpiutil/src/main/java/edu/wpi/first/wpiutil/RuntimeDetector.java
+++ b/wpiutil/src/main/java/edu/wpi/first/util/RuntimeDetector.java
@@ -1,11 +1,8 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2017-2020 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. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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.wpiutil;
+package edu.wpi.first.util;
import java.io.BufferedReader;
import java.io.File;
@@ -18,13 +15,11 @@
private static String fileExtension;
private static String filePath;
- @SuppressWarnings("PMD.CyclomaticComplexity")
private static synchronized void computePlatform() {
if (fileExtension != null && filePath != null && filePrefix != null) {
return;
}
-
boolean intel32 = is32BitIntel();
boolean intel64 = is64BitIntel();
@@ -67,6 +62,8 @@
/**
* Get the file prefix for the current system.
+ *
+ * @return The file prefix.
*/
public static synchronized String getFilePrefix() {
computePlatform();
@@ -76,6 +73,8 @@
/**
* Get the file extension for the current system.
+ *
+ * @return The file extension.
*/
public static synchronized String getFileExtension() {
computePlatform();
@@ -85,6 +84,8 @@
/**
* Get the platform path for the current system.
+ *
+ * @return The platform path.
*/
public static synchronized String getPlatformPath() {
computePlatform();
@@ -94,6 +95,9 @@
/**
* Get the path to the requested resource.
+ *
+ * @param libName Library name.
+ * @return The path to the requested resource.
*/
public static synchronized String getLibraryResource(String libName) {
computePlatform();
@@ -103,6 +107,9 @@
/**
* Get the path to the hash to the requested resource.
+ *
+ * @param libName Library name.
+ * @return The path to the hash to the requested resource.
*/
public static synchronized String getHashLibraryResource(String libName) {
computePlatform();
@@ -110,25 +117,35 @@
return filePath + libName + ".hash";
}
+ /**
+ * Check if hardware platform is Athena.
+ *
+ * @return True if hardware platform is Athena.
+ */
public static boolean isAthena() {
File runRobotFile = new File("/usr/local/frc/bin/frcRunRobot.sh");
return runRobotFile.exists();
}
- /** check if os is raspbian.
+ /**
+ * Check if OS is Raspbian.
*
- * @return if os is raspbian
+ * @return True if OS is Raspbian.
*/
public static boolean isRaspbian() {
try (BufferedReader reader = Files.newBufferedReader(Paths.get("/etc/os-release"))) {
String value = reader.readLine();
+ if (value == null) {
+ return false;
+ }
return value.contains("Raspbian");
} catch (IOException ex) {
return false;
}
}
- /** check if architecture is aarch64.
+ /**
+ * check if architecture is aarch64.
*
* @return if architecture is aarch64
*/
@@ -158,7 +175,5 @@
return "amd64".equals(arch) || "x86_64".equals(arch);
}
- private RuntimeDetector() {
-
- }
+ private RuntimeDetector() {}
}
diff --git a/wpiutil/src/main/java/edu/wpi/first/wpiutil/RuntimeLoader.java b/wpiutil/src/main/java/edu/wpi/first/util/RuntimeLoader.java
similarity index 75%
rename from wpiutil/src/main/java/edu/wpi/first/wpiutil/RuntimeLoader.java
rename to wpiutil/src/main/java/edu/wpi/first/util/RuntimeLoader.java
index 21193a2..8a2c11a 100644
--- a/wpiutil/src/main/java/edu/wpi/first/wpiutil/RuntimeLoader.java
+++ b/wpiutil/src/main/java/edu/wpi/first/util/RuntimeLoader.java
@@ -1,11 +1,8 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2020 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. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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.wpiutil;
+package edu.wpi.first.util;
import java.io.File;
import java.io.IOException;
@@ -25,6 +22,8 @@
/**
* Gets the default extration root location (~/.wpilib/nativecache).
+ *
+ * @return The default extraction root location.
*/
public static synchronized String getDefaultExtractionRoot() {
if (defaultExtractionRoot != null) {
@@ -42,8 +41,9 @@
/**
* Creates a new library loader.
*
- * <p>Resources loaded on disk from extractionRoot, and from classpath from the
- * passed in class. Library name is the passed in name.
+ * @param libraryName Name of library to load.
+ * @param extractionRoot Location from which to load the library.
+ * @param cls Class whose classpath the given library belongs.
*/
public RuntimeLoader(String libraryName, String extractionRoot, Class<T> cls) {
m_libraryName = libraryName;
@@ -51,24 +51,34 @@
m_extractionRoot = extractionRoot;
}
+ /**
+ * Returns a load error message given the information in the provided UnsatisfiedLinkError.
+ *
+ * @param ule UnsatisfiedLinkError object.
+ * @return A load error message.
+ */
private String getLoadErrorMessage(UnsatisfiedLinkError ule) {
StringBuilder msg = new StringBuilder(512);
msg.append(m_libraryName)
- .append(" could not be loaded from path or an embedded resource.\n"
- + "\tattempted to load for platform ")
- .append(RuntimeDetector.getPlatformPath())
- .append("\nLast Load Error: \n")
- .append(ule.getMessage())
- .append('\n');
+ .append(
+ " could not be loaded from path or an embedded resource.\n"
+ + "\tattempted to load for platform ")
+ .append(RuntimeDetector.getPlatformPath())
+ .append("\nLast Load Error: \n")
+ .append(ule.getMessage())
+ .append('\n');
if (RuntimeDetector.isWindows()) {
- msg.append("A common cause of this error is missing the C++ runtime.\n"
- + "Download the latest at https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads\n");
+ msg.append(
+ "A common cause of this error is missing the C++ runtime.\n"
+ + "Download the latest at https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads\n");
}
return msg.toString();
}
/**
* Loads a native library.
+ *
+ * @throws IOException if the library fails to load
*/
@SuppressWarnings("PMD.PreserveStackTrace")
public void loadLibrary() throws IOException {
@@ -95,7 +105,13 @@
if (resIs == null) {
throw new IOException(getLoadErrorMessage(ule));
}
- jniLibrary.getParentFile().mkdirs();
+
+ var parentFile = jniLibrary.getParentFile();
+ if (parentFile == null) {
+ throw new IOException("JNI library has no parent file");
+ }
+ parentFile.mkdirs();
+
try (OutputStream os = Files.newOutputStream(jniLibrary.toPath())) {
byte[] buffer = new byte[0xFFFF]; // 64K copy buffer
int readBytes;
@@ -113,9 +129,10 @@
/**
* Load a native library by directly hashing the file.
+ *
+ * @throws IOException if the library failed to load
*/
- @SuppressWarnings({"PMD.NPathComplexity", "PMD.PreserveStackTrace", "PMD.EmptyWhileStmt",
- "PMD.AvoidThrowingRawExceptionTypes", "PMD.CyclomaticComplexity"})
+ @SuppressWarnings({"PMD.PreserveStackTrace", "PMD.EmptyWhileStmt"})
public void loadLibraryHashed() throws IOException {
try {
// First, try loading path
@@ -160,7 +177,13 @@
if (resIs == null) {
throw new IOException(getLoadErrorMessage(ule));
}
- jniLibrary.getParentFile().mkdirs();
+
+ var parentFile = jniLibrary.getParentFile();
+ if (parentFile == null) {
+ throw new IOException("JNI library has no parent file");
+ }
+ parentFile.mkdirs();
+
try (OutputStream os = Files.newOutputStream(jniLibrary.toPath())) {
byte[] buffer = new byte[0xFFFF]; // 64K copy buffer
int readBytes;
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/WPIUtilJNI.java b/wpiutil/src/main/java/edu/wpi/first/util/WPIUtilJNI.java
new file mode 100644
index 0000000..b7b2f59
--- /dev/null
+++ b/wpiutil/src/main/java/edu/wpi/first/util/WPIUtilJNI.java
@@ -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.
+
+package edu.wpi.first.util;
+
+import java.io.IOException;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public final class WPIUtilJNI {
+ static boolean libraryLoaded = false;
+ static RuntimeLoader<WPIUtilJNI> loader = null;
+
+ public static class Helper {
+ private static AtomicBoolean extractOnStaticLoad = new AtomicBoolean(true);
+
+ public static boolean getExtractOnStaticLoad() {
+ return extractOnStaticLoad.get();
+ }
+
+ public static void setExtractOnStaticLoad(boolean load) {
+ extractOnStaticLoad.set(load);
+ }
+ }
+
+ static {
+ if (Helper.getExtractOnStaticLoad()) {
+ try {
+ loader =
+ new RuntimeLoader<>(
+ "wpiutiljni", RuntimeLoader.getDefaultExtractionRoot(), WPIUtilJNI.class);
+ loader.loadLibrary();
+ } catch (IOException ex) {
+ ex.printStackTrace();
+ System.exit(1);
+ }
+ libraryLoaded = true;
+ }
+ }
+
+ /**
+ * Force load the library.
+ *
+ * @throws IOException if the library failed to load
+ */
+ public static synchronized void forceLoad() throws IOException {
+ if (libraryLoaded) {
+ return;
+ }
+ loader =
+ new RuntimeLoader<>(
+ "wpiutiljni", RuntimeLoader.getDefaultExtractionRoot(), WPIUtilJNI.class);
+ loader.loadLibrary();
+ libraryLoaded = true;
+ }
+
+ public static native void enableMockTime();
+
+ public static native void setMockTime(long time);
+
+ public static native long now();
+
+ public static native void addPortForwarder(int port, String remoteHost, int remotePort);
+
+ public static native void removePortForwarder(int port);
+
+ public static native int createEvent(boolean manualReset, boolean initialState);
+
+ public static native void destroyEvent(int eventHandle);
+
+ public static native void setEvent(int eventHandle);
+
+ public static native void resetEvent(int eventHandle);
+
+ public static native int createSemaphore(int initialCount, int maximumCount);
+
+ public static native void destroySemaphore(int semHandle);
+
+ public static native boolean releaseSemaphore(int semHandle, int releaseCount);
+
+ /**
+ * Waits for an handle to be signaled.
+ *
+ * @param handle handle to wait on
+ * @throws InterruptedException on failure (e.g. object was destroyed)
+ */
+ public static native void waitForObject(int handle) throws InterruptedException;
+
+ /**
+ * Waits for an handle to be signaled, with timeout.
+ *
+ * @param handle handle to wait on
+ * @param timeout timeout in seconds
+ * @return True if timeout reached without handle being signaled
+ * @throws InterruptedException on failure (e.g. object was destroyed)
+ */
+ public static native boolean waitForObjectTimeout(int handle, double timeout)
+ throws InterruptedException;
+
+ /**
+ * Waits for one or more handles to be signaled.
+ *
+ * <p>Invalid handles are treated as signaled; the returned array will have the handle error bit
+ * set for any invalid handles.
+ *
+ * @param handles array of handles to wait on
+ * @return array of signaled handles
+ * @throws InterruptedException on failure (e.g. no objects were signaled)
+ */
+ public static native int[] waitForObjects(int[] handles) throws InterruptedException;
+
+ /**
+ * Waits for one or more handles to be signaled, with timeout.
+ *
+ * <p>Invalid handles are treated as signaled; the returned array will have the handle error bit
+ * set for any invalid handles.
+ *
+ * @param handles array of handles to wait on
+ * @param timeout timeout in seconds
+ * @return array of signaled handles; empty if timeout reached without any handle being signaled
+ * @throws InterruptedException on failure (e.g. no objects were signaled and no timeout)
+ */
+ public static native int[] waitForObjectsTimeout(int[] handles, double timeout)
+ throws InterruptedException;
+}
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/concurrent/Event.java b/wpiutil/src/main/java/edu/wpi/first/util/concurrent/Event.java
new file mode 100644
index 0000000..bda2e88
--- /dev/null
+++ b/wpiutil/src/main/java/edu/wpi/first/util/concurrent/Event.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.util.concurrent;
+
+import edu.wpi.first.util.WPIUtilJNI;
+
+/**
+ * An atomic signaling event for synchronization.
+ *
+ * <p>Events have binary state (signaled or not signaled) and may be either automatically reset or
+ * manually reset. Automatic-reset events go to non-signaled state when a waitForObject is woken up
+ * by the event; manual-reset events require reset() to be called to set the event to non-signaled
+ * state; if reset() is not called, any waiter on that event will immediately wake when called.
+ */
+public final class Event implements AutoCloseable {
+ /**
+ * Constructor.
+ *
+ * @param manualReset true for manual reset, false for automatic reset
+ * @param initialState true to make the event initially in signaled state
+ */
+ public Event(boolean manualReset, boolean initialState) {
+ m_handle = WPIUtilJNI.createEvent(manualReset, initialState);
+ }
+
+ /**
+ * Constructor. Initial state is false.
+ *
+ * @param manualReset true for manual reset, false for automatic reset
+ */
+ public Event(boolean manualReset) {
+ this(manualReset, false);
+ }
+
+ /** Constructor. Automatic reset, initial state is false. */
+ public Event() {
+ this(false, false);
+ }
+
+ @Override
+ public void close() {
+ if (m_handle != 0) {
+ WPIUtilJNI.destroyEvent(m_handle);
+ m_handle = 0;
+ }
+ }
+
+ /**
+ * Gets the event handle (e.g. for waitForObject).
+ *
+ * @return handle
+ */
+ public int getHandle() {
+ return m_handle;
+ }
+
+ /** Sets the event to signaled state. */
+ public void set() {
+ WPIUtilJNI.setEvent(m_handle);
+ }
+
+ /** Sets the event to non-signaled state. */
+ public void reset() {
+ WPIUtilJNI.resetEvent(m_handle);
+ }
+
+ private int m_handle;
+}
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/concurrent/Semaphore.java b/wpiutil/src/main/java/edu/wpi/first/util/concurrent/Semaphore.java
new file mode 100644
index 0000000..4e9e85f
--- /dev/null
+++ b/wpiutil/src/main/java/edu/wpi/first/util/concurrent/Semaphore.java
@@ -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.
+
+package edu.wpi.first.util.concurrent;
+
+import edu.wpi.first.util.WPIUtilJNI;
+
+/**
+ * A semaphore for synchronization.
+ *
+ * <p>Semaphores keep an internal counter. Releasing the semaphore increases the count. A semaphore
+ * with a non-zero count is considered signaled. When a waiter wakes up it atomically decrements the
+ * count by 1. This is generally useful in a single-supplier, multiple-consumer scenario.
+ */
+public final class Semaphore implements AutoCloseable {
+ /**
+ * Constructor.
+ *
+ * @param initialCount initial value for the semaphore's internal counter
+ * @param maximumCount maximum value for the samephore's internal counter
+ */
+ public Semaphore(int initialCount, int maximumCount) {
+ m_handle = WPIUtilJNI.createSemaphore(initialCount, maximumCount);
+ }
+
+ /**
+ * Constructor. Maximum count is Integer.MAX_VALUE.
+ *
+ * @param initialCount initial value for the semaphore's internal counter
+ */
+ public Semaphore(int initialCount) {
+ this(initialCount, Integer.MAX_VALUE);
+ }
+
+ /** Constructor. Initial count is 0, maximum count is Integer.MAX_VALUE. */
+ public Semaphore() {
+ this(0, Integer.MAX_VALUE);
+ }
+
+ @Override
+ public void close() {
+ if (m_handle != 0) {
+ WPIUtilJNI.destroySemaphore(m_handle);
+ m_handle = 0;
+ }
+ }
+
+ /**
+ * Gets the semaphore handle (e.g. for waitForObject).
+ *
+ * @return handle
+ */
+ public int getHandle() {
+ return m_handle;
+ }
+
+ /**
+ * Releases N counts of the semaphore.
+ *
+ * @param releaseCount amount to add to semaphore's internal counter; must be positive
+ * @return True on successful release, false on failure (e.g. release count would exceed maximum
+ * value, or handle invalid)
+ */
+ public boolean release(int releaseCount) {
+ return WPIUtilJNI.releaseSemaphore(m_handle, releaseCount);
+ }
+
+ /**
+ * Releases 1 count of the semaphore.
+ *
+ * @return True on successful release, false on failure (e.g. release count would exceed maximum
+ * value, or handle invalid)
+ */
+ public boolean release() {
+ return release(1);
+ }
+
+ private int m_handle;
+}
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/function/BooleanConsumer.java b/wpiutil/src/main/java/edu/wpi/first/util/function/BooleanConsumer.java
new file mode 100644
index 0000000..98fd6a2
--- /dev/null
+++ b/wpiutil/src/main/java/edu/wpi/first/util/function/BooleanConsumer.java
@@ -0,0 +1,22 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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.function;
+
+/**
+ * Represents an operation that accepts a single boolean-valued argument and returns no result. This
+ * is the primitive type specialization of {@link java.util.function.Consumer} for boolean. Unlike
+ * most other functional interfaces, BooleanConsumer is expected to operate via side-effects.
+ *
+ * <p>This is a functional interface whose functional method is {@link #accept(boolean)}.
+ */
+@FunctionalInterface
+public interface BooleanConsumer {
+ /**
+ * Performs this operation on the given argument.
+ *
+ * @param value the input argument
+ */
+ void accept(boolean value);
+}
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/function/FloatConsumer.java b/wpiutil/src/main/java/edu/wpi/first/util/function/FloatConsumer.java
new file mode 100644
index 0000000..0fc7ff7
--- /dev/null
+++ b/wpiutil/src/main/java/edu/wpi/first/util/function/FloatConsumer.java
@@ -0,0 +1,22 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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.function;
+
+/**
+ * Represents an operation that accepts a single float-valued argument and returns no result. This
+ * is the primitive type specialization of {@link java.util.function.Consumer} for float. Unlike
+ * most other functional interfaces, BooleanConsumer is expected to operate via side-effects.
+ *
+ * <p>This is a functional interface whose functional method is {@link #accept(float)}.
+ */
+@FunctionalInterface
+public interface FloatConsumer {
+ /**
+ * Performs this operation on the given argument.
+ *
+ * @param value the input argument
+ */
+ void accept(float value);
+}
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/function/FloatSupplier.java b/wpiutil/src/main/java/edu/wpi/first/util/function/FloatSupplier.java
new file mode 100644
index 0000000..eb94b2b
--- /dev/null
+++ b/wpiutil/src/main/java/edu/wpi/first/util/function/FloatSupplier.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.util.function;
+
+/**
+ * Represents a supplier of float-valued results.
+ *
+ * <p>This is the float-producing primitive specialization of {@link java.util.function.Supplier}.
+ *
+ * <p>There is no requirement that a distinct result be returned each time the supplier is invoked.
+ *
+ * <p>This is a functional interface whose functional method is {@link #getAsFloat()}.
+ */
+@FunctionalInterface
+public interface FloatSupplier {
+ /**
+ * Gets a result.
+ *
+ * @return a result
+ */
+ float getAsFloat();
+}
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/net/PortForwarder.java b/wpiutil/src/main/java/edu/wpi/first/util/net/PortForwarder.java
new file mode 100644
index 0000000..5c4c20a
--- /dev/null
+++ b/wpiutil/src/main/java/edu/wpi/first/util/net/PortForwarder.java
@@ -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.
+
+package edu.wpi.first.util.net;
+
+import edu.wpi.first.util.WPIUtilJNI;
+
+/**
+ * Forward ports to another host. This is primarily useful for accessing Ethernet-connected devices
+ * from a computer tethered to the RoboRIO USB port.
+ */
+public final class PortForwarder {
+ private PortForwarder() {
+ throw new UnsupportedOperationException("This is a utility class!");
+ }
+
+ /**
+ * Forward a local TCP port to a remote host and port. Note that local ports less than 1024 won't
+ * work as a normal user.
+ *
+ * @param port local port number
+ * @param remoteHost remote IP address / DNS name
+ * @param remotePort remote port number
+ */
+ public static void add(int port, String remoteHost, int remotePort) {
+ WPIUtilJNI.addPortForwarder(port, remoteHost, remotePort);
+ }
+
+ /**
+ * Stop TCP forwarding on a port.
+ *
+ * @param port local port number
+ */
+ public static void remove(int port) {
+ WPIUtilJNI.removePortForwarder(port);
+ }
+}
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/sendable/Sendable.java b/wpiutil/src/main/java/edu/wpi/first/util/sendable/Sendable.java
new file mode 100644
index 0000000..ad1fe28
--- /dev/null
+++ b/wpiutil/src/main/java/edu/wpi/first/util/sendable/Sendable.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.util.sendable;
+
+/** The base interface for objects that can be sent over the network. */
+public interface Sendable {
+ /**
+ * Initializes this {@link Sendable} object.
+ *
+ * @param builder sendable builder
+ */
+ void initSendable(SendableBuilder builder);
+}
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/sendable/SendableBuilder.java b/wpiutil/src/main/java/edu/wpi/first/util/sendable/SendableBuilder.java
new file mode 100644
index 0000000..35d98fa
--- /dev/null
+++ b/wpiutil/src/main/java/edu/wpi/first/util/sendable/SendableBuilder.java
@@ -0,0 +1,127 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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.sendable;
+
+import edu.wpi.first.util.function.BooleanConsumer;
+import java.util.function.BooleanSupplier;
+import java.util.function.Consumer;
+import java.util.function.DoubleConsumer;
+import java.util.function.DoubleSupplier;
+import java.util.function.Supplier;
+
+public interface SendableBuilder {
+ /** The backend kinds used for the sendable builder. */
+ enum BackendKind {
+ kUnknown,
+ kNetworkTables
+ }
+
+ /**
+ * Set the string representation of the named data type that will be used by the smart dashboard
+ * for this sendable.
+ *
+ * @param type data type
+ */
+ void setSmartDashboardType(String type);
+
+ /**
+ * Set a flag indicating if this sendable should be treated as an actuator. By default this flag
+ * is false.
+ *
+ * @param value true if actuator, false if not
+ */
+ void setActuator(boolean value);
+
+ /**
+ * Set the function that should be called to set the Sendable into a safe state. This is called
+ * when entering and exiting Live Window mode.
+ *
+ * @param func function
+ */
+ void setSafeState(Runnable func);
+
+ /**
+ * Add a boolean property.
+ *
+ * @param key property name
+ * @param getter getter function (returns current value)
+ * @param setter setter function (sets new value)
+ */
+ void addBooleanProperty(String key, BooleanSupplier getter, BooleanConsumer setter);
+
+ /**
+ * Add a double property.
+ *
+ * @param key property name
+ * @param getter getter function (returns current value)
+ * @param setter setter function (sets new value)
+ */
+ void addDoubleProperty(String key, DoubleSupplier getter, DoubleConsumer setter);
+
+ /**
+ * Add a string property.
+ *
+ * @param key property name
+ * @param getter getter function (returns current value)
+ * @param setter setter function (sets new value)
+ */
+ void addStringProperty(String key, Supplier<String> getter, Consumer<String> setter);
+
+ /**
+ * Add a boolean array property.
+ *
+ * @param key property name
+ * @param getter getter function (returns current value)
+ * @param setter setter function (sets new value)
+ */
+ void addBooleanArrayProperty(String key, Supplier<boolean[]> getter, Consumer<boolean[]> setter);
+
+ /**
+ * Add a double array property.
+ *
+ * @param key property name
+ * @param getter getter function (returns current value)
+ * @param setter setter function (sets new value)
+ */
+ void addDoubleArrayProperty(String key, Supplier<double[]> getter, Consumer<double[]> setter);
+
+ /**
+ * Add a string array property.
+ *
+ * @param key property name
+ * @param getter getter function (returns current value)
+ * @param setter setter function (sets new value)
+ */
+ void addStringArrayProperty(String key, Supplier<String[]> getter, Consumer<String[]> setter);
+
+ /**
+ * Add a raw property.
+ *
+ * @param key property name
+ * @param getter getter function (returns current value)
+ * @param setter setter function (sets new value)
+ */
+ void addRawProperty(String key, Supplier<byte[]> getter, Consumer<byte[]> setter);
+
+ /**
+ * Gets the kind of backend being used.
+ *
+ * @return Backend kind
+ */
+ BackendKind getBackendKind();
+
+ /**
+ * Return whether this sendable has been published.
+ *
+ * @return True if it has been published, false if not.
+ */
+ boolean isPublished();
+
+ /** Update the published values by calling the getters for all properties. */
+ void update();
+
+ /** Clear properties. */
+ void clearProperties();
+}
diff --git a/wpiutil/src/main/java/edu/wpi/first/util/sendable/SendableRegistry.java b/wpiutil/src/main/java/edu/wpi/first/util/sendable/SendableRegistry.java
new file mode 100644
index 0000000..9d919af
--- /dev/null
+++ b/wpiutil/src/main/java/edu/wpi/first/util/sendable/SendableRegistry.java
@@ -0,0 +1,513 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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.sendable;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.WeakHashMap;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+/**
+ * The SendableRegistry class is the public interface for registering sensors and actuators for use
+ * on dashboards and LiveWindow.
+ */
+public class SendableRegistry {
+ private static class Component {
+ Component() {}
+
+ Component(Sendable sendable) {
+ m_sendable = new WeakReference<>(sendable);
+ }
+
+ WeakReference<Sendable> m_sendable;
+ SendableBuilder m_builder;
+ String m_name;
+ String m_subsystem = "Ungrouped";
+ WeakReference<Sendable> m_parent;
+ boolean m_liveWindow;
+ Object[] m_data;
+
+ void setName(String moduleType, int channel) {
+ m_name = moduleType + "[" + channel + "]";
+ }
+
+ void setName(String moduleType, int moduleNumber, int channel) {
+ m_name = moduleType + "[" + moduleNumber + "," + channel + "]";
+ }
+ }
+
+ private static Supplier<SendableBuilder> liveWindowFactory;
+ private static final Map<Object, Component> components = new WeakHashMap<>();
+ private static int nextDataHandle;
+
+ private static Component getOrAdd(Sendable sendable) {
+ Component comp = components.get(sendable);
+ if (comp == null) {
+ comp = new Component(sendable);
+ components.put(sendable, comp);
+ } else {
+ if (comp.m_sendable == null) {
+ comp.m_sendable = new WeakReference<>(sendable);
+ }
+ }
+ return comp;
+ }
+
+ private SendableRegistry() {
+ throw new UnsupportedOperationException("This is a utility class!");
+ }
+
+ /**
+ * Sets the factory for LiveWindow builders.
+ *
+ * @param factory factory function
+ */
+ public static synchronized void setLiveWindowBuilderFactory(Supplier<SendableBuilder> factory) {
+ liveWindowFactory = factory;
+ }
+
+ /**
+ * Adds an object to the registry.
+ *
+ * @param sendable object to add
+ * @param name component name
+ */
+ public static synchronized void add(Sendable sendable, String name) {
+ Component comp = getOrAdd(sendable);
+ comp.m_name = name;
+ }
+
+ /**
+ * Adds an object to the registry.
+ *
+ * @param sendable object to add
+ * @param moduleType A string that defines the module name in the label for the value
+ * @param channel The channel number the device is plugged into
+ */
+ public static synchronized void add(Sendable sendable, String moduleType, int channel) {
+ Component comp = getOrAdd(sendable);
+ comp.setName(moduleType, channel);
+ }
+
+ /**
+ * Adds an object to the registry.
+ *
+ * @param sendable object to add
+ * @param moduleType A string that defines the module name in the label for the value
+ * @param moduleNumber The number of the particular module type
+ * @param channel The channel number the device is plugged into
+ */
+ public static synchronized void add(
+ Sendable sendable, String moduleType, int moduleNumber, int channel) {
+ Component comp = getOrAdd(sendable);
+ comp.setName(moduleType, moduleNumber, channel);
+ }
+
+ /**
+ * Adds an object to the registry.
+ *
+ * @param sendable object to add
+ * @param subsystem subsystem name
+ * @param name component name
+ */
+ public static synchronized void add(Sendable sendable, String subsystem, String name) {
+ Component comp = getOrAdd(sendable);
+ comp.m_name = name;
+ comp.m_subsystem = subsystem;
+ }
+
+ /**
+ * Adds an object to the registry and LiveWindow.
+ *
+ * @param sendable object to add
+ * @param name component name
+ */
+ public static synchronized void addLW(Sendable sendable, String name) {
+ Component comp = getOrAdd(sendable);
+ if (liveWindowFactory != null) {
+ comp.m_builder = liveWindowFactory.get();
+ }
+ comp.m_liveWindow = true;
+ comp.m_name = name;
+ }
+
+ /**
+ * Adds an object to the registry and LiveWindow.
+ *
+ * @param sendable object to add
+ * @param moduleType A string that defines the module name in the label for the value
+ * @param channel The channel number the device is plugged into
+ */
+ public static synchronized void addLW(Sendable sendable, String moduleType, int channel) {
+ Component comp = getOrAdd(sendable);
+ if (liveWindowFactory != null) {
+ comp.m_builder = liveWindowFactory.get();
+ }
+ comp.m_liveWindow = true;
+ comp.setName(moduleType, channel);
+ }
+
+ /**
+ * Adds an object to the registry and LiveWindow.
+ *
+ * @param sendable object to add
+ * @param moduleType A string that defines the module name in the label for the value
+ * @param moduleNumber The number of the particular module type
+ * @param channel The channel number the device is plugged into
+ */
+ public static synchronized void addLW(
+ Sendable sendable, String moduleType, int moduleNumber, int channel) {
+ Component comp = getOrAdd(sendable);
+ if (liveWindowFactory != null) {
+ comp.m_builder = liveWindowFactory.get();
+ }
+ comp.m_liveWindow = true;
+ comp.setName(moduleType, moduleNumber, channel);
+ }
+
+ /**
+ * Adds an object to the registry and LiveWindow.
+ *
+ * @param sendable object to add
+ * @param subsystem subsystem name
+ * @param name component name
+ */
+ public static synchronized void addLW(Sendable sendable, String subsystem, String name) {
+ Component comp = getOrAdd(sendable);
+ if (liveWindowFactory != null) {
+ comp.m_builder = liveWindowFactory.get();
+ }
+ comp.m_liveWindow = true;
+ comp.m_name = name;
+ comp.m_subsystem = subsystem;
+ }
+
+ /**
+ * Adds a child object to an object. Adds the child object to the registry if it's not already
+ * present.
+ *
+ * @param parent parent object
+ * @param child child object
+ */
+ public static synchronized void addChild(Sendable parent, Object child) {
+ Component comp = components.get(child);
+ if (comp == null) {
+ comp = new Component();
+ components.put(child, comp);
+ }
+ comp.m_parent = new WeakReference<>(parent);
+ }
+
+ /**
+ * Removes an object from the registry.
+ *
+ * @param sendable object to remove
+ * @return true if the object was removed; false if it was not present
+ */
+ public static synchronized boolean remove(Sendable sendable) {
+ return components.remove(sendable) != null;
+ }
+
+ /**
+ * Determines if an object is in the registry.
+ *
+ * @param sendable object to check
+ * @return True if in registry, false if not.
+ */
+ public static synchronized boolean contains(Sendable sendable) {
+ return components.containsKey(sendable);
+ }
+
+ /**
+ * Gets the name of an object.
+ *
+ * @param sendable object
+ * @return Name (empty if object is not in registry)
+ */
+ public static synchronized String getName(Sendable sendable) {
+ Component comp = components.get(sendable);
+ if (comp == null) {
+ return "";
+ }
+ return comp.m_name;
+ }
+
+ /**
+ * Sets the name of an object.
+ *
+ * @param sendable object
+ * @param name name
+ */
+ public static synchronized void setName(Sendable sendable, String name) {
+ Component comp = components.get(sendable);
+ if (comp != null) {
+ comp.m_name = name;
+ }
+ }
+
+ /**
+ * Sets the name of an object with a channel number.
+ *
+ * @param sendable object
+ * @param moduleType A string that defines the module name in the label for the value
+ * @param channel The channel number the device is plugged into
+ */
+ public static synchronized void setName(Sendable sendable, String moduleType, int channel) {
+ Component comp = components.get(sendable);
+ if (comp != null) {
+ comp.setName(moduleType, channel);
+ }
+ }
+
+ /**
+ * Sets the name of an object with a module and channel number.
+ *
+ * @param sendable object
+ * @param moduleType A string that defines the module name in the label for the value
+ * @param moduleNumber The number of the particular module type
+ * @param channel The channel number the device is plugged into
+ */
+ public static synchronized void setName(
+ Sendable sendable, String moduleType, int moduleNumber, int channel) {
+ Component comp = components.get(sendable);
+ if (comp != null) {
+ comp.setName(moduleType, moduleNumber, channel);
+ }
+ }
+
+ /**
+ * Sets both the subsystem name and device name of an object.
+ *
+ * @param sendable object
+ * @param subsystem subsystem name
+ * @param name device name
+ */
+ public static synchronized void setName(Sendable sendable, String subsystem, String name) {
+ Component comp = components.get(sendable);
+ if (comp != null) {
+ comp.m_name = name;
+ comp.m_subsystem = subsystem;
+ }
+ }
+
+ /**
+ * Gets the subsystem name of an object.
+ *
+ * @param sendable object
+ * @return Subsystem name (empty if object is not in registry)
+ */
+ public static synchronized String getSubsystem(Sendable sendable) {
+ Component comp = components.get(sendable);
+ if (comp == null) {
+ return "";
+ }
+ return comp.m_subsystem;
+ }
+
+ /**
+ * Sets the subsystem name of an object.
+ *
+ * @param sendable object
+ * @param subsystem subsystem name
+ */
+ public static synchronized void setSubsystem(Sendable sendable, String subsystem) {
+ Component comp = components.get(sendable);
+ if (comp != null) {
+ comp.m_subsystem = subsystem;
+ }
+ }
+
+ /**
+ * Gets a unique handle for setting/getting data with setData() and getData().
+ *
+ * @return Handle
+ */
+ public static synchronized int getDataHandle() {
+ return nextDataHandle++;
+ }
+
+ /**
+ * Associates arbitrary data with an object in the registry.
+ *
+ * @param sendable object
+ * @param handle data handle returned by getDataHandle()
+ * @param data data to set
+ * @return Previous data (may be null)
+ */
+ public static synchronized Object setData(Sendable sendable, int handle, Object data) {
+ Component comp = components.get(sendable);
+ if (comp == null) {
+ return null;
+ }
+ Object rv = null;
+ if (comp.m_data == null) {
+ comp.m_data = new Object[handle + 1];
+ } else if (handle < comp.m_data.length) {
+ rv = comp.m_data[handle];
+ } else {
+ comp.m_data = Arrays.copyOf(comp.m_data, handle + 1);
+ }
+ comp.m_data[handle] = data;
+ return rv;
+ }
+
+ /**
+ * Gets arbitrary data associated with an object in the registry.
+ *
+ * @param sendable object
+ * @param handle data handle returned by getDataHandle()
+ * @return data (may be null if none associated)
+ */
+ public static synchronized Object getData(Sendable sendable, int handle) {
+ Component comp = components.get(sendable);
+ if (comp == null || comp.m_data == null || handle >= comp.m_data.length) {
+ return null;
+ }
+ return comp.m_data[handle];
+ }
+
+ /**
+ * Enables LiveWindow for an object.
+ *
+ * @param sendable object
+ */
+ public static synchronized void enableLiveWindow(Sendable sendable) {
+ Component comp = components.get(sendable);
+ if (comp != null) {
+ comp.m_liveWindow = true;
+ }
+ }
+
+ /**
+ * Disables LiveWindow for an object.
+ *
+ * @param sendable object
+ */
+ public static synchronized void disableLiveWindow(Sendable sendable) {
+ Component comp = components.get(sendable);
+ if (comp != null) {
+ comp.m_liveWindow = false;
+ }
+ }
+
+ /**
+ * Publishes an object in the registry to a builder.
+ *
+ * @param sendable object
+ * @param builder sendable builder
+ */
+ public static synchronized void publish(Sendable sendable, SendableBuilder builder) {
+ Component comp = getOrAdd(sendable);
+ if (comp.m_builder != null) {
+ comp.m_builder.clearProperties();
+ }
+ comp.m_builder = builder; // clear any current builder
+ sendable.initSendable(comp.m_builder);
+ comp.m_builder.update();
+ }
+
+ /**
+ * Updates network table information from an object.
+ *
+ * @param sendable object
+ */
+ public static synchronized void update(Sendable sendable) {
+ Component comp = components.get(sendable);
+ if (comp != null && comp.m_builder != null) {
+ comp.m_builder.update();
+ }
+ }
+
+ /** Data passed to foreachLiveWindow() callback function. */
+ public static class CallbackData {
+ /** Sendable object. */
+ @SuppressWarnings("MemberName")
+ public Sendable sendable;
+
+ /** Name. */
+ @SuppressWarnings("MemberName")
+ public String name;
+
+ /** Subsystem. */
+ @SuppressWarnings("MemberName")
+ public String subsystem;
+
+ /** Parent sendable object. */
+ @SuppressWarnings("MemberName")
+ public Sendable parent;
+
+ /** Data stored in object with setData(). Update this to change the data. */
+ @SuppressWarnings("MemberName")
+ public Object data;
+
+ /** Sendable builder for the sendable. */
+ @SuppressWarnings("MemberName")
+ public SendableBuilder builder;
+ }
+
+ // As foreachLiveWindow is single threaded, cache the components it
+ // iterates over to avoid risk of ConcurrentModificationException
+ private static List<Component> foreachComponents = new ArrayList<>();
+
+ /**
+ * Iterates over LiveWindow-enabled objects in the registry. It is *not* safe to call other
+ * SendableRegistry functions from the callback.
+ *
+ * @param dataHandle data handle to get data object passed to callback
+ * @param callback function to call for each object
+ */
+ @SuppressWarnings("PMD.AvoidCatchingThrowable")
+ public static synchronized void foreachLiveWindow(
+ int dataHandle, Consumer<CallbackData> callback) {
+ CallbackData cbdata = new CallbackData();
+ foreachComponents.clear();
+ foreachComponents.addAll(components.values());
+ for (Component comp : foreachComponents) {
+ if (comp.m_builder == null || comp.m_sendable == null) {
+ continue;
+ }
+ cbdata.sendable = comp.m_sendable.get();
+ if (cbdata.sendable != null && comp.m_liveWindow) {
+ cbdata.name = comp.m_name;
+ cbdata.subsystem = comp.m_subsystem;
+ if (comp.m_parent != null) {
+ cbdata.parent = comp.m_parent.get();
+ } else {
+ cbdata.parent = null;
+ }
+ if (comp.m_data != null && dataHandle < comp.m_data.length) {
+ cbdata.data = comp.m_data[dataHandle];
+ } else {
+ cbdata.data = null;
+ }
+ cbdata.builder = comp.m_builder;
+ try {
+ callback.accept(cbdata);
+ } catch (Throwable throwable) {
+ Throwable cause = throwable.getCause();
+ if (cause != null) {
+ throwable = cause;
+ }
+ System.err.println("Unhandled exception calling LiveWindow for " + comp.m_name + ": ");
+ throwable.printStackTrace();
+ comp.m_liveWindow = false;
+ }
+ if (cbdata.data != null) {
+ if (comp.m_data == null) {
+ comp.m_data = new Object[dataHandle + 1];
+ } else if (dataHandle >= comp.m_data.length) {
+ comp.m_data = Arrays.copyOf(comp.m_data, dataHandle + 1);
+ }
+ comp.m_data[dataHandle] = cbdata.data;
+ }
+ }
+ }
+ foreachComponents.clear();
+ }
+}
diff --git a/wpiutil/src/main/java/edu/wpi/first/wpiutil/ErrorMessages.java b/wpiutil/src/main/java/edu/wpi/first/wpiutil/ErrorMessages.java
deleted file mode 100644
index 70387eb..0000000
--- a/wpiutil/src/main/java/edu/wpi/first/wpiutil/ErrorMessages.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2020 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. */
-/*----------------------------------------------------------------------------*/
-
-package edu.wpi.first.wpiutil;
-
-import static java.util.Objects.requireNonNull;
-
-/**
- * Utility class for common WPILib error messages.
- */
-public final class ErrorMessages {
- /**
- * Utility class, so constructor is private.
- */
- private ErrorMessages() {
- throw new UnsupportedOperationException("This is a utility class!");
- }
-
- /**
- * Requires that a parameter of a method not be null; prints an error message with
- * helpful debugging instructions if the parameter is null.
- *
- * @param obj The parameter that must not be null.
- * @param paramName The name of the parameter.
- * @param methodName The name of the method.
- */
- public static <T> T requireNonNullParam(T obj, String paramName, String methodName) {
- return requireNonNull(obj,
- "Parameter " + paramName + " in method " + methodName + " was null when it"
- + " should not have been! Check the stacktrace to find the responsible line of code - "
- + "usually, it is the first line of user-written code indicated in the stacktrace. "
- + "Make sure all objects passed to the method in question were properly initialized -"
- + " note that this may not be obvious if it is being called under "
- + "dynamically-changing conditions! Please do not seek additional technical assistance"
- + " without doing this first!");
- }
-}
diff --git a/wpiutil/src/main/java/edu/wpi/first/wpiutil/WPIUtilJNI.java b/wpiutil/src/main/java/edu/wpi/first/wpiutil/WPIUtilJNI.java
deleted file mode 100644
index 3a22c7b..0000000
--- a/wpiutil/src/main/java/edu/wpi/first/wpiutil/WPIUtilJNI.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2019-2020 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. */
-/*----------------------------------------------------------------------------*/
-
-package edu.wpi.first.wpiutil;
-
-import java.io.IOException;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-public final class WPIUtilJNI {
- static boolean libraryLoaded = false;
- static RuntimeLoader<WPIUtilJNI> loader = null;
-
- public static class Helper {
- private static AtomicBoolean extractOnStaticLoad = new AtomicBoolean(true);
-
- public static boolean getExtractOnStaticLoad() {
- return extractOnStaticLoad.get();
- }
-
- public static void setExtractOnStaticLoad(boolean load) {
- extractOnStaticLoad.set(load);
- }
- }
-
- static {
- if (Helper.getExtractOnStaticLoad()) {
- try {
- loader = new RuntimeLoader<>("wpiutiljni", RuntimeLoader.getDefaultExtractionRoot(), WPIUtilJNI.class);
- loader.loadLibrary();
- } catch (IOException ex) {
- ex.printStackTrace();
- System.exit(1);
- }
- libraryLoaded = true;
- }
- }
-
- /**
- * Force load the library.
- */
- public static synchronized void forceLoad() throws IOException {
- if (libraryLoaded) {
- return;
- }
- loader = new RuntimeLoader<>("wpiutiljni", RuntimeLoader.getDefaultExtractionRoot(), WPIUtilJNI.class);
- loader.loadLibrary();
- libraryLoaded = true;
- }
-
- public static native long now();
-
- public static native void addPortForwarder(int port, String remoteHost, int remotePort);
- public static native void removePortForwarder(int port);
-}
diff --git a/wpiutil/src/main/java/edu/wpi/first/wpiutil/net/PortForwarder.java b/wpiutil/src/main/java/edu/wpi/first/wpiutil/net/PortForwarder.java
deleted file mode 100644
index 4fbdbf5..0000000
--- a/wpiutil/src/main/java/edu/wpi/first/wpiutil/net/PortForwarder.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2019 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
-
-package edu.wpi.first.wpiutil.net;
-
-import edu.wpi.first.wpiutil.WPIUtilJNI;
-
-/**
- * Forward ports to another host. This is primarily useful for accessing
- * Ethernet-connected devices from a computer tethered to the RoboRIO USB port.
- */
-public final class PortForwarder {
- private PortForwarder() {
- throw new UnsupportedOperationException("This is a utility class!");
- }
-
- /**
- * Forward a local TCP port to a remote host and port.
- * Note that local ports less than 1024 won't work as a normal user.
- *
- * @param port local port number
- * @param remoteHost remote IP address / DNS name
- * @param remotePort remote port number
- */
- public static void add(int port, String remoteHost, int remotePort) {
- WPIUtilJNI.addPortForwarder(port, remoteHost, remotePort);
- }
-
- /**
- * Stop TCP forwarding on a port.
- *
- * @param port local port number
- */
- public static void remove(int port) {
- WPIUtilJNI.removePortForwarder(port);
- }
-}
diff --git a/wpiutil/src/main/native/cpp/Base64.cpp b/wpiutil/src/main/native/cpp/Base64.cpp
index 35ac76c..8f1f810 100644
--- a/wpiutil/src/main/native/cpp/Base64.cpp
+++ b/wpiutil/src/main/native/cpp/Base64.cpp
@@ -80,13 +80,19 @@
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64};
-size_t Base64Decode(raw_ostream& os, StringRef encoded) {
- const unsigned char* end = encoded.bytes_begin();
- while (pr2six[*end] <= 63 && end != encoded.bytes_end()) ++end;
- size_t nprbytes = end - encoded.bytes_begin();
- if (nprbytes == 0) return 0;
+size_t Base64Decode(raw_ostream& os, std::string_view encoded) {
+ auto bytes_begin = reinterpret_cast<const unsigned char*>(encoded.data());
+ auto bytes_end = bytes_begin + encoded.size();
+ const unsigned char* end = bytes_begin;
+ while (pr2six[*end] <= 63 && end != bytes_end) {
+ ++end;
+ }
+ size_t nprbytes = end - bytes_begin;
+ if (nprbytes == 0) {
+ return 0;
+ }
- const unsigned char* cur = encoded.bytes_begin();
+ const unsigned char* cur = bytes_begin;
while (nprbytes > 4) {
os << static_cast<unsigned char>(pr2six[cur[0]] << 2 | pr2six[cur[1]] >> 4);
@@ -97,17 +103,20 @@
}
// Note: (nprbytes == 1) would be an error, so just ignore that case
- if (nprbytes > 1)
+ if (nprbytes > 1) {
os << static_cast<unsigned char>(pr2six[cur[0]] << 2 | pr2six[cur[1]] >> 4);
- if (nprbytes > 2)
+ }
+ if (nprbytes > 2) {
os << static_cast<unsigned char>(pr2six[cur[1]] << 4 | pr2six[cur[2]] >> 2);
- if (nprbytes > 3)
+ }
+ if (nprbytes > 3) {
os << static_cast<unsigned char>(pr2six[cur[2]] << 6 | pr2six[cur[3]]);
+ }
- return (end - encoded.bytes_begin()) + ((4 - nprbytes) & 3);
+ return (end - bytes_begin) + ((4 - nprbytes) & 3);
}
-size_t Base64Decode(StringRef encoded, std::string* plain) {
+size_t Base64Decode(std::string_view encoded, std::string* plain) {
plain->resize(0);
raw_string_ostream os(*plain);
size_t rv = Base64Decode(os, encoded);
@@ -115,19 +124,35 @@
return rv;
}
-StringRef Base64Decode(StringRef encoded, size_t* num_read,
- SmallVectorImpl<char>& buf) {
+std::string_view Base64Decode(std::string_view encoded, size_t* num_read,
+ SmallVectorImpl<char>& buf) {
buf.clear();
raw_svector_ostream os(buf);
*num_read = Base64Decode(os, encoded);
return os.str();
}
+size_t Base64Decode(std::string_view encoded, std::vector<uint8_t>* plain) {
+ plain->resize(0);
+ raw_uvector_ostream os(*plain);
+ return Base64Decode(os, encoded);
+}
+
+span<uint8_t> Base64Decode(std::string_view encoded, size_t* num_read,
+ SmallVectorImpl<uint8_t>& buf) {
+ buf.clear();
+ raw_usvector_ostream os(buf);
+ *num_read = Base64Decode(os, encoded);
+ return os.array();
+}
+
static const char basis_64[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-void Base64Encode(raw_ostream& os, StringRef plain) {
- if (plain.empty()) return;
+void Base64Encode(raw_ostream& os, std::string_view plain) {
+ if (plain.empty()) {
+ return;
+ }
size_t len = plain.size();
size_t i;
@@ -153,14 +178,35 @@
}
}
-void Base64Encode(StringRef plain, std::string* encoded) {
+void Base64Encode(std::string_view plain, std::string* encoded) {
encoded->resize(0);
raw_string_ostream os(*encoded);
Base64Encode(os, plain);
os.flush();
}
-StringRef Base64Encode(StringRef plain, SmallVectorImpl<char>& buf) {
+std::string_view Base64Encode(std::string_view plain,
+ SmallVectorImpl<char>& buf) {
+ buf.clear();
+ raw_svector_ostream os(buf);
+ Base64Encode(os, plain);
+ return os.str();
+}
+
+void Base64Encode(raw_ostream& os, span<const uint8_t> plain) {
+ Base64Encode(os, std::string_view{reinterpret_cast<const char*>(plain.data()),
+ plain.size()});
+}
+
+void Base64Encode(span<const uint8_t> plain, std::string* encoded) {
+ encoded->resize(0);
+ raw_string_ostream os(*encoded);
+ Base64Encode(os, plain);
+ os.flush();
+}
+
+std::string_view Base64Encode(span<const uint8_t> plain,
+ SmallVectorImpl<char>& buf) {
buf.clear();
raw_svector_ostream os(buf);
Base64Encode(os, plain);
diff --git a/wpiutil/src/main/native/cpp/DsClient.cpp b/wpiutil/src/main/native/cpp/DsClient.cpp
new file mode 100644
index 0000000..455f10c
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/DsClient.cpp
@@ -0,0 +1,107 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/DsClient.h"
+
+#include <fmt/format.h>
+#include <wpi/StringExtras.h>
+#include <wpi/json.h>
+#include <wpi/uv/Tcp.h>
+#include <wpi/uv/Timer.h>
+
+#include "wpi/Logger.h"
+
+using namespace wpi;
+
+static constexpr uv::Timer::Time kReconnectTime{500};
+
+DsClient::DsClient(wpi::uv::Loop& loop, wpi::Logger& logger,
+ const private_init&)
+ : m_logger{logger},
+ m_tcp{uv::Tcp::Create(loop)},
+ m_timer{uv::Timer::Create(loop)} {
+ m_tcp->end.connect([this] {
+ WPI_DEBUG4(m_logger, "{}", "DS connection closed");
+ clearIp();
+ // try to connect again
+ m_tcp->Reuse([this] { m_timer->Start(kReconnectTime); });
+ });
+ m_tcp->data.connect([this](wpi::uv::Buffer buf, size_t len) {
+ HandleIncoming({buf.base, len});
+ });
+ m_timer->timeout.connect([this] { Connect(); });
+ Connect();
+}
+
+DsClient::~DsClient() = default;
+
+void DsClient::Close() {
+ m_tcp->Close();
+ m_timer->Close();
+ clearIp();
+}
+
+void DsClient::Connect() {
+ auto connreq = std::make_shared<uv::TcpConnectReq>();
+ connreq->connected.connect([this] {
+ m_json.clear();
+ m_tcp->StopRead();
+ m_tcp->StartRead();
+ });
+
+ connreq->error = [this](uv::Error err) {
+ WPI_DEBUG4(m_logger, "DS connect failure: {}", err.str());
+ // try to connect again
+ m_tcp->Reuse([this] { m_timer->Start(kReconnectTime); });
+ };
+
+ WPI_DEBUG4(m_logger, "{}", "Starting DS connection attempt");
+ m_tcp->Connect("127.0.0.1", 1742, connreq);
+}
+
+void DsClient::HandleIncoming(std::string_view in) {
+ // this is very bare-bones, as there are never nested {} in these messages
+ while (!in.empty()) {
+ // if json is empty, look for the first { (and discard)
+ if (m_json.empty()) {
+ auto start = in.find('{');
+ in = wpi::slice(in, start, std::string_view::npos);
+ }
+
+ // look for the terminating } (and save)
+ auto end = in.find('}');
+ if (end == std::string_view::npos) {
+ m_json.append(in);
+ return; // nothing left to read
+ }
+
+ // have complete json message
+ ++end;
+ m_json.append(wpi::slice(in, 0, end));
+ in = wpi::slice(in, end, std::string_view::npos);
+ ParseJson();
+ m_json.clear();
+ }
+}
+
+void DsClient::ParseJson() {
+ WPI_DEBUG4(m_logger, "DsClient JSON: {}", m_json);
+ unsigned int ip = 0;
+ try {
+ ip = wpi::json::parse(m_json).at("robotIP").get<unsigned int>();
+ } catch (wpi::json::exception& e) {
+ WPI_INFO(m_logger, "DsClient JSON error: {}", e.what());
+ return;
+ }
+
+ if (ip == 0) {
+ clearIp();
+ } else {
+ // Convert number into dotted quad
+ auto newip = fmt::format("{}.{}.{}.{}", (ip >> 24) & 0xff,
+ (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff);
+ WPI_INFO(m_logger, "DS received server IP: {}", newip);
+ setIp(newip);
+ }
+}
diff --git a/wpiutil/src/main/native/cpp/EventLoopRunner.cpp b/wpiutil/src/main/native/cpp/EventLoopRunner.cpp
index 1c54bdf..c86176e 100644
--- a/wpiutil/src/main/native/cpp/EventLoopRunner.cpp
+++ b/wpiutil/src/main/native/cpp/EventLoopRunner.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/EventLoopRunner.h"
@@ -21,7 +18,9 @@
Thread() : m_loop(uv::Loop::Create()) {
// set up async handles
- if (!m_loop) return;
+ if (!m_loop) {
+ return;
+ }
// run function
m_doExec = UvExecFunc::Create(
@@ -31,8 +30,10 @@
});
}
- void Main() {
- if (m_loop) m_loop->Run();
+ void Main() override {
+ if (m_loop) {
+ m_loop->Run();
+ }
}
// the loop
@@ -42,9 +43,13 @@
std::weak_ptr<UvExecFunc> m_doExec;
};
-EventLoopRunner::EventLoopRunner() { m_owner.Start(); }
+EventLoopRunner::EventLoopRunner() {
+ m_owner.Start();
+}
-EventLoopRunner::~EventLoopRunner() { Stop(); }
+EventLoopRunner::~EventLoopRunner() {
+ Stop();
+}
void EventLoopRunner::Stop() {
ExecAsync([](uv::Loop& loop) {
@@ -60,7 +65,7 @@
void EventLoopRunner::ExecAsync(LoopFunc func) {
if (auto thr = m_owner.GetThread()) {
if (auto doExec = thr->m_doExec.lock()) {
- doExec->Call(func);
+ doExec->Call(std::move(func));
}
}
}
@@ -69,13 +74,17 @@
wpi::future<void> f;
if (auto thr = m_owner.GetThread()) {
if (auto doExec = thr->m_doExec.lock()) {
- f = doExec->Call(func);
+ f = doExec->Call(std::move(func));
}
}
- if (f.valid()) f.wait();
+ if (f.valid()) {
+ f.wait();
+ }
}
std::shared_ptr<uv::Loop> EventLoopRunner::GetLoop() {
- if (auto thr = m_owner.GetThread()) return thr->m_loop;
+ if (auto thr = m_owner.GetThread()) {
+ return thr->m_loop;
+ }
return nullptr;
}
diff --git a/wpiutil/src/main/native/cpp/HttpParser.cpp b/wpiutil/src/main/native/cpp/HttpParser.cpp
index 4560b38..3c18e0f 100644
--- a/wpiutil/src/main/native/cpp/HttpParser.cpp
+++ b/wpiutil/src/main/native/cpp/HttpParser.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/HttpParser.h"
@@ -37,8 +34,10 @@
m_settings.on_url = [](http_parser* p, const char* at, size_t length) -> int {
auto& self = *static_cast<HttpParser*>(p->data);
// append to buffer
- if ((self.m_urlBuf.size() + length) > self.m_maxLength) return 1;
- self.m_urlBuf += StringRef{at, length};
+ if ((self.m_urlBuf.size() + length) > self.m_maxLength) {
+ return 1;
+ }
+ self.m_urlBuf += std::string_view{at, length};
self.m_state = kUrl;
return 0;
};
@@ -48,8 +47,10 @@
size_t length) -> int {
auto& self = *static_cast<HttpParser*>(p->data);
// use valueBuf for the status
- if ((self.m_valueBuf.size() + length) > self.m_maxLength) return 1;
- self.m_valueBuf += StringRef{at, length};
+ if ((self.m_valueBuf.size() + length) > self.m_maxLength) {
+ return 1;
+ }
+ self.m_valueBuf += std::string_view{at, length};
self.m_state = kStatus;
return 0;
};
@@ -62,19 +63,25 @@
// once we're in header, we know the URL is complete
if (self.m_state == kUrl) {
self.url(self.m_urlBuf);
- if (self.m_aborted) return 1;
+ if (self.m_aborted) {
+ return 1;
+ }
}
// once we're in header, we know the status is complete
if (self.m_state == kStatus) {
self.status(self.m_valueBuf);
- if (self.m_aborted) return 1;
+ if (self.m_aborted) {
+ return 1;
+ }
}
// if we previously were in value state, that means we finished a header
if (self.m_state == kValue) {
self.header(self.m_fieldBuf, self.m_valueBuf);
- if (self.m_aborted) return 1;
+ if (self.m_aborted) {
+ return 1;
+ }
}
// clear field and value when we enter this state
@@ -85,8 +92,10 @@
}
// append data to field buffer
- if ((self.m_fieldBuf.size() + length) > self.m_maxLength) return 1;
- self.m_fieldBuf += StringRef{at, length};
+ if ((self.m_fieldBuf.size() + length) > self.m_maxLength) {
+ return 1;
+ }
+ self.m_fieldBuf += std::string_view{at, length};
return 0;
};
@@ -102,8 +111,10 @@
}
// append data to value buffer
- if ((self.m_valueBuf.size() + length) > self.m_maxLength) return 1;
- self.m_valueBuf += StringRef{at, length};
+ if ((self.m_valueBuf.size() + length) > self.m_maxLength) {
+ return 1;
+ }
+ self.m_valueBuf += std::string_view{at, length};
return 0;
};
@@ -114,19 +125,25 @@
// if we previously were in url state, that means we finished the url
if (self.m_state == kUrl) {
self.url(self.m_urlBuf);
- if (self.m_aborted) return 1;
+ if (self.m_aborted) {
+ return 1;
+ }
}
// if we previously were in status state, that means we finished the status
if (self.m_state == kStatus) {
self.status(self.m_valueBuf);
- if (self.m_aborted) return 1;
+ if (self.m_aborted) {
+ return 1;
+ }
}
// if we previously were in value state, that means we finished a header
if (self.m_state == kValue) {
self.header(self.m_fieldBuf, self.m_valueBuf);
- if (self.m_aborted) return 1;
+ if (self.m_aborted) {
+ return 1;
+ }
}
self.headersComplete(self.ShouldKeepAlive());
@@ -137,7 +154,7 @@
m_settings.on_body = [](http_parser* p, const char* at,
size_t length) -> int {
auto& self = *static_cast<HttpParser*>(p->data);
- self.body(StringRef{at, length}, self.IsBodyFinal());
+ self.body(std::string_view{at, length}, self.IsBodyFinal());
return self.m_aborted;
};
diff --git a/wpiutil/src/main/native/cpp/HttpServerConnection.cpp b/wpiutil/src/main/native/cpp/HttpServerConnection.cpp
index 0308e8c..716d2af 100644
--- a/wpiutil/src/main/native/cpp/HttpServerConnection.cpp
+++ b/wpiutil/src/main/native/cpp/HttpServerConnection.cpp
@@ -1,14 +1,15 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/HttpServerConnection.h"
+#include "fmt/format.h"
#include "wpi/SmallString.h"
#include "wpi/SmallVector.h"
+#include "wpi/SpanExtras.h"
+#include "wpi/StringExtras.h"
+#include "wpi/fmt/raw_ostream.h"
#include "wpi/raw_uv_ostream.h"
using namespace wpi;
@@ -24,16 +25,18 @@
// look for Accept-Encoding headers to determine if gzip is acceptable
m_request.messageBegin.connect([this] { m_acceptGzip = false; });
- m_request.header.connect([this](StringRef name, StringRef value) {
- if (name.equals_lower("accept-encoding") && value.contains("gzip")) {
- m_acceptGzip = true;
- }
- });
+ m_request.header.connect(
+ [this](std::string_view name, std::string_view value) {
+ if (wpi::equals_lower(name, "accept-encoding") &&
+ wpi::contains(value, "gzip")) {
+ m_acceptGzip = true;
+ }
+ });
// pass incoming data to HTTP parser
m_dataConn =
stream->data.connect_connection([this](uv::Buffer& buf, size_t size) {
- m_request.Execute(StringRef{buf.base, size});
+ m_request.Execute({buf.base, size});
if (m_request.HasError()) {
// could not parse; just close the connection
m_stream.Close();
@@ -57,37 +60,46 @@
}
void HttpServerConnection::BuildHeader(raw_ostream& os, int code,
- const Twine& codeText,
- const Twine& contentType,
+ std::string_view codeText,
+ std::string_view contentType,
uint64_t contentLength,
- const Twine& extra) {
- os << "HTTP/" << m_request.GetMajor() << '.' << m_request.GetMinor() << ' '
- << code << ' ' << codeText << "\r\n";
- if (contentLength == 0) m_keepAlive = false;
- if (!m_keepAlive) os << "Connection: close\r\n";
+ std::string_view extra) {
+ fmt::print(os, "HTTP/{}.{} {} {}\r\n", m_request.GetMajor(),
+ m_request.GetMinor(), code, codeText);
+ if (contentLength == 0) {
+ m_keepAlive = false;
+ }
+ if (!m_keepAlive) {
+ os << "Connection: close\r\n";
+ }
BuildCommonHeaders(os);
os << "Content-Type: " << contentType << "\r\n";
- if (contentLength != 0) os << "Content-Length: " << contentLength << "\r\n";
+ if (contentLength != 0) {
+ fmt::print(os, "Content-Length: {}\r\n", contentLength);
+ }
os << "Access-Control-Allow-Origin: *\r\nAccess-Control-Allow-Methods: *\r\n";
- SmallString<128> extraBuf;
- StringRef extraStr = extra.toStringRef(extraBuf);
- if (!extraStr.empty()) os << extraStr;
+ if (!extra.empty()) {
+ os << extra;
+ }
os << "\r\n"; // header ends with a blank line
}
-void HttpServerConnection::SendData(ArrayRef<uv::Buffer> bufs,
+void HttpServerConnection::SendData(span<const uv::Buffer> bufs,
bool closeAfter) {
- m_stream.Write(bufs, [closeAfter, stream = &m_stream](
- MutableArrayRef<uv::Buffer> bufs, uv::Error) {
- for (auto&& buf : bufs) buf.Deallocate();
- if (closeAfter) stream->Close();
+ m_stream.Write(bufs, [closeAfter, stream = &m_stream](auto bufs, uv::Error) {
+ for (auto&& buf : bufs) {
+ buf.Deallocate();
+ }
+ if (closeAfter) {
+ stream->Close();
+ }
});
}
-void HttpServerConnection::SendResponse(int code, const Twine& codeText,
- const Twine& contentType,
- StringRef content,
- const Twine& extraHeader) {
+void HttpServerConnection::SendResponse(int code, std::string_view codeText,
+ std::string_view contentType,
+ std::string_view content,
+ std::string_view extraHeader) {
SmallVector<uv::Buffer, 4> toSend;
raw_uv_ostream os{toSend, 4096};
BuildHeader(os, code, codeText, contentType, content.size(), extraHeader);
@@ -96,33 +108,37 @@
SendData(os.bufs(), !m_keepAlive);
}
-void HttpServerConnection::SendStaticResponse(int code, const Twine& codeText,
- const Twine& contentType,
- StringRef content, bool gzipped,
- const Twine& extraHeader) {
+void HttpServerConnection::SendStaticResponse(
+ int code, std::string_view codeText, std::string_view contentType,
+ std::string_view content, bool gzipped, std::string_view extraHeader) {
// TODO: handle remote side not accepting gzip (very rare)
- StringRef contentEncodingHeader;
- if (gzipped /* && m_acceptGzip*/)
+ std::string_view contentEncodingHeader;
+ if (gzipped /* && m_acceptGzip*/) {
contentEncodingHeader = "Content-Encoding: gzip\r\n";
+ }
SmallVector<uv::Buffer, 4> bufs;
raw_uv_ostream os{bufs, 4096};
BuildHeader(os, code, codeText, contentType, content.size(),
- extraHeader + contentEncodingHeader);
+ fmt::format("{}{}", extraHeader, contentEncodingHeader));
// can send content without copying
bufs.emplace_back(content);
m_stream.Write(bufs, [closeAfter = !m_keepAlive, stream = &m_stream](
- MutableArrayRef<uv::Buffer> bufs, uv::Error) {
+ auto bufs, uv::Error) {
// don't deallocate the static content
- for (auto&& buf : bufs.drop_back()) buf.Deallocate();
- if (closeAfter) stream->Close();
+ for (auto&& buf : wpi::drop_back(bufs)) {
+ buf.Deallocate();
+ }
+ if (closeAfter) {
+ stream->Close();
+ }
});
}
-void HttpServerConnection::SendError(int code, const Twine& message) {
- StringRef codeText, extra, baseMessage;
+void HttpServerConnection::SendError(int code, std::string_view message) {
+ std::string_view codeText, extra, baseMessage;
switch (code) {
case 401:
codeText = "Unauthorized";
@@ -155,8 +171,8 @@
baseMessage = "501: Not Implemented!";
break;
}
- SmallString<256> content = baseMessage;
+ SmallString<256> content{baseMessage};
content += "\r\n";
- message.toVector(content);
- SendResponse(code, codeText, "text/plain", content, extra);
+ content += message;
+ SendResponse(code, codeText, "text/plain", content.str(), extra);
}
diff --git a/wpiutil/src/main/native/cpp/HttpUtil.cpp b/wpiutil/src/main/native/cpp/HttpUtil.cpp
index 866ee10..afce3a6 100644
--- a/wpiutil/src/main/native/cpp/HttpUtil.cpp
+++ b/wpiutil/src/main/native/cpp/HttpUtil.cpp
@@ -1,70 +1,64 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2020 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. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/HttpUtil.h"
#include <cctype>
+#include "fmt/format.h"
#include "wpi/Base64.h"
-#include "wpi/STLExtras.h"
#include "wpi/StringExtras.h"
#include "wpi/TCPConnector.h"
#include "wpi/raw_ostream.h"
namespace wpi {
-StringRef UnescapeURI(const Twine& str, SmallVectorImpl<char>& buf,
- bool* error) {
- SmallString<128> strBuf;
- StringRef strStr = str.toStringRef(strBuf);
+std::string_view UnescapeURI(std::string_view str, SmallVectorImpl<char>& buf,
+ bool* error) {
buf.clear();
- for (auto i = strStr.begin(), end = strStr.end(); i != end; ++i) {
+ for (auto i = str.begin(), end = str.end(); i != end; ++i) {
// pass non-escaped characters to output
if (*i != '%') {
// decode + to space
- if (*i == '+')
+ if (*i == '+') {
buf.push_back(' ');
- else
+ } else {
buf.push_back(*i);
+ }
continue;
}
// are there enough characters left?
if (i + 2 >= end) {
*error = true;
- return StringRef{};
+ return {};
}
// replace %xx with the corresponding character
unsigned val1 = hexDigitValue(*++i);
if (val1 == -1U) {
*error = true;
- return StringRef{};
+ return {};
}
unsigned val2 = hexDigitValue(*++i);
if (val2 == -1U) {
*error = true;
- return StringRef{};
+ return {};
}
buf.push_back((val1 << 4) | val2);
}
*error = false;
- return StringRef{buf.data(), buf.size()};
+ return {buf.data(), buf.size()};
}
-StringRef EscapeURI(const Twine& str, SmallVectorImpl<char>& buf,
- bool spacePlus) {
+std::string_view EscapeURI(std::string_view str, SmallVectorImpl<char>& buf,
+ bool spacePlus) {
static const char* const hexLut = "0123456789ABCDEF";
- SmallString<128> strBuf;
- StringRef strStr = str.toStringRef(strBuf);
buf.clear();
- for (auto i = strStr.begin(), end = strStr.end(); i != end; ++i) {
+ for (auto i = str.begin(), end = str.end(); i != end; ++i) {
// pass unreserved characters to output
if (std::isalnum(*i) || *i == '-' || *i == '_' || *i == '.' || *i == '~') {
buf.push_back(*i);
@@ -83,42 +77,48 @@
buf.push_back(hexLut[(*i) & 0x0f]);
}
- return StringRef{buf.data(), buf.size()};
+ return {buf.data(), buf.size()};
}
-HttpQueryMap::HttpQueryMap(wpi::StringRef query) {
- wpi::SmallVector<wpi::StringRef, 16> queryElems;
- query.split(queryElems, '&', 100, false);
+HttpQueryMap::HttpQueryMap(std::string_view query) {
+ SmallVector<std::string_view, 16> queryElems;
+ split(query, queryElems, '&', 100, false);
for (auto elem : queryElems) {
- auto [nameEsc, valueEsc] = elem.split('=');
- wpi::SmallString<64> nameBuf;
+ auto [nameEsc, valueEsc] = split(elem, '=');
+ SmallString<64> nameBuf;
bool err = false;
auto name = wpi::UnescapeURI(nameEsc, nameBuf, &err);
// note: ignores duplicates
- if (!err) m_elems.try_emplace(name, valueEsc);
+ if (!err) {
+ m_elems.try_emplace(name, valueEsc);
+ }
}
}
-std::optional<wpi::StringRef> HttpQueryMap::Get(
- wpi::StringRef name, wpi::SmallVectorImpl<char>& buf) const {
+std::optional<std::string_view> HttpQueryMap::Get(
+ std::string_view name, wpi::SmallVectorImpl<char>& buf) const {
auto it = m_elems.find(name);
- if (it == m_elems.end()) return {};
+ if (it == m_elems.end()) {
+ return {};
+ }
bool err = false;
auto val = wpi::UnescapeURI(it->second, buf, &err);
- if (err) return {};
+ if (err) {
+ return {};
+ }
return val;
}
-HttpPath::HttpPath(wpi::StringRef path) {
+HttpPath::HttpPath(std::string_view path) {
// special-case root path to be a single empty element
if (path == "/") {
m_pathEnds.emplace_back(0);
return;
}
- wpi::SmallVector<wpi::StringRef, 16> pathElems;
- path.split(pathElems, '/', 100, false);
+ wpi::SmallVector<std::string_view, 16> pathElems;
+ split(path, pathElems, '/', 100, false);
for (auto elem : pathElems) {
- wpi::SmallString<64> buf;
+ SmallString<64> buf;
bool err = false;
auto val = wpi::UnescapeURI(elem, buf, &err);
if (err) {
@@ -130,59 +130,78 @@
}
}
-bool HttpPath::startswith(size_t start, ArrayRef<StringRef> match) const {
- if (m_pathEnds.size() < (start + match.size())) return false;
+bool HttpPath::startswith(size_t start,
+ span<const std::string_view> match) const {
+ if (m_pathEnds.size() < (start + match.size())) {
+ return false;
+ }
bool first = start == 0;
auto p = m_pathEnds.begin() + start;
for (auto m : match) {
- auto val = m_pathBuf.slice(first ? 0 : *(p - 1), *p);
- if (val != m) return false;
+ auto val = slice(m_pathBuf, first ? 0 : *(p - 1), *p);
+ if (val != m) {
+ return false;
+ }
first = false;
++p;
}
return true;
}
+std::string_view HttpPath::operator[](size_t n) const {
+ return slice(m_pathBuf, n == 0 ? 0 : m_pathEnds[n - 1], m_pathEnds[n]);
+}
+
bool ParseHttpHeaders(raw_istream& is, SmallVectorImpl<char>* contentType,
SmallVectorImpl<char>* contentLength) {
- if (contentType) contentType->clear();
- if (contentLength) contentLength->clear();
+ if (contentType) {
+ contentType->clear();
+ }
+ if (contentLength) {
+ contentLength->clear();
+ }
bool inContentType = false;
bool inContentLength = false;
SmallString<64> lineBuf;
for (;;) {
- StringRef line = is.getline(lineBuf, 1024).rtrim();
- if (is.has_error()) return false;
- if (line.empty()) return true; // empty line signals end of headers
+ std::string_view line = rtrim(is.getline(lineBuf, 1024));
+ if (is.has_error()) {
+ return false;
+ }
+ if (line.empty()) {
+ return true; // empty line signals end of headers
+ }
// header fields start at the beginning of the line
if (!std::isspace(line[0])) {
inContentType = false;
inContentLength = false;
- StringRef field;
- std::tie(field, line) = line.split(':');
- field = field.rtrim();
- if (field.equals_lower("content-type"))
+ std::string_view field;
+ std::tie(field, line) = split(line, ':');
+ field = rtrim(field);
+ if (equals_lower(field, "content-type")) {
inContentType = true;
- else if (field.equals_lower("content-length"))
+ } else if (equals_lower(field, "content-length")) {
inContentLength = true;
- else
+ } else {
continue; // ignore other fields
+ }
}
// collapse whitespace
- line = line.ltrim();
+ line = ltrim(line);
// save field data
- if (inContentType && contentType)
+ if (inContentType && contentType) {
contentType->append(line.begin(), line.end());
- else if (inContentLength && contentLength)
+ } else if (inContentLength && contentLength) {
contentLength->append(line.begin(), line.end());
+ }
}
}
-bool FindMultipartBoundary(raw_istream& is, StringRef boundary,
+bool FindMultipartBoundary(raw_istream& is, std::string_view boundary,
std::string* saveBuf) {
SmallString<64> searchBuf;
searchBuf.resize(boundary.size() + 2);
@@ -194,7 +213,9 @@
if (!saveBuf) {
do {
is.read(searchBuf.data(), 1);
- if (is.has_error()) return false;
+ if (is.has_error()) {
+ return false;
+ }
} while (searchBuf[0] == '\r' || searchBuf[0] == '\n');
searchPos = 1;
}
@@ -205,19 +226,26 @@
// there's a bunch of continuous -'s in the output, but that's unlikely.
for (;;) {
is.read(searchBuf.data() + searchPos, searchBuf.size() - searchPos);
- if (is.has_error()) return false;
+ if (is.has_error()) {
+ return false;
+ }
// Did we find the boundary?
if (searchBuf[0] == '-' && searchBuf[1] == '-' &&
- searchBuf.substr(2) == boundary)
+ searchBuf.substr(2) == boundary) {
return true;
+ }
// Fast-scan for '-'
size_t pos = searchBuf.find('-', searchBuf[0] == '-' ? 1 : 0);
- if (pos == StringRef::npos) {
- if (saveBuf) saveBuf->append(searchBuf.data(), searchBuf.size());
+ if (pos == std::string_view::npos) {
+ if (saveBuf) {
+ saveBuf->append(searchBuf.data(), searchBuf.size());
+ }
} else {
- if (saveBuf) saveBuf->append(searchBuf.data(), pos);
+ if (saveBuf) {
+ saveBuf->append(searchBuf.data(), pos);
+ }
// move '-' and following to start of buffer (next read will fill)
std::memmove(searchBuf.data(), searchBuf.data() + pos,
@@ -227,63 +255,58 @@
}
}
-HttpLocation::HttpLocation(const Twine& url_, bool* error,
+HttpLocation::HttpLocation(std::string_view url_, bool* error,
std::string* errorMsg)
- : url{url_.str()} {
+ : url{url_} {
// Split apart into components
- StringRef query{url};
+ std::string_view query{url};
// scheme:
- StringRef scheme;
- std::tie(scheme, query) = query.split(':');
- if (!scheme.equals_lower("http")) {
+ std::string_view scheme;
+ std::tie(scheme, query) = split(query, ':');
+ if (!equals_lower(scheme, "http")) {
*errorMsg = "only supports http URLs";
*error = true;
return;
}
// "//"
- if (!query.startswith("//")) {
+ if (!starts_with(query, "//")) {
*errorMsg = "expected http://...";
*error = true;
return;
}
- query = query.drop_front(2);
+ query.remove_prefix(2);
// user:password@host:port/
- StringRef authority;
- std::tie(authority, query) = query.split('/');
+ std::string_view authority;
+ std::tie(authority, query) = split(query, '/');
- StringRef userpass, hostport;
- std::tie(userpass, hostport) = authority.split('@');
+ auto [userpass, hostport] = split(authority, '@');
// split leaves the RHS empty if the split char isn't present...
if (hostport.empty()) {
hostport = userpass;
- userpass = StringRef{};
+ userpass = {};
}
if (!userpass.empty()) {
- StringRef rawUser, rawPassword;
- std::tie(rawUser, rawPassword) = userpass.split(':');
+ auto [rawUser, rawPassword] = split(userpass, ':');
SmallString<64> userBuf, passBuf;
user = UnescapeURI(rawUser, userBuf, error);
if (*error) {
- raw_string_ostream oss(*errorMsg);
- oss << "could not unescape user \"" << rawUser << "\"";
- oss.flush();
+ *errorMsg = fmt::format("could not unescape user \"{}\"", rawUser);
return;
}
password = UnescapeURI(rawPassword, passBuf, error);
if (*error) {
- raw_string_ostream oss(*errorMsg);
- oss << "could not unescape password \"" << rawPassword << "\"";
- oss.flush();
+ *errorMsg =
+ fmt::format("could not unescape password \"{}\"", rawPassword);
return;
}
}
- StringRef portStr;
- std::tie(host, portStr) = hostport.rsplit(':');
+ std::string_view portStr;
+ std::tie(host, portStr) = rsplit(hostport, ':');
if (host.empty()) {
*errorMsg = "host is empty";
*error = true;
@@ -291,44 +314,42 @@
}
if (portStr.empty()) {
port = 80;
- } else if (portStr.getAsInteger(10, port)) {
- raw_string_ostream oss(*errorMsg);
- oss << "port \"" << portStr << "\" is not an integer";
- oss.flush();
+ } else if (auto p = parse_integer<int>(portStr, 10)) {
+ port = p.value();
+ } else {
+ *errorMsg = fmt::format("port \"{}\" is not an integer", portStr);
*error = true;
return;
}
// path?query#fragment
- std::tie(query, fragment) = query.split('#');
- std::tie(path, query) = query.split('?');
+ std::tie(query, fragment) = split(query, '#');
+ std::tie(path, query) = split(query, '?');
// Split query string into parameters
while (!query.empty()) {
// split out next param and value
- StringRef rawParam, rawValue;
- std::tie(rawParam, query) = query.split('&');
- if (rawParam.empty()) continue; // ignore "&&"
- std::tie(rawParam, rawValue) = rawParam.split('=');
+ std::string_view rawParam, rawValue;
+ std::tie(rawParam, query) = split(query, '&');
+ if (rawParam.empty()) {
+ continue; // ignore "&&"
+ }
+ std::tie(rawParam, rawValue) = split(rawParam, '=');
// unescape param
*error = false;
SmallString<64> paramBuf;
- StringRef param = UnescapeURI(rawParam, paramBuf, error);
+ std::string_view param = UnescapeURI(rawParam, paramBuf, error);
if (*error) {
- raw_string_ostream oss(*errorMsg);
- oss << "could not unescape parameter \"" << rawParam << "\"";
- oss.flush();
+ *errorMsg = fmt::format("could not unescape parameter \"{}\"", rawParam);
return;
}
// unescape value
SmallString<64> valueBuf;
- StringRef value = UnescapeURI(rawValue, valueBuf, error);
+ std::string_view value = UnescapeURI(rawValue, valueBuf, error);
if (*error) {
- raw_string_ostream oss(*errorMsg);
- oss << "could not unescape value \"" << rawValue << "\"";
- oss.flush();
+ *errorMsg = fmt::format("could not unescape value \"{}\"", rawValue);
return;
}
@@ -344,7 +365,7 @@
userpass += loc.user;
userpass += ':';
userpass += loc.password;
- Base64Encode(userpass, &auth);
+ Base64Encode(userpass.str(), &auth);
}
}
@@ -353,31 +374,30 @@
// send GET request
os << "GET /" << request.path << " HTTP/1.1\r\n";
os << "Host: " << request.host << "\r\n";
- if (!request.auth.empty())
+ if (!request.auth.empty()) {
os << "Authorization: Basic " << request.auth << "\r\n";
+ }
os << "\r\n";
os.flush();
// read first line of response
SmallString<64> lineBuf;
- StringRef line = is.getline(lineBuf, 1024).rtrim();
+ std::string_view line = rtrim(is.getline(lineBuf, 1024));
if (is.has_error()) {
*warnMsg = "disconnected before response";
return false;
}
// see if we got a HTTP 200 response
- StringRef httpver, code, codeText;
- std::tie(httpver, line) = line.split(' ');
- std::tie(code, codeText) = line.split(' ');
- if (!httpver.startswith("HTTP")) {
+ std::string_view httpver, code, codeText;
+ std::tie(httpver, line) = split(line, ' ');
+ std::tie(code, codeText) = split(line, ' ');
+ if (!starts_with(httpver, "HTTP")) {
*warnMsg = "did not receive HTTP response";
return false;
}
if (code != "200") {
- raw_string_ostream oss(*warnMsg);
- oss << "received " << code << " " << codeText << " response";
- oss.flush();
+ *warnMsg = fmt::format("received {} {} response", code, codeText);
return false;
}
@@ -390,7 +410,7 @@
return true;
}
-void HttpMultipartScanner::SetBoundary(StringRef boundary) {
+void HttpMultipartScanner::SetBoundary(std::string_view boundary) {
m_boundaryWith = "\n--";
m_boundaryWith += boundary;
m_boundaryWithout = "\n";
@@ -406,9 +426,13 @@
m_buf.resize(0);
}
-StringRef HttpMultipartScanner::Execute(StringRef in) {
- if (m_state == kDone) Reset(m_saveSkipped);
- if (m_saveSkipped) m_buf += in;
+std::string_view HttpMultipartScanner::Execute(std::string_view in) {
+ if (m_state == kDone) {
+ Reset(m_saveSkipped);
+ }
+ if (m_saveSkipped) {
+ m_buf += in;
+ }
size_t pos = 0;
if (m_state == kBoundary) {
@@ -449,19 +473,21 @@
}
if (m_state == kPadding) {
- for (char ch : in.drop_front(pos)) {
+ for (char ch : drop_front(in, pos)) {
++pos;
if (ch == '\n') {
// Found the LF; return remaining input buffer (following it)
m_state = kDone;
- if (m_saveSkipped) m_buf.resize(m_buf.size() - in.size() + pos);
- return in.drop_front(pos);
+ if (m_saveSkipped) {
+ m_buf.resize(m_buf.size() - in.size() + pos);
+ }
+ return drop_front(in, pos);
}
}
}
// We consumed the entire input
- return StringRef{};
+ return {};
}
} // namespace wpi
diff --git a/wpiutil/src/main/native/cpp/Logger.cpp b/wpiutil/src/main/native/cpp/Logger.cpp
new file mode 100644
index 0000000..2b8ec6d
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/Logger.cpp
@@ -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.
+
+#include "wpi/Logger.h"
+
+using namespace wpi;
+
+void Logger::DoLog(unsigned int level, const char* file, unsigned int line,
+ const char* msg) {
+ if (!m_func || level < m_min_level) {
+ return;
+ }
+ m_func(level, file, line, msg);
+}
+
+void Logger::LogV(unsigned int level, const char* file, unsigned int line,
+ fmt::string_view format, fmt::format_args args) {
+ if (!m_func || level < m_min_level) {
+ return;
+ }
+ fmt::memory_buffer out;
+ fmt::vformat_to(fmt::appender{out}, format, args);
+ out.push_back('\0');
+ m_func(level, file, line, out.data());
+}
diff --git a/wpiutil/src/main/native/cpp/MimeTypes.cpp b/wpiutil/src/main/native/cpp/MimeTypes.cpp
index 5bf9d29..082dab1 100644
--- a/wpiutil/src/main/native/cpp/MimeTypes.cpp
+++ b/wpiutil/src/main/native/cpp/MimeTypes.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2020 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. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/MimeTypes.h"
@@ -13,7 +10,7 @@
// derived partially from
// https://github.com/DEGoodmanWilson/libmime/blob/stable/0.1.2/mime/mime.cpp
-StringRef MimeTypeFromPath(StringRef path) {
+std::string_view MimeTypeFromPath(std::string_view path) {
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types
static StringMap<const char*> mimeTypes{
// text
@@ -55,11 +52,11 @@
static const char* defaultType = "application/octet-stream";
auto pos = path.find_last_of("/");
- if (pos != StringRef::npos) {
+ if (pos != std::string_view::npos) {
path = path.substr(pos + 1);
}
auto dot_pos = path.find_last_of(".");
- if (dot_pos > 0 && dot_pos != StringRef::npos) {
+ if (dot_pos > 0 && dot_pos != std::string_view::npos) {
auto type = mimeTypes.find(path.substr(dot_pos + 1));
if (type != mimeTypes.end()) {
return type->getValue();
diff --git a/wpiutil/src/main/native/cpp/ParallelTcpConnector.cpp b/wpiutil/src/main/native/cpp/ParallelTcpConnector.cpp
new file mode 100644
index 0000000..5a8394a
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/ParallelTcpConnector.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 "wpi/ParallelTcpConnector.h"
+
+#include <fmt/format.h>
+
+#include "wpi/Logger.h"
+#include "wpi/uv/GetAddrInfo.h"
+#include "wpi/uv/Loop.h"
+#include "wpi/uv/Tcp.h"
+#include "wpi/uv/Timer.h"
+#include "wpi/uv/util.h"
+
+using namespace wpi;
+
+ParallelTcpConnector::ParallelTcpConnector(
+ wpi::uv::Loop& loop, wpi::uv::Timer::Time reconnectRate,
+ wpi::Logger& logger, std::function<void(wpi::uv::Tcp& tcp)> connected,
+ const private_init&)
+ : m_loop{loop},
+ m_logger{logger},
+ m_reconnectRate{reconnectRate},
+ m_connected{std::move(connected)},
+ m_reconnectTimer{uv::Timer::Create(loop)} {
+ m_reconnectTimer->timeout.connect([this] {
+ if (!IsConnected()) {
+ WPI_DEBUG1(m_logger, "{}", "timed out, reconnecting");
+ Connect();
+ }
+ });
+}
+
+ParallelTcpConnector::~ParallelTcpConnector() = default;
+
+void ParallelTcpConnector::Close() {
+ CancelAll();
+ m_reconnectTimer->Close();
+}
+
+void ParallelTcpConnector::SetServers(
+ wpi::span<const std::pair<std::string, unsigned int>> servers) {
+ m_servers.assign(servers.begin(), servers.end());
+ if (!IsConnected()) {
+ Connect();
+ }
+}
+
+void ParallelTcpConnector::Disconnected() {
+ if (m_isConnected) {
+ m_isConnected = false;
+ Connect();
+ }
+}
+
+void ParallelTcpConnector::Succeeded(uv::Tcp& tcp) {
+ if (!m_isConnected) {
+ m_isConnected = true;
+ m_reconnectTimer->Stop();
+ CancelAll(&tcp);
+ }
+}
+
+void ParallelTcpConnector::Connect() {
+ if (IsConnected()) {
+ return;
+ }
+
+ CancelAll();
+ m_reconnectTimer->Start(m_reconnectRate);
+
+ WPI_DEBUG3(m_logger, "{}", "starting new connection attempts");
+
+ // kick off parallel lookups
+ for (auto&& server : m_servers) {
+ auto req = std::make_shared<uv::GetAddrInfoReq>();
+ m_resolvers.emplace_back(req);
+
+ req->resolved.connect(
+ [this, req = req.get()](const addrinfo& addrinfo) {
+ if (IsConnected()) {
+ return;
+ }
+
+ // kick off parallel connection attempts
+ for (auto ai = &addrinfo; ai; ai = ai->ai_next) {
+ auto tcp = uv::Tcp::Create(m_loop);
+ m_attempts.emplace_back(tcp);
+
+ auto connreq = std::make_shared<uv::TcpConnectReq>();
+ connreq->connected.connect(
+ [this, tcp = tcp.get()] {
+ if (m_logger.min_level() <= wpi::WPI_LOG_DEBUG4) {
+ std::string ip;
+ unsigned int port = 0;
+ uv::AddrToName(tcp->GetPeer(), &ip, &port);
+ WPI_DEBUG4(m_logger,
+ "successful connection ({}) to {} port {}",
+ static_cast<void*>(tcp), ip, port);
+ }
+ if (IsConnected()) {
+ tcp->Shutdown([tcp] { tcp->Close(); });
+ return;
+ }
+ if (m_connected) {
+ m_connected(*tcp);
+ }
+ },
+ shared_from_this());
+
+ connreq->error = [selfWeak = weak_from_this(),
+ tcp = tcp.get()](uv::Error err) {
+ if (auto self = selfWeak.lock()) {
+ WPI_DEBUG1(self->m_logger, "connect failure ({}): {}",
+ static_cast<void*>(tcp), err.str());
+ }
+ };
+
+ if (m_logger.min_level() <= wpi::WPI_LOG_DEBUG4) {
+ std::string ip;
+ unsigned int port = 0;
+ uv::AddrToName(*reinterpret_cast<sockaddr_storage*>(ai->ai_addr),
+ &ip, &port);
+ WPI_DEBUG4(
+ m_logger,
+ "Info({}) starting connection attempt ({}) to {} port {}",
+ static_cast<void*>(req), static_cast<void*>(tcp.get()), ip,
+ port);
+ }
+ tcp->Connect(*ai->ai_addr, connreq);
+ }
+ },
+ shared_from_this());
+
+ req->error = [req = req.get(), selfWeak = weak_from_this()](uv::Error err) {
+ if (auto self = selfWeak.lock()) {
+ WPI_DEBUG1(self->m_logger, "GetAddrInfo({}) failure: {}",
+ static_cast<void*>(req), err.str());
+ }
+ };
+
+ WPI_DEBUG4(m_logger, "starting GetAddrInfo({}) for {} port {}",
+ static_cast<void*>(req.get()), server.first, server.second);
+ addrinfo hints;
+ std::memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+ hints.ai_flags = AI_NUMERICSERV | AI_ADDRCONFIG;
+ uv::GetAddrInfo(m_loop, req, server.first, fmt::format("{}", server.second),
+ &hints);
+ }
+}
+
+void ParallelTcpConnector::CancelAll(wpi::uv::Tcp* except) {
+ WPI_DEBUG4(m_logger, "{}", "canceling previous attempts");
+ for (auto&& resolverWeak : m_resolvers) {
+ if (auto resolver = resolverWeak.lock()) {
+ WPI_DEBUG4(m_logger, "canceling GetAddrInfo({})",
+ static_cast<void*>(resolver.get()));
+ resolver->Cancel();
+ }
+ }
+ m_resolvers.clear();
+
+ for (auto&& tcpWeak : m_attempts) {
+ if (auto tcp = tcpWeak.lock()) {
+ if (tcp.get() != except) {
+ WPI_DEBUG4(m_logger, "canceling connection attempt ({})",
+ static_cast<void*>(tcp.get()));
+ tcp->Close();
+ }
+ }
+ }
+ m_attempts.clear();
+}
diff --git a/wpiutil/src/main/native/cpp/PortForwarder.cpp b/wpiutil/src/main/native/cpp/PortForwarder.cpp
index fe54b63..a423d48 100644
--- a/wpiutil/src/main/native/cpp/PortForwarder.cpp
+++ b/wpiutil/src/main/native/cpp/PortForwarder.cpp
@@ -1,16 +1,12 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2019 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/PortForwarder.h"
+#include "fmt/format.h"
#include "wpi/DenseMap.h"
#include "wpi/EventLoopRunner.h"
-#include "wpi/SmallString.h"
-#include "wpi/raw_ostream.h"
#include "wpi/uv/GetAddrInfo.h"
#include "wpi/uv/Tcp.h"
#include "wpi/uv/Timer.h"
@@ -36,16 +32,19 @@
buf2.len = len;
auto out = outWeak.lock();
if (!out) {
+ buf2.Deallocate();
in.Close();
return;
}
- out->Write(buf2, [](auto bufs, uv::Error) {
- for (auto buf : bufs) buf.Deallocate();
+ out->Write({buf2}, [](auto bufs, uv::Error) {
+ for (auto buf : bufs) {
+ buf.Deallocate();
+ }
});
});
}
-void PortForwarder::Add(unsigned int port, const Twine& remoteHost,
+void PortForwarder::Add(unsigned int port, std::string_view remoteHost,
unsigned int remotePort) {
m_impl->runner.ExecSync([&](uv::Loop& loop) {
auto server = uv::Tcp::Create(loop);
@@ -55,10 +54,12 @@
// when we get a connection, accept it
server->connection.connect([serverPtr = server.get(),
- host = remoteHost.str(), remotePort] {
+ host = std::string{remoteHost}, remotePort] {
auto& loop = serverPtr->GetLoopRef();
auto client = serverPtr->Accept();
- if (!client) return;
+ if (!client) {
+ return;
+ }
// close on error
client->error.connect(
@@ -73,20 +74,20 @@
[remotePtr = remote.get(),
clientWeak = std::weak_ptr<uv::Tcp>(client)](uv::Error err) {
remotePtr->Close();
- if (auto client = clientWeak.lock()) client->Close();
+ if (auto client = clientWeak.lock()) {
+ client->Close();
+ }
});
- // convert port to string
- SmallString<16> remotePortStr;
- raw_svector_ostream(remotePortStr) << remotePort;
-
// resolve address
uv::GetAddrInfo(
loop,
[clientWeak = std::weak_ptr<uv::Tcp>(client),
remoteWeak = std::weak_ptr<uv::Tcp>(remote)](const addrinfo& addr) {
auto remote = remoteWeak.lock();
- if (!remote) return;
+ if (!remote) {
+ return;
+ }
// connect to remote address/port
remote->Connect(*addr.ai_addr, [remotePtr = remote.get(),
@@ -101,11 +102,15 @@
// close both when either side closes
client->end.connect([clientPtr = client.get(), remoteWeak] {
clientPtr->Close();
- if (auto remote = remoteWeak.lock()) remote->Close();
+ if (auto remote = remoteWeak.lock()) {
+ remote->Close();
+ }
});
remotePtr->end.connect([remotePtr, clientWeak] {
remotePtr->Close();
- if (auto client = clientWeak.lock()) client->Close();
+ if (auto client = clientWeak.lock()) {
+ client->Close();
+ }
});
// copy bidirectionally
@@ -115,7 +120,7 @@
CopyStream(*remotePtr, clientWeak);
});
},
- host, remotePortStr);
+ host, fmt::to_string(remotePort));
// time out for connection
uv::Timer::SingleShot(loop, uv::Timer::Time{500},
@@ -124,10 +129,12 @@
remoteWeak = std::weak_ptr<uv::Tcp>(remote)] {
if (auto connected = connectedWeak.lock()) {
if (!*connected) {
- if (auto client = clientWeak.lock())
+ if (auto client = clientWeak.lock()) {
client->Close();
- if (auto remote = remoteWeak.lock())
+ }
+ if (auto remote = remoteWeak.lock()) {
remote->Close();
+ }
}
}
});
diff --git a/wpiutil/src/main/native/cpp/SafeThread.cpp b/wpiutil/src/main/native/cpp/SafeThread.cpp
index 3a906bd..bbecc5c 100644
--- a/wpiutil/src/main/native/cpp/SafeThread.cpp
+++ b/wpiutil/src/main/native/cpp/SafeThread.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2015-2019 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/SafeThread.h"
@@ -12,7 +9,9 @@
detail::SafeThreadProxyBase::SafeThreadProxyBase(
std::shared_ptr<SafeThread> thr)
: m_thread(std::move(thr)) {
- if (!m_thread) return;
+ if (!m_thread) {
+ return;
+ }
m_lock = std::unique_lock<wpi::mutex>(m_thread->m_mutex);
if (!m_thread->m_active) {
m_lock.unlock();
@@ -22,15 +21,18 @@
}
detail::SafeThreadOwnerBase::~SafeThreadOwnerBase() {
- if (m_joinAtExit)
+ if (m_joinAtExit) {
Join();
- else
+ } else {
Stop();
+ }
}
void detail::SafeThreadOwnerBase::Start(std::shared_ptr<SafeThread> thr) {
std::scoped_lock lock(m_mutex);
- if (auto thr = m_thread.lock()) return;
+ if (auto thr = m_thread.lock()) {
+ return;
+ }
m_stdThread = std::thread([=] { thr->Main(); });
thr->m_threadId = m_stdThread.get_id();
m_thread = thr;
@@ -43,7 +45,9 @@
thr->m_cond.notify_all();
m_thread.reset();
}
- if (m_stdThread.joinable()) m_stdThread.detach();
+ if (m_stdThread.joinable()) {
+ m_stdThread.detach();
+ }
}
void detail::SafeThreadOwnerBase::Join() {
@@ -62,7 +66,9 @@
void detail::swap(SafeThreadOwnerBase& lhs, SafeThreadOwnerBase& rhs) noexcept {
using std::swap;
- if (&lhs == &rhs) return;
+ if (&lhs == &rhs) {
+ return;
+ }
std::scoped_lock lock(lhs.m_mutex, rhs.m_mutex);
std::swap(lhs.m_stdThread, rhs.m_stdThread);
std::swap(lhs.m_thread, rhs.m_thread);
diff --git a/wpiutil/src/main/native/cpp/SocketError.cpp b/wpiutil/src/main/native/cpp/SocketError.cpp
index 1695846..3f08d1e 100644
--- a/wpiutil/src/main/native/cpp/SocketError.cpp
+++ b/wpiutil/src/main/native/cpp/SocketError.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2015-2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/SocketError.h"
diff --git a/wpiutil/src/main/native/cpp/StringExtras.cpp b/wpiutil/src/main/native/cpp/StringExtras.cpp
new file mode 100644
index 0000000..45d23ea
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/StringExtras.cpp
@@ -0,0 +1,360 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+//===-- StringExtras.cpp - Implement the StringExtras header --------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the StringExtras.h header
+//
+//===----------------------------------------------------------------------===//
+
+#include "wpi/StringExtras.h"
+
+#include <algorithm>
+#include <cstdlib>
+#include <string_view>
+
+#include "wpi/SmallString.h"
+#include "wpi/SmallVector.h"
+
+// strncasecmp() is not available on non-POSIX systems, so define an
+// alternative function here.
+static int ascii_strncasecmp(const char* lhs, const char* rhs,
+ size_t length) noexcept {
+ for (size_t i = 0; i < length; ++i) {
+ unsigned char lhc = wpi::toLower(lhs[i]);
+ unsigned char rhc = wpi::toLower(rhs[i]);
+ if (lhc != rhc) {
+ return lhc < rhc ? -1 : 1;
+ }
+ }
+ return 0;
+}
+
+int wpi::compare_lower(std::string_view lhs, std::string_view rhs) noexcept {
+ if (int Res = ascii_strncasecmp(lhs.data(), rhs.data(),
+ (std::min)(lhs.size(), rhs.size()))) {
+ return Res;
+ }
+ if (lhs.size() == rhs.size()) {
+ return 0;
+ }
+ return lhs.size() < rhs.size() ? -1 : 1;
+}
+
+std::string_view::size_type wpi::find_lower(
+ std::string_view str, char ch, std::string_view::size_type from) noexcept {
+ char lch = toLower(ch);
+ auto s = drop_front(str, from);
+ while (!s.empty()) {
+ if (toLower(s.front()) == lch) {
+ return str.size() - s.size();
+ }
+ s.remove_prefix(1);
+ }
+ return std::string_view::npos;
+}
+
+std::string_view::size_type wpi::find_lower(
+ std::string_view str, std::string_view other,
+ std::string_view::size_type from) noexcept {
+ auto s = str.substr(from);
+ while (s.size() >= other.size()) {
+ if (starts_with_lower(s, other)) {
+ return from;
+ }
+ s.remove_prefix(1);
+ ++from;
+ }
+ return std::string_view::npos;
+}
+
+std::string_view::size_type wpi::rfind_lower(
+ std::string_view str, char ch, std::string_view::size_type from) noexcept {
+ from = (std::min)(from, str.size());
+ auto data = str.data();
+ std::string_view::size_type i = from;
+ while (i != 0) {
+ --i;
+ if (toLower(data[i]) == toLower(ch)) {
+ return i;
+ }
+ }
+ return std::string_view::npos;
+}
+
+std::string_view::size_type wpi::rfind_lower(std::string_view str,
+ std::string_view other) noexcept {
+ std::string_view::size_type n = other.size();
+ if (n > str.size()) {
+ return std::string_view::npos;
+ }
+ for (size_t i = str.size() - n + 1, e = 0; i != e;) {
+ --i;
+ if (equals_lower(str.substr(i, n), other)) {
+ return i;
+ }
+ }
+ return std::string_view::npos;
+}
+
+bool wpi::starts_with_lower(std::string_view str,
+ std::string_view prefix) noexcept {
+ return str.size() >= prefix.size() &&
+ ascii_strncasecmp(str.data(), prefix.data(), prefix.size()) == 0;
+}
+
+bool wpi::ends_with_lower(std::string_view str,
+ std::string_view suffix) noexcept {
+ return str.size() >= suffix.size() &&
+ ascii_strncasecmp(str.data() + str.size() - suffix.size(),
+ suffix.data(), suffix.size()) == 0;
+}
+
+void wpi::split(std::string_view str, SmallVectorImpl<std::string_view>& arr,
+ std::string_view separator, int maxSplit,
+ bool keepEmpty) noexcept {
+ std::string_view s = str;
+
+ // Count down from maxSplit. When maxSplit is -1, this will just split
+ // "forever". This doesn't support splitting more than 2^31 times
+ // intentionally; if we ever want that we can make maxSplit a 64-bit integer
+ // but that seems unlikely to be useful.
+ while (maxSplit-- != 0) {
+ auto idx = s.find(separator);
+ if (idx == std::string_view::npos) {
+ break;
+ }
+
+ // Push this split.
+ if (keepEmpty || idx > 0) {
+ arr.push_back(slice(s, 0, idx));
+ }
+
+ // Jump forward.
+ s = slice(s, idx + separator.size(), std::string_view::npos);
+ }
+
+ // Push the tail.
+ if (keepEmpty || !s.empty()) {
+ arr.push_back(s);
+ }
+}
+
+void wpi::split(std::string_view str, SmallVectorImpl<std::string_view>& arr,
+ char separator, int maxSplit, bool keepEmpty) noexcept {
+ std::string_view s = str;
+
+ // Count down from maxSplit. When maxSplit is -1, this will just split
+ // "forever". This doesn't support splitting more than 2^31 times
+ // intentionally; if we ever want that we can make maxSplit a 64-bit integer
+ // but that seems unlikely to be useful.
+ while (maxSplit-- != 0) {
+ size_t idx = s.find(separator);
+ if (idx == std::string_view::npos) {
+ break;
+ }
+
+ // Push this split.
+ if (keepEmpty || idx > 0) {
+ arr.push_back(slice(s, 0, idx));
+ }
+
+ // Jump forward.
+ s = slice(s, idx + 1, std::string_view::npos);
+ }
+
+ // Push the tail.
+ if (keepEmpty || !s.empty()) {
+ arr.push_back(s);
+ }
+}
+
+static unsigned GetAutoSenseRadix(std::string_view& str) noexcept {
+ if (str.empty()) {
+ return 10;
+ }
+
+ if (wpi::starts_with(str, "0x") || wpi::starts_with(str, "0X")) {
+ str.remove_prefix(2);
+ return 16;
+ }
+
+ if (wpi::starts_with(str, "0b") || wpi::starts_with(str, "0B")) {
+ str.remove_prefix(2);
+ return 2;
+ }
+
+ if (wpi::starts_with(str, "0o")) {
+ str.remove_prefix(2);
+ return 8;
+ }
+
+ if (str[0] == '0' && str.size() > 1 && wpi::isDigit(str[1])) {
+ str.remove_prefix(1);
+ return 8;
+ }
+
+ return 10;
+}
+
+bool wpi::detail::ConsumeUnsignedInteger(
+ std::string_view& str, unsigned radix,
+ unsigned long long& result) noexcept { // NOLINT(runtime/int)
+ // Autosense radix if not specified.
+ if (radix == 0) {
+ radix = GetAutoSenseRadix(str);
+ }
+
+ // Empty strings (after the radix autosense) are invalid.
+ if (str.empty()) {
+ return true;
+ }
+
+ // Parse all the bytes of the string given this radix. Watch for overflow.
+ std::string_view str2 = str;
+ result = 0;
+ while (!str2.empty()) {
+ unsigned charVal;
+ if (str2[0] >= '0' && str2[0] <= '9') {
+ charVal = str2[0] - '0';
+ } else if (str2[0] >= 'a' && str2[0] <= 'z') {
+ charVal = str2[0] - 'a' + 10;
+ } else if (str2[0] >= 'A' && str2[0] <= 'Z') {
+ charVal = str2[0] - 'A' + 10;
+ } else {
+ break;
+ }
+
+ // If the parsed value is larger than the integer radix, we cannot
+ // consume any more characters.
+ if (charVal >= radix) {
+ break;
+ }
+
+ // Add in this character.
+ unsigned long long prevResult = result; // NOLINT(runtime/int)
+ result = result * radix + charVal;
+
+ // Check for overflow by shifting back and seeing if bits were lost.
+ if (result / radix < prevResult) {
+ return true;
+ }
+
+ str2.remove_prefix(1);
+ }
+
+ // We consider the operation a failure if no characters were consumed
+ // successfully.
+ if (str.size() == str2.size()) {
+ return true;
+ }
+
+ str = str2;
+ return false;
+}
+
+bool wpi::detail::ConsumeSignedInteger(
+ std::string_view& str, unsigned radix,
+ long long& result) noexcept { // NOLINT(runtime/int)
+ unsigned long long ullVal; // NOLINT(runtime/int)
+
+ // Handle positive strings first.
+ if (str.empty() || str.front() != '-') {
+ if (wpi::detail::ConsumeUnsignedInteger(str, radix, ullVal) ||
+ // Check for value so large it overflows a signed value.
+ static_cast<long long>(ullVal) < 0) { // NOLINT(runtime/int)
+ return true;
+ }
+ result = ullVal;
+ return false;
+ }
+
+ // Get the positive part of the value.
+ std::string_view str2 = wpi::drop_front(str, 1);
+ if (wpi::detail::ConsumeUnsignedInteger(str2, radix, ullVal) ||
+ // Reject values so large they'd overflow as negative signed, but allow
+ // "-0". This negates the unsigned so that the negative isn't undefined
+ // on signed overflow.
+ static_cast<long long>(-ullVal) > 0) { // NOLINT(runtime/int)
+ return true;
+ }
+
+ str = str2;
+ result = -ullVal;
+ return false;
+}
+
+bool wpi::detail::GetAsUnsignedInteger(
+ std::string_view str, unsigned radix,
+ unsigned long long& result) noexcept { // NOLINT(runtime/int)
+ if (wpi::detail::ConsumeUnsignedInteger(str, radix, result)) {
+ return true;
+ }
+
+ // For getAsUnsignedInteger, we require the whole string to be consumed or
+ // else we consider it a failure.
+ return !str.empty();
+}
+
+bool wpi::detail::GetAsSignedInteger(
+ std::string_view str, unsigned radix,
+ long long& result) noexcept { // NOLINT(runtime/int)
+ if (wpi::detail::ConsumeSignedInteger(str, radix, result)) {
+ return true;
+ }
+
+ // For getAsSignedInteger, we require the whole string to be consumed or else
+ // we consider it a failure.
+ return !str.empty();
+}
+
+template <>
+std::optional<float> wpi::parse_float<float>(std::string_view str) noexcept {
+ if (str.empty()) {
+ return std::nullopt;
+ }
+ wpi::SmallString<32> storage{str};
+ char* end;
+ float val = std::strtof(storage.c_str(), &end);
+ if (*end != '\0') {
+ return std::nullopt;
+ }
+ return val;
+}
+
+template <>
+std::optional<double> wpi::parse_float<double>(std::string_view str) noexcept {
+ if (str.empty()) {
+ return std::nullopt;
+ }
+ wpi::SmallString<32> storage{str};
+ char* end;
+ double val = std::strtod(storage.c_str(), &end);
+ if (*end != '\0') {
+ return std::nullopt;
+ }
+ return val;
+}
+
+template <>
+std::optional<long double> wpi::parse_float<long double>(
+ std::string_view str) noexcept {
+ if (str.empty()) {
+ return std::nullopt;
+ }
+ wpi::SmallString<32> storage{str};
+ char* end;
+ long double val = std::strtold(storage.c_str(), &end);
+ if (*end != '\0') {
+ return std::nullopt;
+ }
+ return val;
+}
diff --git a/wpiutil/src/main/native/cpp/Synchronization.cpp b/wpiutil/src/main/native/cpp/Synchronization.cpp
new file mode 100644
index 0000000..da97897
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/Synchronization.cpp
@@ -0,0 +1,368 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/Synchronization.h"
+
+#include <algorithm>
+#include <cstring>
+#include <mutex>
+
+#include "wpi/DenseMap.h"
+#include "wpi/SmallVector.h"
+#include "wpi/UidVector.h"
+#include "wpi/condition_variable.h"
+#include "wpi/mutex.h"
+
+using namespace wpi;
+
+namespace {
+
+struct State {
+ int signaled{0};
+ bool autoReset{false};
+ wpi::SmallVector<wpi::condition_variable*, 2> waiters;
+};
+
+struct HandleManager {
+ wpi::mutex mutex;
+ wpi::UidVector<int, 8> eventIds;
+ wpi::UidVector<int, 8> semaphoreIds;
+ wpi::DenseMap<WPI_Handle, State> states;
+};
+
+} // namespace
+
+static HandleManager& GetManager() {
+ static HandleManager manager;
+ return manager;
+}
+
+WPI_EventHandle wpi::CreateEvent(bool manualReset, bool initialState) {
+ auto& manager = GetManager();
+ std::scoped_lock lock{manager.mutex};
+
+ auto index = manager.eventIds.emplace_back(0);
+ WPI_EventHandle handle = (kHandleTypeEvent << 24) | (index & 0xffffff);
+
+ // configure state data
+ auto& state = manager.states[handle];
+ state.signaled = initialState ? 1 : 0;
+ state.autoReset = !manualReset;
+
+ return handle;
+}
+
+void wpi::DestroyEvent(WPI_EventHandle handle) {
+ if ((handle >> 24) != kHandleTypeEvent) {
+ return;
+ }
+
+ DestroySignalObject(handle);
+
+ auto& manager = GetManager();
+ std::scoped_lock lock{manager.mutex};
+ manager.eventIds.erase(handle & 0xffffff);
+}
+
+void wpi::SetEvent(WPI_EventHandle handle) {
+ if ((handle >> 24) != kHandleTypeEvent) {
+ return;
+ }
+
+ SetSignalObject(handle);
+}
+
+void wpi::ResetEvent(WPI_EventHandle handle) {
+ if ((handle >> 24) != kHandleTypeEvent) {
+ return;
+ }
+
+ ResetSignalObject(handle);
+}
+
+WPI_SemaphoreHandle wpi::CreateSemaphore(int initialCount, int maximumCount) {
+ auto& manager = GetManager();
+ std::scoped_lock lock{manager.mutex};
+
+ auto index = manager.semaphoreIds.emplace_back(maximumCount);
+ WPI_EventHandle handle = (kHandleTypeSemaphore << 24) | (index & 0xffffff);
+
+ // configure state data
+ auto& state = manager.states[handle];
+ state.signaled = initialCount;
+ state.autoReset = true;
+
+ return handle;
+}
+
+void wpi::DestroySemaphore(WPI_SemaphoreHandle handle) {
+ if ((handle >> 24) != kHandleTypeSemaphore) {
+ return;
+ }
+
+ DestroySignalObject(handle);
+
+ auto& manager = GetManager();
+ std::scoped_lock lock{manager.mutex};
+ manager.eventIds.erase(handle & 0xffffff);
+}
+
+bool wpi::ReleaseSemaphore(WPI_SemaphoreHandle handle, int releaseCount,
+ int* prevCount) {
+ if ((handle >> 24) != kHandleTypeSemaphore) {
+ return false;
+ }
+ if (releaseCount <= 0) {
+ return false;
+ }
+ int index = handle & 0xffffff;
+
+ auto& manager = GetManager();
+ std::scoped_lock lock{manager.mutex};
+ auto it = manager.states.find(handle);
+ if (it == manager.states.end()) {
+ return false;
+ }
+ auto& state = it->second;
+ int maxCount = manager.eventIds[index];
+ if (prevCount) {
+ *prevCount = state.signaled;
+ }
+ if ((maxCount - state.signaled) < releaseCount) {
+ return false;
+ }
+ state.signaled += releaseCount;
+ for (auto& waiter : state.waiters) {
+ waiter->notify_all();
+ }
+ return true;
+}
+
+bool wpi::WaitForObject(WPI_Handle handle) {
+ return WaitForObject(handle, -1, nullptr);
+}
+
+bool wpi::WaitForObject(WPI_Handle handle, double timeout, bool* timedOut) {
+ WPI_Handle signaledValue;
+ auto signaled = WaitForObjects(
+ wpi::span(&handle, 1), wpi::span(&signaledValue, 1), timeout, timedOut);
+ if (signaled.empty()) {
+ return false;
+ }
+ return (signaled[0] & 0x80000000ul) == 0;
+}
+
+wpi::span<WPI_Handle> wpi::WaitForObjects(wpi::span<const WPI_Handle> handles,
+ wpi::span<WPI_Handle> signaled) {
+ return WaitForObjects(handles, signaled, -1, nullptr);
+}
+
+wpi::span<WPI_Handle> wpi::WaitForObjects(wpi::span<const WPI_Handle> handles,
+ wpi::span<WPI_Handle> signaled,
+ double timeout, bool* timedOut) {
+ auto& manager = GetManager();
+ std::unique_lock lock{manager.mutex};
+ wpi::condition_variable cv;
+ bool addedWaiters = false;
+ bool timedOutVal = false;
+ size_t count = 0;
+
+ for (;;) {
+ for (auto handle : handles) {
+ auto it = manager.states.find(handle);
+ if (it == manager.states.end()) {
+ if (count < signaled.size()) {
+ // treat a non-existent handle as signaled, but set the error bit
+ signaled[count++] = handle | 0x80000000ul;
+ }
+ } else {
+ auto& state = it->second;
+ if (state.signaled > 0) {
+ if (count < signaled.size()) {
+ signaled[count++] = handle;
+ }
+ if (state.autoReset) {
+ --state.signaled;
+ if (state.signaled < 0) {
+ state.signaled = 0;
+ }
+ }
+ }
+ }
+ }
+
+ if (timedOutVal || count != 0) {
+ break;
+ }
+
+ if (timeout == 0) {
+ timedOutVal = true;
+ break;
+ }
+
+ if (!addedWaiters) {
+ addedWaiters = true;
+ for (auto handle : handles) {
+ auto& state = manager.states[handle];
+ state.waiters.emplace_back(&cv);
+ }
+ }
+
+ if (timeout < 0) {
+ cv.wait(lock);
+ } else {
+ auto timeoutTime = std::chrono::steady_clock::now() +
+ std::chrono::duration<double>(timeout);
+ if (cv.wait_until(lock, timeoutTime) == std::cv_status::timeout) {
+ timedOutVal = true;
+ }
+ }
+ }
+
+ if (addedWaiters) {
+ for (auto handle : handles) {
+ auto& state = manager.states[handle];
+ auto it = std::find(state.waiters.begin(), state.waiters.end(), &cv);
+ if (it != state.waiters.end()) {
+ state.waiters.erase(it);
+ }
+ }
+ }
+
+ if (timedOut) {
+ *timedOut = timedOutVal;
+ }
+
+ return signaled.subspan(0, count);
+}
+
+void wpi::CreateSignalObject(WPI_Handle handle, bool manualReset,
+ bool initialState) {
+ auto& manager = GetManager();
+ std::scoped_lock lock{manager.mutex};
+ auto& state = manager.states[handle];
+ state.signaled = initialState ? 1 : 0;
+ state.autoReset = !manualReset;
+}
+
+void wpi::SetSignalObject(WPI_Handle handle) {
+ auto& manager = GetManager();
+ std::scoped_lock lock{manager.mutex};
+ auto it = manager.states.find(handle);
+ if (it == manager.states.end()) {
+ return;
+ }
+ auto& state = it->second;
+ state.signaled = 1;
+ for (auto& waiter : state.waiters) {
+ waiter->notify_all();
+ if (state.autoReset) {
+ // expect the first waiter to reset it
+ break;
+ }
+ }
+}
+
+void wpi::ResetSignalObject(WPI_Handle handle) {
+ auto& manager = GetManager();
+ std::scoped_lock lock{manager.mutex};
+ auto it = manager.states.find(handle);
+ if (it != manager.states.end()) {
+ it->second.signaled = 0;
+ }
+}
+
+void wpi::DestroySignalObject(WPI_Handle handle) {
+ auto& manager = GetManager();
+ std::scoped_lock lock{manager.mutex};
+
+ auto it = manager.states.find(handle);
+ if (it != manager.states.end()) {
+ // wake up any waiters
+ for (auto& waiter : it->second.waiters) {
+ waiter->notify_all();
+ }
+ manager.states.erase(it);
+ }
+}
+
+extern "C" {
+
+WPI_EventHandle WPI_CreateEvent(int manual_reset, int initial_state) {
+ return wpi::CreateEvent(manual_reset != 0, initial_state != 0);
+}
+
+void WPI_DestroyEvent(WPI_EventHandle handle) {
+ wpi::DestroyEvent(handle);
+}
+
+void WPI_SetEvent(WPI_EventHandle handle) {
+ wpi::SetEvent(handle);
+}
+
+void WPI_ResetEvent(WPI_EventHandle handle) {
+ wpi::ResetEvent(handle);
+}
+
+WPI_SemaphoreHandle WPI_CreateSemaphore(int initial_count, int maximum_count) {
+ return wpi::CreateSemaphore(initial_count, maximum_count);
+}
+
+void WPI_DestroySemaphore(WPI_SemaphoreHandle handle) {
+ wpi::DestroySemaphore(handle);
+}
+
+int WPI_ReleaseSemaphore(WPI_SemaphoreHandle handle, int release_count,
+ int* prev_count) {
+ return wpi::ReleaseSemaphore(handle, release_count, prev_count);
+}
+
+int WPI_WaitForObject(WPI_Handle handle) {
+ return wpi::WaitForObject(handle);
+}
+
+int WPI_WaitForObjectTimeout(WPI_Handle handle, double timeout,
+ int* timed_out) {
+ bool timedOutBool;
+ int rv = wpi::WaitForObject(handle, timeout, &timedOutBool);
+ *timed_out = timedOutBool ? 1 : 0;
+ return rv;
+}
+
+int WPI_WaitForObjects(const WPI_Handle* handles, int handles_count,
+ WPI_Handle* signaled) {
+ return wpi::WaitForObjects(wpi::span(handles, handles_count),
+ wpi::span(signaled, handles_count))
+ .size();
+}
+
+int WPI_WaitForObjectsTimeout(const WPI_Handle* handles, int handles_count,
+ WPI_Handle* signaled, double timeout,
+ int* timed_out) {
+ bool timedOutBool;
+ auto signaledResult = wpi::WaitForObjects(wpi::span(handles, handles_count),
+ wpi::span(signaled, handles_count),
+ timeout, &timedOutBool);
+ *timed_out = timedOutBool ? 1 : 0;
+ return signaledResult.size();
+}
+
+void WPI_CreateSignalObject(WPI_Handle handle, int manual_reset,
+ int initial_state) {
+ wpi::CreateSignalObject(handle, manual_reset, initial_state);
+}
+
+void WPI_SetSignalObject(WPI_Handle handle) {
+ wpi::SetSignalObject(handle);
+}
+
+void WPI_ResetSignalObject(WPI_Handle handle) {
+ wpi::ResetSignalObject(handle);
+}
+
+void WPI_DestroySignalObject(WPI_Handle handle) {
+ wpi::DestroySignalObject(handle);
+}
+
+} // extern "C"
diff --git a/wpiutil/src/main/native/cpp/TCPAcceptor.cpp b/wpiutil/src/main/native/cpp/TCPAcceptor.cpp
index 90d9496..8d12ac3 100644
--- a/wpiutil/src/main/native/cpp/TCPAcceptor.cpp
+++ b/wpiutil/src/main/native/cpp/TCPAcceptor.cpp
@@ -44,7 +44,7 @@
using namespace wpi;
-TCPAcceptor::TCPAcceptor(int port, const char* address, Logger& logger)
+TCPAcceptor::TCPAcceptor(int port, std::string_view address, Logger& logger)
: m_lsd(0),
m_port(port),
m_address(address),
@@ -73,11 +73,13 @@
}
int TCPAcceptor::start() {
- if (m_listening) return 0;
+ if (m_listening) {
+ return 0;
+ }
m_lsd = socket(PF_INET, SOCK_STREAM, 0);
if (m_lsd < 0) {
- WPI_ERROR(m_logger, "could not create socket");
+ WPI_ERROR(m_logger, "{}", "could not create socket");
return -1;
}
struct sockaddr_in address;
@@ -93,7 +95,7 @@
int res = inet_pton(PF_INET, m_address.c_str(), &(address.sin_addr));
#endif
if (res != 1) {
- WPI_ERROR(m_logger, "could not resolve " << m_address << " address");
+ WPI_ERROR(m_logger, "could not resolve {} address", m_address);
return -1;
}
} else {
@@ -114,15 +116,15 @@
int result = bind(m_lsd, reinterpret_cast<struct sockaddr*>(&address),
sizeof(address));
if (result != 0) {
- WPI_ERROR(m_logger,
- "bind() to port " << m_port << " failed: " << SocketStrerror());
+ WPI_ERROR(m_logger, "bind() to port {} failed: {}", m_port,
+ SocketStrerror());
return result;
}
result = listen(m_lsd, 5);
if (result != 0) {
- WPI_ERROR(m_logger,
- "listen() on port " << m_port << " failed: " << SocketStrerror());
+ WPI_ERROR(m_logger, "listen() on port {} failed: {}", m_port,
+ SocketStrerror());
return result;
}
m_listening = true;
@@ -153,7 +155,8 @@
address.sin_port = htons(m_port);
int result = -1, sd = socket(AF_INET, SOCK_STREAM, 0);
- if (sd < 0) return;
+ if (sd < 0)
+ return;
// Set socket to non-blocking
u_long mode = 1;
@@ -176,7 +179,9 @@
}
std::unique_ptr<NetworkStream> TCPAcceptor::accept() {
- if (!m_listening || m_shutdown) return nullptr;
+ if (!m_listening || m_shutdown) {
+ return nullptr;
+ }
struct sockaddr_in address;
#ifdef _WIN32
@@ -185,11 +190,12 @@
socklen_t len = sizeof(address);
#endif
std::memset(&address, 0, sizeof(address));
- int sd = ::accept(m_lsd, (struct sockaddr*)&address, &len);
+ int sd = ::accept(m_lsd, reinterpret_cast<struct sockaddr*>(&address), &len);
if (sd < 0) {
- if (!m_shutdown)
- WPI_ERROR(m_logger, "accept() on port "
- << m_port << " failed: " << SocketStrerror());
+ if (!m_shutdown) {
+ WPI_ERROR(m_logger, "accept() on port {} failed: {}", m_port,
+ SocketStrerror());
+ }
return nullptr;
}
if (m_shutdown) {
diff --git a/wpiutil/src/main/native/cpp/TCPConnector.cpp b/wpiutil/src/main/native/cpp/TCPConnector.cpp
index 6110133..ed97962 100644
--- a/wpiutil/src/main/native/cpp/TCPConnector.cpp
+++ b/wpiutil/src/main/native/cpp/TCPConnector.cpp
@@ -96,7 +96,7 @@
int res = inet_pton(PF_INET, server, &(address.sin_addr));
#endif
if (res != 1) {
- WPI_ERROR(logger, "could not resolve " << server << " address");
+ WPI_ERROR(logger, "could not resolve {} address", server);
return nullptr;
}
}
@@ -105,12 +105,13 @@
if (timeout == 0) {
int sd = socket(AF_INET, SOCK_STREAM, 0);
if (sd < 0) {
- WPI_ERROR(logger, "could not create socket");
+ WPI_ERROR(logger, "{}", "could not create socket");
return nullptr;
}
- if (::connect(sd, (struct sockaddr*)&address, sizeof(address)) != 0) {
- WPI_ERROR(logger, "connect() to " << server << " port " << port
- << " failed: " << SocketStrerror());
+ if (::connect(sd, reinterpret_cast<struct sockaddr*>(&address),
+ sizeof(address)) != 0) {
+ WPI_ERROR(logger, "connect() to {} port {} failed: {}", server, port,
+ SocketStrerror());
#ifdef _WIN32
closesocket(sd);
#else
@@ -126,7 +127,7 @@
socklen_t len;
int result = -1, valopt, sd = socket(AF_INET, SOCK_STREAM, 0);
if (sd < 0) {
- WPI_ERROR(logger, "could not create socket");
+ WPI_ERROR(logger, "{}", "could not create socket");
return nullptr;
}
@@ -134,25 +135,26 @@
#ifdef _WIN32
u_long mode = 1;
if (ioctlsocket(sd, FIONBIO, &mode) == SOCKET_ERROR)
- WPI_WARNING(logger,
- "could not set socket to non-blocking: " << SocketStrerror());
+ WPI_WARNING(logger, "could not set socket to non-blocking: {}",
+ SocketStrerror());
#else
int arg;
arg = fcntl(sd, F_GETFL, nullptr);
if (arg < 0) {
- WPI_WARNING(logger,
- "could not set socket to non-blocking: " << SocketStrerror());
+ WPI_WARNING(logger, "could not set socket to non-blocking: {}",
+ SocketStrerror());
} else {
arg |= O_NONBLOCK;
- if (fcntl(sd, F_SETFL, arg) < 0)
- WPI_WARNING(logger,
- "could not set socket to non-blocking: " << SocketStrerror());
+ if (fcntl(sd, F_SETFL, arg) < 0) {
+ WPI_WARNING(logger, "could not set socket to non-blocking: {}",
+ SocketStrerror());
+ }
}
#endif
// Connect with time limit
- if ((result = ::connect(sd, (struct sockaddr*)&address, sizeof(address))) <
- 0) {
+ if ((result = ::connect(sd, reinterpret_cast<struct sockaddr*>(&address),
+ sizeof(address))) < 0) {
int my_errno = SocketErrno();
#ifdef _WIN32
if (my_errno == WSAEWOULDBLOCK || my_errno == WSAEINPROGRESS) {
@@ -168,21 +170,18 @@
getsockopt(sd, SOL_SOCKET, SO_ERROR, reinterpret_cast<char*>(&valopt),
&len);
if (valopt) {
- WPI_ERROR(logger, "select() to " << server << " port " << port
- << " error " << valopt << " - "
- << SocketStrerror(valopt));
- }
- // connection established
- else
+ WPI_ERROR(logger, "select() to {} port {} error {} - {}", server,
+ port, valopt, SocketStrerror(valopt));
+ } else {
+ // connection established
result = 0;
+ }
} else {
- WPI_INFO(logger,
- "connect() to " << server << " port " << port << " timed out");
+ WPI_INFO(logger, "connect() to {} port {} timed out", server, port);
}
} else {
- WPI_ERROR(logger, "connect() to " << server << " port " << port
- << " error " << SocketErrno() << " - "
- << SocketStrerror());
+ WPI_ERROR(logger, "connect() to {} port {} error {} - {}", server, port,
+ SocketErrno(), SocketStrerror());
}
}
@@ -190,18 +189,19 @@
#ifdef _WIN32
mode = 0;
if (ioctlsocket(sd, FIONBIO, &mode) == SOCKET_ERROR)
- WPI_WARNING(logger,
- "could not set socket to blocking: " << SocketStrerror());
+ WPI_WARNING(logger, "could not set socket to blocking: {}",
+ SocketStrerror());
#else
arg = fcntl(sd, F_GETFL, nullptr);
if (arg < 0) {
- WPI_WARNING(logger,
- "could not set socket to blocking: " << SocketStrerror());
+ WPI_WARNING(logger, "could not set socket to blocking: {}",
+ SocketStrerror());
} else {
arg &= (~O_NONBLOCK);
- if (fcntl(sd, F_SETFL, arg) < 0)
- WPI_WARNING(logger,
- "could not set socket to blocking: " << SocketStrerror());
+ if (fcntl(sd, F_SETFL, arg) < 0) {
+ WPI_WARNING(logger, "could not set socket to blocking: {}",
+ SocketStrerror());
+ }
}
#endif
diff --git a/wpiutil/src/main/native/cpp/TCPConnector_parallel.cpp b/wpiutil/src/main/native/cpp/TCPConnector_parallel.cpp
index ee5803f..26258cf 100644
--- a/wpiutil/src/main/native/cpp/TCPConnector_parallel.cpp
+++ b/wpiutil/src/main/native/cpp/TCPConnector_parallel.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2017-2020 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. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/TCPConnector.h" // NOLINT(build/include_order)
@@ -27,9 +24,11 @@
#endif
std::unique_ptr<NetworkStream> TCPConnector::connect_parallel(
- ArrayRef<std::pair<const char*, int>> servers, Logger& logger,
+ span<const std::pair<const char*, int>> servers, Logger& logger,
int timeout) {
- if (servers.empty()) return nullptr;
+ if (servers.empty()) {
+ return nullptr;
+ }
// structure to make sure we don't start duplicate workers
struct GlobalState {
@@ -75,13 +74,15 @@
// attempt to the same server
{
std::scoped_lock lock(local->mtx);
- if (local->active.count(active_tracker) > 0) continue; // already in set
+ if (local->active.count(active_tracker) > 0) {
+ continue; // already in set
+ }
}
++num_workers;
// start the worker
- std::thread([=]() {
+ std::thread([=] {
if (!result->done) {
// add to global state
{
@@ -102,7 +103,9 @@
// successful connection
if (stream) {
std::scoped_lock lock(result->mtx);
- if (!result->done.exchange(true)) result->stream = std::move(stream);
+ if (!result->done.exchange(true)) {
+ result->stream = std::move(stream);
+ }
}
}
++result->count;
diff --git a/wpiutil/src/main/native/cpp/TCPStream.cpp b/wpiutil/src/main/native/cpp/TCPStream.cpp
index 4af3e6f..4567161 100644
--- a/wpiutil/src/main/native/cpp/TCPStream.cpp
+++ b/wpiutil/src/main/native/cpp/TCPStream.cpp
@@ -57,7 +57,9 @@
m_peerPort = ntohs(address->sin_port);
}
-TCPStream::~TCPStream() { close(); }
+TCPStream::~TCPStream() {
+ close();
+}
size_t TCPStream::send(const char* buffer, size_t len, Error* err) {
if (m_sd < 0) {
@@ -101,10 +103,11 @@
ssize_t rv = ::send(m_sd, buffer, len, 0);
#endif
if (rv < 0) {
- if (!m_blocking && (errno == EAGAIN || errno == EWOULDBLOCK))
+ if (!m_blocking && (errno == EAGAIN || errno == EWOULDBLOCK)) {
*err = kWouldBlock;
- else
+ } else {
*err = kConnectionReset;
+ }
return 0;
}
#endif
@@ -139,13 +142,14 @@
}
if (rv < 0) {
#ifdef _WIN32
- if (!m_blocking && WSAGetLastError() == WSAEWOULDBLOCK)
+ if (!m_blocking && WSAGetLastError() == WSAEWOULDBLOCK) {
#else
- if (!m_blocking && (errno == EAGAIN || errno == EWOULDBLOCK))
+ if (!m_blocking && (errno == EAGAIN || errno == EWOULDBLOCK)) {
#endif
*err = kWouldBlock;
- else
+ } else {
*err = kConnectionReset;
+ }
return 0;
}
return static_cast<size_t>(rv);
@@ -164,35 +168,52 @@
m_sd = -1;
}
-StringRef TCPStream::getPeerIP() const { return m_peerIP; }
+std::string_view TCPStream::getPeerIP() const {
+ return m_peerIP;
+}
-int TCPStream::getPeerPort() const { return m_peerPort; }
+int TCPStream::getPeerPort() const {
+ return m_peerPort;
+}
void TCPStream::setNoDelay() {
- if (m_sd < 0) return;
+ if (m_sd < 0) {
+ return;
+ }
int optval = 1;
setsockopt(m_sd, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char*>(&optval),
sizeof optval);
}
bool TCPStream::setBlocking(bool enabled) {
- if (m_sd < 0) return true; // silently accept
+ if (m_sd < 0) {
+ return true; // silently accept
+ }
#ifdef _WIN32
u_long mode = enabled ? 0 : 1;
- if (ioctlsocket(m_sd, FIONBIO, &mode) == SOCKET_ERROR) return false;
+ if (ioctlsocket(m_sd, FIONBIO, &mode) == SOCKET_ERROR) {
+ return false;
+ }
#else
int flags = fcntl(m_sd, F_GETFL, nullptr);
- if (flags < 0) return false;
- if (enabled)
+ if (flags < 0) {
+ return false;
+ }
+ if (enabled) {
flags &= ~O_NONBLOCK;
- else
+ } else {
flags |= O_NONBLOCK;
- if (fcntl(m_sd, F_SETFL, flags) < 0) return false;
+ }
+ if (fcntl(m_sd, F_SETFL, flags) < 0) {
+ return false;
+ }
#endif
return true;
}
-int TCPStream::getNativeHandle() const { return m_sd; }
+int TCPStream::getNativeHandle() const {
+ return m_sd;
+}
bool TCPStream::WaitForReadEvent(int timeout) {
fd_set sdset;
@@ -202,7 +223,7 @@
tv.tv_usec = 0;
FD_ZERO(&sdset);
FD_SET(m_sd, &sdset);
- if (select(m_sd + 1, &sdset, NULL, NULL, &tv) > 0) {
+ if (select(m_sd + 1, &sdset, nullptr, nullptr, &tv) > 0) {
return true;
}
return false;
diff --git a/wpiutil/src/main/native/cpp/UDPClient.cpp b/wpiutil/src/main/native/cpp/UDPClient.cpp
index aafdf47..108ef54 100644
--- a/wpiutil/src/main/native/cpp/UDPClient.cpp
+++ b/wpiutil/src/main/native/cpp/UDPClient.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* 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. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/UDPClient.h"
@@ -19,14 +16,15 @@
#endif
#include "wpi/Logger.h"
+#include "wpi/SmallString.h"
#include "wpi/SocketError.h"
using namespace wpi;
UDPClient::UDPClient(Logger& logger) : UDPClient("", logger) {}
-UDPClient::UDPClient(const Twine& address, Logger& logger)
- : m_lsd(0), m_port(0), m_address(address.str()), m_logger(logger) {}
+UDPClient::UDPClient(std::string_view address, Logger& logger)
+ : m_lsd(0), m_port(0), m_address(address), m_logger(logger) {}
UDPClient::UDPClient(UDPClient&& other)
: m_lsd(other.m_lsd),
@@ -44,7 +42,9 @@
}
UDPClient& UDPClient::operator=(UDPClient&& other) {
- if (this == &other) return *this;
+ if (this == &other) {
+ return *this;
+ }
shutdown();
m_logger = other.m_logger;
m_lsd = other.m_lsd;
@@ -55,10 +55,14 @@
return *this;
}
-int UDPClient::start() { return start(0); }
+int UDPClient::start() {
+ return start(0);
+}
int UDPClient::start(int port) {
- if (m_lsd > 0) return 0;
+ if (m_lsd > 0) {
+ return 0;
+ }
#ifdef _WIN32
WSAData wsaData;
@@ -69,7 +73,7 @@
m_lsd = socket(AF_INET, SOCK_DGRAM, 0);
if (m_lsd < 0) {
- WPI_ERROR(m_logger, "could not create socket");
+ WPI_ERROR(m_logger, "{}", "could not create socket");
return -1;
}
@@ -85,7 +89,7 @@
int res = inet_pton(PF_INET, m_address.c_str(), &(addr.sin_addr));
#endif
if (res != 1) {
- WPI_ERROR(m_logger, "could not resolve " << m_address << " address");
+ WPI_ERROR(m_logger, "could not resolve {} address", m_address);
return -1;
}
} else {
@@ -107,7 +111,7 @@
int result = bind(m_lsd, reinterpret_cast<sockaddr*>(&addr), sizeof(addr));
if (result != 0) {
- WPI_ERROR(m_logger, "bind() failed: " << SocketStrerror());
+ WPI_ERROR(m_logger, "bind() failed: {}", SocketStrerror());
return result;
}
m_port = port;
@@ -129,25 +133,25 @@
}
}
-int UDPClient::send(ArrayRef<uint8_t> data, const Twine& server, int port) {
+int UDPClient::send(span<const uint8_t> data, std::string_view server,
+ int port) {
// server must be a resolvable IP address
struct sockaddr_in addr;
std::memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
- SmallVector<char, 128> addr_store;
- StringRef remoteAddr = server.toNullTerminatedStringRef(addr_store);
+ SmallString<128> remoteAddr{server};
if (remoteAddr.empty()) {
- WPI_ERROR(m_logger, "server must be passed");
+ WPI_ERROR(m_logger, "{}", "server must be passed");
return -1;
}
#ifdef _WIN32
- int res = InetPton(AF_INET, remoteAddr.data(), &(addr.sin_addr));
+ int res = InetPton(AF_INET, remoteAddr.c_str(), &(addr.sin_addr));
#else
- int res = inet_pton(AF_INET, remoteAddr.data(), &(addr.sin_addr));
+ int res = inet_pton(AF_INET, remoteAddr.c_str(), &(addr.sin_addr));
#endif
if (res != 1) {
- WPI_ERROR(m_logger, "could not resolve " << server << " address");
+ WPI_ERROR(m_logger, "could not resolve {} address", server);
return -1;
}
addr.sin_port = htons(port);
@@ -159,25 +163,24 @@
return result;
}
-int UDPClient::send(StringRef data, const Twine& server, int port) {
+int UDPClient::send(std::string_view data, std::string_view server, int port) {
// server must be a resolvable IP address
struct sockaddr_in addr;
std::memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
- SmallVector<char, 128> addr_store;
- StringRef remoteAddr = server.toNullTerminatedStringRef(addr_store);
+ SmallString<128> remoteAddr{server};
if (remoteAddr.empty()) {
- WPI_ERROR(m_logger, "server must be passed");
+ WPI_ERROR(m_logger, "{}", "server must be passed");
return -1;
}
#ifdef _WIN32
- int res = InetPton(AF_INET, remoteAddr.data(), &(addr.sin_addr));
+ int res = InetPton(AF_INET, remoteAddr.c_str(), &(addr.sin_addr));
#else
- int res = inet_pton(AF_INET, remoteAddr.data(), &(addr.sin_addr));
+ int res = inet_pton(AF_INET, remoteAddr.c_str(), &(addr.sin_addr));
#endif
if (res != 1) {
- WPI_ERROR(m_logger, "could not resolve " << server << " address");
+ WPI_ERROR(m_logger, "could not resolve {} address", server);
return -1;
}
addr.sin_port = htons(port);
@@ -189,14 +192,18 @@
}
int UDPClient::receive(uint8_t* data_received, int receive_len) {
- if (m_port == 0) return -1; // return if not receiving
+ if (m_port == 0) {
+ return -1; // return if not receiving
+ }
return recv(m_lsd, reinterpret_cast<char*>(data_received), receive_len, 0);
}
int UDPClient::receive(uint8_t* data_received, int receive_len,
SmallVectorImpl<char>* addr_received,
int* port_received) {
- if (m_port == 0) return -1; // return if not receiving
+ if (m_port == 0) {
+ return -1; // return if not receiving
+ }
struct sockaddr_in remote;
socklen_t remote_len = sizeof(remote);
@@ -225,13 +232,17 @@
}
int UDPClient::set_timeout(double timeout) {
- if (timeout < 0) return -1;
+ if (timeout < 0) {
+ return -1;
+ }
struct timeval tv;
tv.tv_sec = timeout; // truncating will give seconds
timeout -= tv.tv_sec; // remove seconds portion
tv.tv_usec = timeout * 1000000; // fractions of a second to us
int ret = setsockopt(m_lsd, SOL_SOCKET, SO_RCVTIMEO,
reinterpret_cast<char*>(&tv), sizeof(tv));
- if (ret < 0) WPI_ERROR(m_logger, "set timeout failed");
+ if (ret < 0) {
+ WPI_ERROR(m_logger, "{}", "set timeout failed");
+ }
return ret;
}
diff --git a/wpiutil/src/main/native/cpp/WebSocket.cpp b/wpiutil/src/main/native/cpp/WebSocket.cpp
index 82c83bb..4bb49d3 100644
--- a/wpiutil/src/main/native/cpp/WebSocket.cpp
+++ b/wpiutil/src/main/native/cpp/WebSocket.cpp
@@ -1,18 +1,17 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/WebSocket.h"
#include <random>
+#include "fmt/format.h"
#include "wpi/Base64.h"
#include "wpi/HttpParser.h"
#include "wpi/SmallString.h"
#include "wpi/SmallVector.h"
+#include "wpi/StringExtras.h"
#include "wpi/raw_uv_ostream.h"
#include "wpi/sha1.h"
#include "wpi/uv/Stream.h"
@@ -23,14 +22,18 @@
class WebSocketWriteReq : public uv::WriteReq {
public:
explicit WebSocketWriteReq(
- std::function<void(MutableArrayRef<uv::Buffer>, uv::Error)> callback) {
- finish.connect([=](uv::Error err) {
- MutableArrayRef<uv::Buffer> bufs{m_bufs};
- for (auto&& buf : bufs.slice(0, m_startUser)) buf.Deallocate();
- callback(bufs.slice(m_startUser), err);
+ std::function<void(span<uv::Buffer>, uv::Error)> callback)
+ : m_callback{std::move(callback)} {
+ finish.connect([this](uv::Error err) {
+ span<uv::Buffer> bufs{m_bufs};
+ for (auto&& buf : bufs.subspan(0, m_startUser)) {
+ buf.Deallocate();
+ }
+ m_callback(bufs.subspan(m_startUser), err);
});
}
+ std::function<void(span<uv::Buffer>, uv::Error)> m_callback;
SmallVector<uv::Buffer, 4> m_bufs;
size_t m_startUser;
};
@@ -44,9 +47,11 @@
static std::default_random_engine gen{rd()};
std::uniform_int_distribution<unsigned int> dist(0, 255);
char nonce[16]; // the nonce sent to the server
- for (char& v : nonce) v = static_cast<char>(dist(gen));
+ for (char& v : nonce) {
+ v = static_cast<char>(dist(gen));
+ }
raw_svector_ostream os(key);
- Base64Encode(os, StringRef{nonce, 16});
+ Base64Encode(os, {nonce, 16});
}
~ClientHandshakeData() {
if (auto t = timer.lock()) {
@@ -66,7 +71,8 @@
std::weak_ptr<uv::Timer> timer;
};
-static StringRef AcceptHash(StringRef key, SmallVectorImpl<char>& buf) {
+static std::string_view AcceptHash(std::string_view key,
+ SmallVectorImpl<char>& buf) {
SHA1 hash;
hash.Update(key);
hash.Update("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
@@ -79,7 +85,7 @@
// Connect closed and error signals to ourselves
m_stream.closed.connect([this]() { SetClosed(1006, "handle closed"); });
m_stream.error.connect([this](uv::Error err) {
- Terminate(1006, "stream error: " + Twine(err.name()));
+ Terminate(1006, fmt::format("stream error: {}", err.name()));
});
// Start reading
@@ -91,11 +97,11 @@
[this]() { Terminate(1006, "remote end closed connection"); });
}
-WebSocket::~WebSocket() {}
+WebSocket::~WebSocket() = default;
std::shared_ptr<WebSocket> WebSocket::CreateClient(
- uv::Stream& stream, const Twine& uri, const Twine& host,
- ArrayRef<StringRef> protocols, const ClientOptions& options) {
+ uv::Stream& stream, std::string_view uri, std::string_view host,
+ span<const std::string_view> protocols, const ClientOptions& options) {
auto ws = std::make_shared<WebSocket>(stream, false, private_init{});
stream.SetData(ws);
ws->StartClient(uri, host, protocols, options);
@@ -103,35 +109,41 @@
}
std::shared_ptr<WebSocket> WebSocket::CreateServer(uv::Stream& stream,
- StringRef key,
- StringRef version,
- StringRef protocol) {
+ std::string_view key,
+ std::string_view version,
+ std::string_view protocol) {
auto ws = std::make_shared<WebSocket>(stream, true, private_init{});
stream.SetData(ws);
ws->StartServer(key, version, protocol);
return ws;
}
-void WebSocket::Close(uint16_t code, const Twine& reason) {
+void WebSocket::Close(uint16_t code, std::string_view reason) {
SendClose(code, reason);
- if (m_state != FAILED && m_state != CLOSED) m_state = CLOSING;
+ if (m_state != FAILED && m_state != CLOSED) {
+ m_state = CLOSING;
+ }
}
-void WebSocket::Fail(uint16_t code, const Twine& reason) {
- if (m_state == FAILED || m_state == CLOSED) return;
+void WebSocket::Fail(uint16_t code, std::string_view reason) {
+ if (m_state == FAILED || m_state == CLOSED) {
+ return;
+ }
SendClose(code, reason);
SetClosed(code, reason, true);
Shutdown();
}
-void WebSocket::Terminate(uint16_t code, const Twine& reason) {
- if (m_state == FAILED || m_state == CLOSED) return;
+void WebSocket::Terminate(uint16_t code, std::string_view reason) {
+ if (m_state == FAILED || m_state == CLOSED) {
+ return;
+ }
SetClosed(code, reason);
Shutdown();
}
-void WebSocket::StartClient(const Twine& uri, const Twine& host,
- ArrayRef<StringRef> protocols,
+void WebSocket::StartClient(std::string_view uri, std::string_view host,
+ span<const std::string_view> protocols,
const ClientOptions& options) {
// Create client handshake data
m_clientHandshake = std::make_unique<ClientHandshakeData>();
@@ -152,10 +164,11 @@
os << "Sec-WebSocket-Protocol: ";
bool first = true;
for (auto protocol : protocols) {
- if (!first)
+ if (!first) {
os << ", ";
- else
+ } else {
first = false;
+ }
os << protocol;
// also save for later checking against server response
m_clientHandshake->protocols.emplace_back(protocol);
@@ -164,52 +177,64 @@
}
// other headers
- for (auto&& header : options.extraHeaders)
+ for (auto&& header : options.extraHeaders) {
os << header.first << ": " << header.second << "\r\n";
+ }
// finish headers
os << "\r\n";
// Send client request
m_stream.Write(bufs, [](auto bufs, uv::Error) {
- for (auto& buf : bufs) buf.Deallocate();
+ for (auto& buf : bufs) {
+ buf.Deallocate();
+ }
});
// Set up client response handling
- m_clientHandshake->parser.status.connect([this](StringRef status) {
+ m_clientHandshake->parser.status.connect([this](std::string_view status) {
unsigned int code = m_clientHandshake->parser.GetStatusCode();
- if (code != 101) Terminate(code, status);
+ if (code != 101) {
+ Terminate(code, status);
+ }
});
m_clientHandshake->parser.header.connect(
- [this](StringRef name, StringRef value) {
- value = value.trim();
- if (name.equals_lower("upgrade")) {
- if (!value.equals_lower("websocket"))
+ [this](std::string_view name, std::string_view value) {
+ value = trim(value);
+ if (equals_lower(name, "upgrade")) {
+ if (!equals_lower(value, "websocket")) {
return Terminate(1002, "invalid upgrade response value");
+ }
m_clientHandshake->hasUpgrade = true;
- } else if (name.equals_lower("connection")) {
- if (!value.equals_lower("upgrade"))
+ } else if (equals_lower(name, "connection")) {
+ if (!equals_lower(value, "upgrade")) {
return Terminate(1002, "invalid connection response value");
+ }
m_clientHandshake->hasConnection = true;
- } else if (name.equals_lower("sec-websocket-accept")) {
+ } else if (equals_lower(name, "sec-websocket-accept")) {
// Check against expected response
SmallString<64> acceptBuf;
- if (!value.equals(AcceptHash(m_clientHandshake->key, acceptBuf)))
+ if (!equals(value, AcceptHash(m_clientHandshake->key, acceptBuf))) {
return Terminate(1002, "invalid accept key");
+ }
m_clientHandshake->hasAccept = true;
- } else if (name.equals_lower("sec-websocket-extensions")) {
+ } else if (equals_lower(name, "sec-websocket-extensions")) {
// No extensions are supported
- if (!value.empty()) return Terminate(1010, "unsupported extension");
- } else if (name.equals_lower("sec-websocket-protocol")) {
+ if (!value.empty()) {
+ return Terminate(1010, "unsupported extension");
+ }
+ } else if (equals_lower(name, "sec-websocket-protocol")) {
// Make sure it was one of the provided protocols
bool match = false;
for (auto&& protocol : m_clientHandshake->protocols) {
- if (value.equals_lower(protocol)) {
+ if (equals_lower(value, protocol)) {
match = true;
break;
}
}
- if (!match) return Terminate(1003, "unsupported protocol");
+ if (!match) {
+ return Terminate(1003, "unsupported protocol");
+ }
m_clientHandshake->hasProtocol = true;
m_protocol = value;
}
@@ -237,8 +262,8 @@
}
}
-void WebSocket::StartServer(StringRef key, StringRef version,
- StringRef protocol) {
+void WebSocket::StartServer(std::string_view key, std::string_view version,
+ std::string_view protocol) {
m_protocol = protocol;
// Build server response
@@ -251,7 +276,9 @@
os << "Upgrade: WebSocket\r\n";
os << "Sec-WebSocket-Version: 13\r\n\r\n";
m_stream.Write(bufs, [this](auto bufs, uv::Error) {
- for (auto& buf : bufs) buf.Deallocate();
+ for (auto& buf : bufs) {
+ buf.Deallocate();
+ }
// XXX: Should we support sending a new handshake on the same connection?
// XXX: "this->" is required by GCC 5.5 (bug)
this->Terminate(1003, "unsupported protocol version");
@@ -267,14 +294,18 @@
SmallString<64> acceptBuf;
os << "Sec-WebSocket-Accept: " << AcceptHash(key, acceptBuf) << "\r\n";
- if (!protocol.empty()) os << "Sec-WebSocket-Protocol: " << protocol << "\r\n";
+ if (!protocol.empty()) {
+ os << "Sec-WebSocket-Protocol: " << protocol << "\r\n";
+ }
// end headers
os << "\r\n";
// Send server response
m_stream.Write(bufs, [this](auto bufs, uv::Error) {
- for (auto& buf : bufs) buf.Deallocate();
+ for (auto& buf : bufs) {
+ buf.Deallocate();
+ }
if (m_state == CONNECTING) {
m_state = OPEN;
open(m_protocol);
@@ -282,25 +313,28 @@
});
}
-void WebSocket::SendClose(uint16_t code, const Twine& reason) {
+void WebSocket::SendClose(uint16_t code, std::string_view reason) {
SmallVector<uv::Buffer, 4> bufs;
if (code != 1005) {
raw_uv_ostream os{bufs, 4096};
const uint8_t codeMsb[] = {static_cast<uint8_t>((code >> 8) & 0xff),
static_cast<uint8_t>(code & 0xff)};
- os << ArrayRef<uint8_t>(codeMsb);
- reason.print(os);
+ os << span{codeMsb};
+ os << reason;
}
Send(kFlagFin | kOpClose, bufs, [](auto bufs, uv::Error) {
- for (auto&& buf : bufs) buf.Deallocate();
+ for (auto&& buf : bufs) {
+ buf.Deallocate();
+ }
});
}
-void WebSocket::SetClosed(uint16_t code, const Twine& reason, bool failed) {
- if (m_state == FAILED || m_state == CLOSED) return;
+void WebSocket::SetClosed(uint16_t code, std::string_view reason, bool failed) {
+ if (m_state == FAILED || m_state == CLOSED) {
+ return;
+ }
m_state = failed ? FAILED : CLOSED;
- SmallString<64> reasonBuf;
- closed(code, reason.toStringRef(reasonBuf));
+ closed(code, reason);
}
void WebSocket::Shutdown() {
@@ -309,18 +343,23 @@
void WebSocket::HandleIncoming(uv::Buffer& buf, size_t size) {
// ignore incoming data if we're failed or closed
- if (m_state == FAILED || m_state == CLOSED) return;
+ if (m_state == FAILED || m_state == CLOSED) {
+ return;
+ }
- StringRef data{buf.base, size};
+ std::string_view data{buf.base, size};
// Handle connecting state (mainly on client)
if (m_state == CONNECTING) {
if (m_clientHandshake) {
data = m_clientHandshake->parser.Execute(data);
// check for parser failure
- if (m_clientHandshake->parser.HasError())
+ if (m_clientHandshake->parser.HasError()) {
return Terminate(1003, "invalid response");
- if (m_state != OPEN) return; // not done with handshake yet
+ }
+ if (m_state != OPEN) {
+ return; // not done with handshake yet
+ }
// we're done with the handshake, so release its memory
m_clientHandshake.reset();
@@ -337,45 +376,58 @@
// Need at least two bytes to determine header length
if (m_header.size() < 2u) {
size_t toCopy = (std::min)(2u - m_header.size(), data.size());
- m_header.append(data.bytes_begin(), data.bytes_begin() + toCopy);
- data = data.drop_front(toCopy);
- if (m_header.size() < 2u) return; // need more data
+ m_header.append(data.data(), data.data() + toCopy);
+ data.remove_prefix(toCopy);
+ if (m_header.size() < 2u) {
+ return; // need more data
+ }
// Validate RSV bits are zero
- if ((m_header[0] & 0x70) != 0) return Fail(1002, "nonzero RSV");
+ if ((m_header[0] & 0x70) != 0) {
+ return Fail(1002, "nonzero RSV");
+ }
}
// Once we have first two bytes, we can calculate the header size
if (m_headerSize == 0) {
m_headerSize = 2;
uint8_t len = m_header[1] & kLenMask;
- if (len == 126)
+ if (len == 126) {
m_headerSize += 2;
- else if (len == 127)
+ } else if (len == 127) {
m_headerSize += 8;
+ }
bool masking = (m_header[1] & kFlagMasking) != 0;
- if (masking) m_headerSize += 4; // masking key
+ if (masking) {
+ m_headerSize += 4; // masking key
+ }
// On server side, incoming messages MUST be masked
// On client side, incoming messages MUST NOT be masked
- if (m_server && !masking) return Fail(1002, "client data not masked");
- if (!m_server && masking) return Fail(1002, "server data masked");
+ if (m_server && !masking) {
+ return Fail(1002, "client data not masked");
+ }
+ if (!m_server && masking) {
+ return Fail(1002, "server data masked");
+ }
}
// Need to complete header to calculate message size
if (m_header.size() < m_headerSize) {
size_t toCopy = (std::min)(m_headerSize - m_header.size(), data.size());
- m_header.append(data.bytes_begin(), data.bytes_begin() + toCopy);
- data = data.drop_front(toCopy);
- if (m_header.size() < m_headerSize) return; // need more data
+ m_header.append(data.data(), data.data() + toCopy);
+ data.remove_prefix(toCopy);
+ if (m_header.size() < m_headerSize) {
+ return; // need more data
+ }
}
if (m_header.size() >= m_headerSize) {
// get payload length
uint8_t len = m_header[1] & kLenMask;
- if (len == 126)
+ if (len == 126) {
m_frameSize = (static_cast<uint16_t>(m_header[2]) << 8) |
static_cast<uint16_t>(m_header[3]);
- else if (len == 127)
+ } else if (len == 127) {
m_frameSize = (static_cast<uint64_t>(m_header[2]) << 56) |
(static_cast<uint64_t>(m_header[3]) << 48) |
(static_cast<uint64_t>(m_header[4]) << 40) |
@@ -384,20 +436,22 @@
(static_cast<uint64_t>(m_header[7]) << 16) |
(static_cast<uint64_t>(m_header[8]) << 8) |
static_cast<uint64_t>(m_header[9]);
- else
+ } else {
m_frameSize = len;
+ }
// limit maximum size
- if ((m_payload.size() + m_frameSize) > m_maxMessageSize)
+ if ((m_payload.size() + m_frameSize) > m_maxMessageSize) {
return Fail(1009, "message too large");
+ }
}
}
if (m_frameSize != UINT64_MAX) {
size_t need = m_frameStart + m_frameSize - m_payload.size();
size_t toCopy = (std::min)(need, data.size());
- m_payload.append(data.bytes_begin(), data.bytes_begin() + toCopy);
- data = data.drop_front(toCopy);
+ m_payload.append(data.data(), data.data() + toCopy);
+ data.remove_prefix(toCopy);
need -= toCopy;
if (need == 0) {
// We have a complete frame
@@ -407,10 +461,11 @@
m_header[m_headerSize - 4], m_header[m_headerSize - 3],
m_header[m_headerSize - 2], m_header[m_headerSize - 1]};
int n = 0;
- for (uint8_t& ch :
- MutableArrayRef<uint8_t>{m_payload}.slice(m_frameStart)) {
+ for (uint8_t& ch : span{m_payload}.subspan(m_frameStart)) {
ch ^= key[n++];
- if (n >= 4) n = 0;
+ if (n >= 4) {
+ n = 0;
+ }
}
}
@@ -421,36 +476,53 @@
case kOpCont:
switch (m_fragmentOpcode) {
case kOpText:
- if (!m_combineFragments || fin)
- text(StringRef{reinterpret_cast<char*>(m_payload.data()),
- m_payload.size()},
+ if (!m_combineFragments || fin) {
+ text(std::string_view{reinterpret_cast<char*>(
+ m_payload.data()),
+ m_payload.size()},
fin);
+ }
break;
case kOpBinary:
- if (!m_combineFragments || fin) binary(m_payload, fin);
+ if (!m_combineFragments || fin) {
+ binary(m_payload, fin);
+ }
break;
default:
// no preceding message?
return Fail(1002, "invalid continuation message");
}
- if (fin) m_fragmentOpcode = 0;
+ if (fin) {
+ m_fragmentOpcode = 0;
+ }
break;
case kOpText:
- if (m_fragmentOpcode != 0) return Fail(1002, "incomplete fragment");
- if (!m_combineFragments || fin)
- text(StringRef{reinterpret_cast<char*>(m_payload.data()),
- m_payload.size()},
+ if (m_fragmentOpcode != 0) {
+ return Fail(1002, "incomplete fragment");
+ }
+ if (!m_combineFragments || fin) {
+ text(std::string_view{reinterpret_cast<char*>(m_payload.data()),
+ m_payload.size()},
fin);
- if (!fin) m_fragmentOpcode = opcode;
+ }
+ if (!fin) {
+ m_fragmentOpcode = opcode;
+ }
break;
case kOpBinary:
- if (m_fragmentOpcode != 0) return Fail(1002, "incomplete fragment");
- if (!m_combineFragments || fin) binary(m_payload, fin);
- if (!fin) m_fragmentOpcode = opcode;
+ if (m_fragmentOpcode != 0) {
+ return Fail(1002, "incomplete fragment");
+ }
+ if (!m_combineFragments || fin) {
+ binary(m_payload, fin);
+ }
+ if (!fin) {
+ m_fragmentOpcode = opcode;
+ }
break;
case kOpClose: {
uint16_t code;
- StringRef reason;
+ std::string_view reason;
if (!fin) {
code = 1002;
reason = "cannot fragment control frames";
@@ -459,23 +531,31 @@
} else {
code = (static_cast<uint16_t>(m_payload[0]) << 8) |
static_cast<uint16_t>(m_payload[1]);
- reason = StringRef{reinterpret_cast<char*>(m_payload.data()),
- m_payload.size()}
- .drop_front(2);
+ reason = drop_front(
+ {reinterpret_cast<char*>(m_payload.data()), m_payload.size()},
+ 2);
}
// Echo the close if we didn't previously send it
- if (m_state != CLOSING) SendClose(code, reason);
+ if (m_state != CLOSING) {
+ SendClose(code, reason);
+ }
SetClosed(code, reason);
// If we're the server, shutdown the connection.
- if (m_server) Shutdown();
+ if (m_server) {
+ Shutdown();
+ }
break;
}
case kOpPing:
- if (!fin) return Fail(1002, "cannot fragment control frames");
+ if (!fin) {
+ return Fail(1002, "cannot fragment control frames");
+ }
ping(m_payload);
break;
case kOpPong:
- if (!fin) return Fail(1002, "cannot fragment control frames");
+ if (!fin) {
+ return Fail(1002, "cannot fragment control frames");
+ }
pong(m_payload);
break;
default:
@@ -485,7 +565,9 @@
// Prepare for next message
m_header.clear();
m_headerSize = 0;
- if (!m_combineFragments || fin) m_payload.clear();
+ if (!m_combineFragments || fin) {
+ m_payload.clear();
+ }
m_frameStart = m_payload.size();
m_frameSize = UINT64_MAX;
}
@@ -494,21 +576,22 @@
}
void WebSocket::Send(
- uint8_t opcode, ArrayRef<uv::Buffer> data,
- std::function<void(MutableArrayRef<uv::Buffer>, uv::Error)> callback) {
+ uint8_t opcode, span<const uv::Buffer> data,
+ std::function<void(span<uv::Buffer>, uv::Error)> callback) {
// If we're not open, emit an error and don't send the data
if (m_state != OPEN) {
int err;
- if (m_state == CONNECTING)
+ if (m_state == CONNECTING) {
err = UV_EAGAIN;
- else
+ } else {
err = UV_ESHUTDOWN;
+ }
SmallVector<uv::Buffer, 4> bufs{data.begin(), data.end()};
callback(bufs, uv::Error{err});
return;
}
- auto req = std::make_shared<WebSocketWriteReq>(callback);
+ auto req = std::make_shared<WebSocketWriteReq>(std::move(callback));
raw_uv_ostream os{req->m_bufs, 4096};
// opcode (includes FIN bit)
@@ -516,14 +599,16 @@
// payload length
uint64_t size = 0;
- for (auto&& buf : data) size += buf.len;
+ for (auto&& buf : data) {
+ size += buf.len;
+ }
if (size < 126) {
os << static_cast<unsigned char>((m_server ? 0x00 : kFlagMasking) | size);
} else if (size <= 0xffff) {
os << static_cast<unsigned char>((m_server ? 0x00 : kFlagMasking) | 126);
const uint8_t sizeMsb[] = {static_cast<uint8_t>((size >> 8) & 0xff),
static_cast<uint8_t>(size & 0xff)};
- os << ArrayRef<uint8_t>(sizeMsb);
+ os << span{sizeMsb};
} else {
os << static_cast<unsigned char>((m_server ? 0x00 : kFlagMasking) | 127);
const uint8_t sizeMsb[] = {static_cast<uint8_t>((size >> 56) & 0xff),
@@ -534,7 +619,7 @@
static_cast<uint8_t>((size >> 16) & 0xff),
static_cast<uint8_t>((size >> 8) & 0xff),
static_cast<uint8_t>(size & 0xff)};
- os << ArrayRef<uint8_t>(sizeMsb);
+ os << span{sizeMsb};
}
// clients need to mask the input data
@@ -544,21 +629,24 @@
static std::default_random_engine gen{rd()};
std::uniform_int_distribution<unsigned int> dist(0, 255);
uint8_t key[4];
- for (uint8_t& v : key) v = dist(gen);
- os << ArrayRef<uint8_t>{key, 4};
+ for (uint8_t& v : key) {
+ v = dist(gen);
+ }
+ os << 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;
+ if (n >= 4) {
+ n = 0;
+ }
}
}
req->m_startUser = req->m_bufs.size();
req->m_bufs.append(data.begin(), data.end());
// don't send the user bufs as we copied their data
- m_stream.Write(ArrayRef<uv::Buffer>{req->m_bufs}.slice(0, req->m_startUser),
- req);
+ m_stream.Write(span{req->m_bufs}.subspan(0, req->m_startUser), req);
} else {
// servers can just send the buffers directly without masking
req->m_startUser = req->m_bufs.size();
diff --git a/wpiutil/src/main/native/cpp/WebSocketServer.cpp b/wpiutil/src/main/native/cpp/WebSocketServer.cpp
index 056cb62..1562f3b 100644
--- a/wpiutil/src/main/native/cpp/WebSocketServer.cpp
+++ b/wpiutil/src/main/native/cpp/WebSocketServer.cpp
@@ -1,12 +1,13 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/WebSocketServer.h"
+#include <utility>
+
+#include "wpi/StringExtras.h"
+#include "wpi/fmt/raw_ostream.h"
#include "wpi/raw_uv_ostream.h"
#include "wpi/uv/Buffer.h"
#include "wpi/uv/Stream.h"
@@ -14,76 +15,92 @@
using namespace wpi;
WebSocketServerHelper::WebSocketServerHelper(HttpParser& req) {
- req.header.connect([this](StringRef name, StringRef value) {
- if (name.equals_lower("host")) {
+ req.header.connect([this](std::string_view name, std::string_view value) {
+ if (equals_lower(name, "host")) {
m_gotHost = true;
- } else if (name.equals_lower("upgrade")) {
- if (value.equals_lower("websocket")) m_websocket = true;
- } else if (name.equals_lower("sec-websocket-key")) {
+ } else if (equals_lower(name, "upgrade")) {
+ if (equals_lower(value, "websocket")) {
+ m_websocket = true;
+ }
+ } else if (equals_lower(name, "sec-websocket-key")) {
m_key = value;
- } else if (name.equals_lower("sec-websocket-version")) {
+ } else if (equals_lower(name, "sec-websocket-version")) {
m_version = value;
- } else if (name.equals_lower("sec-websocket-protocol")) {
+ } else if (equals_lower(name, "sec-websocket-protocol")) {
// Protocols are comma delimited, repeated headers add to list
- SmallVector<StringRef, 2> protocols;
- value.split(protocols, ",", -1, false);
+ SmallVector<std::string_view, 2> protocols;
+ split(value, protocols, ",", -1, false);
for (auto protocol : protocols) {
- protocol = protocol.trim();
- if (!protocol.empty()) m_protocols.emplace_back(protocol);
+ protocol = trim(protocol);
+ if (!protocol.empty()) {
+ m_protocols.emplace_back(protocol);
+ }
}
}
});
req.headersComplete.connect([&req, this](bool) {
- if (req.IsUpgrade() && IsUpgrade()) upgrade();
+ if (req.IsUpgrade() && IsUpgrade()) {
+ upgrade();
+ }
});
}
-std::pair<bool, StringRef> WebSocketServerHelper::MatchProtocol(
- ArrayRef<StringRef> protocols) {
- if (protocols.empty() && m_protocols.empty())
- return std::make_pair(true, StringRef{});
+std::pair<bool, std::string_view> WebSocketServerHelper::MatchProtocol(
+ span<const std::string_view> protocols) {
+ if (protocols.empty() && m_protocols.empty()) {
+ return {true, {}};
+ }
for (auto protocol : protocols) {
for (auto&& clientProto : m_protocols) {
- if (protocol == clientProto) return std::make_pair(true, protocol);
+ if (protocol == clientProto) {
+ return {true, protocol};
+ }
}
}
- return std::make_pair(false, StringRef{});
+ return {false, {}};
}
WebSocketServer::WebSocketServer(uv::Stream& stream,
- ArrayRef<StringRef> protocols,
- const ServerOptions& options,
- const private_init&)
+ span<const std::string_view> protocols,
+ ServerOptions options, const private_init&)
: m_stream{stream},
m_helper{m_req},
m_protocols{protocols.begin(), protocols.end()},
- m_options{options} {
+ m_options{std::move(options)} {
// Header handling
- m_req.header.connect([this](StringRef name, StringRef value) {
- if (name.equals_lower("host")) {
+ m_req.header.connect([this](std::string_view name, std::string_view value) {
+ if (equals_lower(name, "host")) {
if (m_options.checkHost) {
- if (!m_options.checkHost(value)) Abort(401, "Unrecognized Host");
+ if (!m_options.checkHost(value)) {
+ Abort(401, "Unrecognized Host");
+ }
}
}
});
- m_req.url.connect([this](StringRef name) {
+ m_req.url.connect([this](std::string_view name) {
if (m_options.checkUrl) {
- if (!m_options.checkUrl(name)) Abort(404, "Not Found");
+ if (!m_options.checkUrl(name)) {
+ Abort(404, "Not Found");
+ }
}
});
m_req.headersComplete.connect([this](bool) {
// We only accept websocket connections
- if (!m_helper.IsUpgrade() || !m_req.IsUpgrade())
+ if (!m_helper.IsUpgrade() || !m_req.IsUpgrade()) {
Abort(426, "Upgrade Required");
+ }
});
// Handle upgrade event
m_helper.upgrade.connect([this] {
- if (m_aborted) return;
+ if (m_aborted) {
+ return;
+ }
// Negotiate sub-protocol
- SmallVector<StringRef, 2> protocols{m_protocols.begin(), m_protocols.end()};
- StringRef protocol = m_helper.MatchProtocol(protocols).second;
+ SmallVector<std::string_view, 2> protocols{m_protocols.begin(),
+ m_protocols.end()};
+ std::string_view protocol = m_helper.MatchProtocol(protocols).second;
// Disconnect our header reader
m_dataConn.disconnect();
@@ -96,19 +113,24 @@
auto ws = m_helper.Accept(m_stream, protocol);
// Connect the websocket open event to our connected event.
- ws->open.connect_extended([self, s = ws.get()](auto conn, StringRef) {
- self->connected(self->m_req.GetUrl(), *s);
- conn.disconnect(); // one-shot
- });
+ ws->open.connect_extended(
+ [self, s = ws.get()](auto conn, std::string_view) {
+ self->connected(self->m_req.GetUrl(), *s);
+ conn.disconnect(); // one-shot
+ });
});
// Set up stream
stream.StartRead();
m_dataConn =
stream.data.connect_connection([this](uv::Buffer& buf, size_t size) {
- if (m_aborted) return;
- m_req.Execute(StringRef{buf.base, size});
- if (m_req.HasError()) Abort(400, "Bad Request");
+ if (m_aborted) {
+ return;
+ }
+ m_req.Execute(std::string_view{buf.base, size});
+ if (m_req.HasError()) {
+ Abort(400, "Bad Request");
+ }
});
m_errorConn =
stream.error.connect_connection([this](uv::Error) { m_stream.Close(); });
@@ -116,7 +138,7 @@
}
std::shared_ptr<WebSocketServer> WebSocketServer::Create(
- uv::Stream& stream, ArrayRef<StringRef> protocols,
+ uv::Stream& stream, span<const std::string_view> protocols,
const ServerOptions& options) {
auto server = std::make_shared<WebSocketServer>(stream, protocols, options,
private_init{});
@@ -124,8 +146,10 @@
return server;
}
-void WebSocketServer::Abort(uint16_t code, StringRef reason) {
- if (m_aborted) return;
+void WebSocketServer::Abort(uint16_t code, std::string_view reason) {
+ if (m_aborted) {
+ return;
+ }
m_aborted = true;
// Build response
@@ -133,11 +157,15 @@
raw_uv_ostream os{bufs, 1024};
// Handle unsupported version
- os << "HTTP/1.1 " << code << ' ' << reason << "\r\n";
- if (code == 426) os << "Upgrade: WebSocket\r\n";
+ fmt::print(os, "HTTP/1.1 {} {}\r\n", code, reason);
+ if (code == 426) {
+ os << "Upgrade: WebSocket\r\n";
+ }
os << "\r\n";
m_stream.Write(bufs, [this](auto bufs, uv::Error) {
- for (auto& buf : bufs) buf.Deallocate();
+ for (auto& buf : bufs) {
+ buf.Deallocate();
+ }
m_stream.Shutdown([this] { m_stream.Close(); });
});
}
diff --git a/wpiutil/src/main/native/cpp/fs.cpp b/wpiutil/src/main/native/cpp/fs.cpp
new file mode 100644
index 0000000..34d6cca
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/fs.cpp
@@ -0,0 +1,326 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+//===----------------------------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include <cassert>
+
+#ifdef _WIN32
+#include <fcntl.h>
+#include <io.h>
+#include <sys/types.h>
+// Require at least Windows 7 API.
+#define _WIN32_WINNT 0x0601
+#define _WIN32_IE 0x0800 // MinGW at it again. FIXME: verify if still needed.
+#define WIN32_LEAN_AND_MEAN
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif
+
+#define WIN32_NO_STATUS
+#include <windows.h>
+#undef WIN32_NO_STATUS
+#include <winternl.h>
+#include <ntstatus.h>
+
+#include <shellapi.h>
+#include <shlobj.h>
+
+#include "wpi/WindowsError.h"
+
+#else // _WIN32
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#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"
+#include "wpi/fs.h"
+
+namespace fs {
+
+#ifdef _WIN32
+
+#ifdef _MSC_VER
+#pragma comment(lib, "shell32.lib")
+#pragma comment(lib, "ole32.lib")
+#pragma warning(push)
+#pragma warning(disable : 4244 4267 4146)
+#endif
+
+const file_t kInvalidFile = INVALID_HANDLE_VALUE;
+
+static DWORD nativeDisposition(CreationDisposition Disp, OpenFlags Flags) {
+ // This is a compatibility hack. Really we should respect the creation
+ // disposition, but a lot of old code relied on the implicit assumption that
+ // OF_Append implied it would open an existing file. Since the disposition is
+ // now explicit and defaults to CD_CreateAlways, this assumption would cause
+ // any usage of OF_Append to append to a new file, even if the file already
+ // existed. A better solution might have two new creation dispositions:
+ // CD_AppendAlways and CD_AppendNew. This would also address the problem of
+ // OF_Append being used on a read-only descriptor, which doesn't make sense.
+ if (Flags & OF_Append)
+ return OPEN_ALWAYS;
+
+ switch (Disp) {
+ case CD_CreateAlways:
+ return CREATE_ALWAYS;
+ case CD_CreateNew:
+ return CREATE_NEW;
+ case CD_OpenAlways:
+ return OPEN_ALWAYS;
+ case CD_OpenExisting:
+ return OPEN_EXISTING;
+ }
+ wpi_unreachable("unreachable!");
+}
+
+static DWORD nativeAccess(FileAccess Access, OpenFlags Flags) {
+ DWORD Result = 0;
+ if (Access & FA_Read)
+ Result |= GENERIC_READ;
+ if (Access & FA_Write)
+ Result |= GENERIC_WRITE;
+ if (Flags & OF_Delete)
+ Result |= DELETE;
+ if (Flags & OF_UpdateAtime)
+ Result |= FILE_WRITE_ATTRIBUTES;
+ return Result;
+}
+
+static file_t openFileInternal(const path& Path, std::error_code& EC,
+ DWORD Disp, DWORD Access, DWORD Flags,
+ bool Inherit = false) {
+ SECURITY_ATTRIBUTES SA;
+ SA.nLength = sizeof(SA);
+ SA.lpSecurityDescriptor = nullptr;
+ SA.bInheritHandle = Inherit;
+
+ HANDLE H =
+ ::CreateFileW(Path.c_str(), Access,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, &SA,
+ Disp, Flags, NULL);
+ if (H == INVALID_HANDLE_VALUE) {
+ DWORD LastError = ::GetLastError();
+ EC = wpi::mapWindowsError(LastError);
+ // Provide a better error message when trying to open directories.
+ // This only runs if we failed to open the file, so there is probably
+ // no performances issues.
+ if (LastError != ERROR_ACCESS_DENIED) {
+ return kInvalidFile;
+ }
+ if (is_directory(Path)) {
+ EC = std::make_error_code(std::errc::is_a_directory);
+ }
+ return kInvalidFile;
+ }
+ EC = std::error_code();
+ return H;
+}
+
+static std::error_code setDeleteDisposition(HANDLE Handle, bool Delete) {
+ FILE_DISPOSITION_INFO Disposition;
+ Disposition.DeleteFile = Delete;
+ if (!::SetFileInformationByHandle(Handle, FileDispositionInfo, &Disposition,
+ sizeof(Disposition)))
+ return wpi::mapWindowsError(::GetLastError());
+ return std::error_code();
+}
+
+file_t OpenFile(const path& Path, std::error_code& EC, CreationDisposition Disp,
+ FileAccess Access, OpenFlags Flags, unsigned Mode) {
+ // Verify that we don't have both "append" and "excl".
+ assert((!(Disp == CD_CreateNew) || !(Flags & OF_Append)) &&
+ "Cannot specify both 'CreateNew' and 'Append' file creation flags!");
+
+ DWORD NativeDisp = nativeDisposition(Disp, Flags);
+ DWORD NativeAccess = nativeAccess(Access, Flags);
+
+ bool Inherit = false;
+ if (Flags & OF_ChildInherit) {
+ Inherit = true;
+ }
+
+ file_t Result = openFileInternal(Path, EC, NativeDisp, NativeAccess,
+ FILE_ATTRIBUTE_NORMAL, Inherit);
+ if (EC) {
+ return Result;
+ }
+
+ if (Flags & OF_UpdateAtime) {
+ FILETIME FileTime;
+ SYSTEMTIME SystemTime;
+ ::GetSystemTime(&SystemTime);
+ if (::SystemTimeToFileTime(&SystemTime, &FileTime) == 0 ||
+ ::SetFileTime(Result, NULL, &FileTime, NULL) == 0) {
+ DWORD LastError = ::GetLastError();
+ ::CloseHandle(Result);
+ EC = wpi::mapWindowsError(LastError);
+ return kInvalidFile;
+ }
+ }
+
+ if (Flags & OF_Delete) {
+ if ((EC = setDeleteDisposition(Result, true))) {
+ ::CloseHandle(Result);
+ return kInvalidFile;
+ }
+ }
+ return Result;
+}
+
+file_t OpenFileForRead(const path& Path, std::error_code& EC, OpenFlags Flags) {
+ return OpenFile(Path, EC, CD_OpenExisting, FA_Read, Flags);
+}
+
+int FileToFd(file_t& F, std::error_code& EC, OpenFlags Flags) {
+ if (F == kInvalidFile) {
+ EC = wpi::mapWindowsError(ERROR_INVALID_HANDLE);
+ return -1;
+ }
+
+ int CrtOpenFlags = 0;
+ if (Flags & OF_Append) {
+ CrtOpenFlags |= _O_APPEND;
+ }
+
+ if (Flags & OF_Text) {
+ CrtOpenFlags |= _O_TEXT;
+ }
+
+ int ResultFD = ::_open_osfhandle(intptr_t(F), CrtOpenFlags);
+ if (ResultFD == -1) {
+ ::CloseHandle(F);
+ EC = wpi::mapWindowsError(ERROR_INVALID_HANDLE);
+ return -1;
+ }
+
+ EC = std::error_code();
+ F = kInvalidFile;
+ return ResultFD;
+}
+
+void CloseFile(file_t& F) {
+ ::CloseHandle(F);
+ F = kInvalidFile;
+}
+
+#else // _WIN32
+
+const file_t kInvalidFile = -1;
+
+static int nativeOpenFlags(CreationDisposition Disp, OpenFlags Flags,
+ FileAccess Access) {
+ int Result = 0;
+ if (Access == FA_Read) {
+ Result |= O_RDONLY;
+ } else if (Access == FA_Write) {
+ Result |= O_WRONLY;
+ } else if (Access == (FA_Read | FA_Write)) {
+ Result |= O_RDWR;
+ }
+
+ // This is for compatibility with old code that assumed F_Append implied
+ // would open an existing file. See Windows/Path.inc for a longer comment.
+ if (Flags & F_Append) {
+ Disp = CD_OpenAlways;
+ }
+
+ if (Disp == CD_CreateNew) {
+ Result |= O_CREAT; // Create if it doesn't exist.
+ Result |= O_EXCL; // Fail if it does.
+ } else if (Disp == CD_CreateAlways) {
+ Result |= O_CREAT; // Create if it doesn't exist.
+ Result |= O_TRUNC; // Truncate if it does.
+ } else if (Disp == CD_OpenAlways) {
+ Result |= O_CREAT; // Create if it doesn't exist.
+ } else if (Disp == CD_OpenExisting) {
+ // Nothing special, just don't add O_CREAT and we get these semantics.
+ }
+
+ if (Flags & F_Append) {
+ Result |= O_APPEND;
+ }
+
+#ifdef O_CLOEXEC
+ if (!(Flags & OF_ChildInherit)) {
+ Result |= O_CLOEXEC;
+ }
+#endif
+
+ return Result;
+}
+
+file_t OpenFile(const path& Path, std::error_code& EC, CreationDisposition Disp,
+ FileAccess Access, OpenFlags Flags, unsigned Mode) {
+ int OpenFlags = nativeOpenFlags(Disp, Flags, Access);
+ file_t ResultFD = kInvalidFile;
+
+ // Call ::open in a lambda to avoid overload resolution in RetryAfterSignal
+ // when open is overloaded, such as in Bionic.
+ auto Open = [&]() { return ::open(Path.c_str(), OpenFlags, Mode); };
+ if ((ResultFD = wpi::sys::RetryAfterSignal(-1, Open)) < 0) {
+ EC = std::error_code(errno, std::generic_category());
+ return kInvalidFile;
+ }
+#ifndef O_CLOEXEC
+ if (!(Flags & OF_ChildInherit)) {
+ int r = fcntl(ResultFD, F_SETFD, FD_CLOEXEC);
+ (void)r;
+ assert(r == 0 && "fcntl(F_SETFD, FD_CLOEXEC) failed");
+ }
+#endif
+ EC = std::error_code();
+ return ResultFD;
+}
+
+file_t OpenFileForRead(const path& Path, std::error_code& EC, OpenFlags Flags) {
+ return OpenFile(Path, EC, CD_OpenExisting, FA_Read, Flags, 0666);
+}
+
+int FileToFd(file_t& F, std::error_code& EC, OpenFlags Flags) {
+ int fd = F;
+ F = kInvalidFile;
+ EC = std::error_code();
+ return fd;
+}
+
+void CloseFile(file_t& F) {
+ ::close(F);
+ F = kInvalidFile;
+}
+
+#endif // _WIN32
+
+} // namespace fs
diff --git a/wpiutil/src/main/native/cpp/future.cpp b/wpiutil/src/main/native/cpp/future.cpp
index 7ce0875..109eeb9 100644
--- a/wpiutil/src/main/native/cpp/future.cpp
+++ b/wpiutil/src/main/native/cpp/future.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/future.h"
@@ -28,10 +25,14 @@
}
bool PromiseFactoryBase::EraseRequest(uint64_t request) {
- if (request == 0) return false;
+ if (request == 0) {
+ return false;
+ }
auto it = std::find_if(m_requests.begin(), m_requests.end(),
[=](auto r) { return r == request; });
- if (it == m_requests.end()) return false; // no waiters
+ if (it == m_requests.end()) {
+ return false; // no waiters
+ }
m_requests.erase(it);
return true;
}
@@ -47,7 +48,9 @@
void PromiseFactory<void>::SetValue(uint64_t request) {
std::unique_lock lock(GetResultMutex());
- if (!EraseRequest(request)) return;
+ if (!EraseRequest(request)) {
+ return;
+ }
auto it = std::find_if(m_thens.begin(), m_thens.end(),
[=](const auto& x) { return x.request == request; });
if (it != m_thens.end()) {
@@ -105,7 +108,9 @@
// Did we get a response to *our* request?
auto it = std::find_if(m_results.begin(), m_results.end(),
[=](const auto& r) { return r == request; });
- if (it != m_results.end()) return;
+ if (it != m_results.end()) {
+ return;
+ }
// No, keep waiting for a response
Wait(lock);
}
diff --git a/wpiutil/src/main/native/cpp/hostname.cpp b/wpiutil/src/main/native/cpp/hostname.cpp
index ffc5cad..d907023 100644
--- a/wpiutil/src/main/native/cpp/hostname.cpp
+++ b/wpiutil/src/main/native/cpp/hostname.cpp
@@ -1,18 +1,15 @@
-/*----------------------------------------------------------------------------*/
-/* 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. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/hostname.h"
#include <cstdlib>
#include <string>
+#include <string_view>
#include "uv.h"
#include "wpi/SmallVector.h"
-#include "wpi/StringRef.h"
namespace wpi {
@@ -27,14 +24,16 @@
} else if (err == UV_ENOBUFS) {
char* name2 = static_cast<char*>(std::malloc(size));
err = uv_os_gethostname(name2, &size);
- if (err == 0) rv.assign(name2, size);
+ if (err == 0) {
+ rv.assign(name2, size);
+ }
std::free(name2);
}
return rv;
}
-StringRef GetHostname(SmallVectorImpl<char>& name) {
+std::string_view GetHostname(SmallVectorImpl<char>& name) {
// Use a tmp array to not require the SmallVector to be too large.
char tmpName[256];
size_t size = sizeof(tmpName);
@@ -47,10 +46,12 @@
} else if (err == UV_ENOBUFS) {
name.resize(size);
err = uv_os_gethostname(name.data(), &size);
- if (err != 0) size = 0;
+ if (err != 0) {
+ size = 0;
+ }
}
- return StringRef{name.data(), size};
+ return {name.data(), size};
}
} // namespace wpi
diff --git a/wpiutil/src/main/native/cpp/jni/WPIUtilJNI.cpp b/wpiutil/src/main/native/cpp/jni/WPIUtilJNI.cpp
index 90a2291..4fc5f45 100644
--- a/wpiutil/src/main/native/cpp/jni/WPIUtilJNI.cpp
+++ b/wpiutil/src/main/native/cpp/jni/WPIUtilJNI.cpp
@@ -1,50 +1,94 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2019-2020 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. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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 "edu_wpi_first_wpiutil_WPIUtilJNI.h"
+#include "edu_wpi_first_util_WPIUtilJNI.h"
#include "wpi/PortForwarder.h"
+#include "wpi/Synchronization.h"
#include "wpi/jni_util.h"
#include "wpi/timestamp.h"
using namespace wpi::java;
+static bool mockTimeEnabled = false;
+static uint64_t mockNow = 0;
+
+static JException interruptedEx;
+
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)
+ if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
return JNI_ERR;
+ }
+
+ interruptedEx = JException(env, "java/lang/InterruptedException");
+ if (!interruptedEx) {
+ return JNI_ERR;
+ }
return JNI_VERSION_1_6;
}
-JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved) {}
+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;
+ }
+ interruptedEx.free(env);
+}
/*
- * Class: edu_wpi_first_wpiutil_WPIUtilJNI
+ * Class: edu_wpi_first_util_WPIUtilJNI
+ * Method: enableMockTime
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_util_WPIUtilJNI_enableMockTime
+ (JNIEnv*, jclass)
+{
+ mockTimeEnabled = true;
+ wpi::SetNowImpl([] { return mockNow; });
+}
+
+/*
+ * Class: edu_wpi_first_util_WPIUtilJNI
+ * Method: setMockTime
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_util_WPIUtilJNI_setMockTime
+ (JNIEnv*, jclass, jlong time)
+{
+ mockNow = time;
+}
+
+/*
+ * Class: edu_wpi_first_util_WPIUtilJNI
* Method: now
* Signature: ()J
*/
JNIEXPORT jlong JNICALL
-Java_edu_wpi_first_wpiutil_WPIUtilJNI_now
+Java_edu_wpi_first_util_WPIUtilJNI_now
(JNIEnv*, jclass)
{
- return wpi::Now();
+ if (mockTimeEnabled) {
+ return mockNow;
+ } else {
+ return wpi::Now();
+ }
}
/*
- * Class: edu_wpi_first_wpiutil_WPIUtilJNI
+ * Class: edu_wpi_first_util_WPIUtilJNI
* Method: addPortForwarder
* Signature: (ILjava/lang/String;I)V
*/
JNIEXPORT void JNICALL
-Java_edu_wpi_first_wpiutil_WPIUtilJNI_addPortForwarder
+Java_edu_wpi_first_util_WPIUtilJNI_addPortForwarder
(JNIEnv* env, jclass, jint port, jstring remoteHost, jint remotePort)
{
wpi::PortForwarder::GetInstance().Add(static_cast<unsigned int>(port),
@@ -53,15 +97,180 @@
}
/*
- * Class: edu_wpi_first_wpiutil_WPIUtilJNI
+ * Class: edu_wpi_first_util_WPIUtilJNI
* Method: removePortForwarder
* Signature: (I)V
*/
JNIEXPORT void JNICALL
-Java_edu_wpi_first_wpiutil_WPIUtilJNI_removePortForwarder
+Java_edu_wpi_first_util_WPIUtilJNI_removePortForwarder
(JNIEnv* env, jclass, jint port)
{
wpi::PortForwarder::GetInstance().Remove(port);
}
+/*
+ * Class: edu_wpi_first_util_WPIUtilJNI
+ * Method: createEvent
+ * Signature: (ZZ)I
+ */
+JNIEXPORT jint JNICALL
+Java_edu_wpi_first_util_WPIUtilJNI_createEvent
+ (JNIEnv*, jclass, jboolean manualReset, jboolean initialState)
+{
+ return wpi::CreateEvent(manualReset, initialState);
+}
+
+/*
+ * Class: edu_wpi_first_util_WPIUtilJNI
+ * Method: destroyEvent
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_util_WPIUtilJNI_destroyEvent
+ (JNIEnv*, jclass, jint eventHandle)
+{
+ wpi::DestroyEvent(eventHandle);
+}
+
+/*
+ * Class: edu_wpi_first_util_WPIUtilJNI
+ * Method: setEvent
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_util_WPIUtilJNI_setEvent
+ (JNIEnv*, jclass, jint eventHandle)
+{
+ wpi::SetEvent(eventHandle);
+}
+
+/*
+ * Class: edu_wpi_first_util_WPIUtilJNI
+ * Method: resetEvent
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_util_WPIUtilJNI_resetEvent
+ (JNIEnv*, jclass, jint eventHandle)
+{
+ wpi::ResetEvent(eventHandle);
+}
+
+/*
+ * Class: edu_wpi_first_util_WPIUtilJNI
+ * Method: createSemaphore
+ * Signature: (II)I
+ */
+JNIEXPORT jint JNICALL
+Java_edu_wpi_first_util_WPIUtilJNI_createSemaphore
+ (JNIEnv*, jclass, jint initialCount, jint maximumCount)
+{
+ return wpi::CreateSemaphore(initialCount, maximumCount);
+}
+
+/*
+ * Class: edu_wpi_first_util_WPIUtilJNI
+ * Method: destroySemaphore
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_util_WPIUtilJNI_destroySemaphore
+ (JNIEnv*, jclass, jint semHandle)
+{
+ wpi::DestroySemaphore(semHandle);
+}
+
+/*
+ * Class: edu_wpi_first_util_WPIUtilJNI
+ * Method: releaseSemaphore
+ * Signature: (II)Z
+ */
+JNIEXPORT jboolean JNICALL
+Java_edu_wpi_first_util_WPIUtilJNI_releaseSemaphore
+ (JNIEnv*, jclass, jint semHandle, jint releaseCount)
+{
+ return wpi::ReleaseSemaphore(semHandle, releaseCount);
+}
+
+/*
+ * Class: edu_wpi_first_util_WPIUtilJNI
+ * Method: waitForObject
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_util_WPIUtilJNI_waitForObject
+ (JNIEnv* env, jclass, jint handle)
+{
+ if (!wpi::WaitForObject(handle)) {
+ interruptedEx.Throw(env, "WaitForObject interrupted");
+ }
+}
+
+/*
+ * Class: edu_wpi_first_util_WPIUtilJNI
+ * Method: waitForObjectTimeout
+ * Signature: (ID)Z
+ */
+JNIEXPORT jboolean JNICALL
+Java_edu_wpi_first_util_WPIUtilJNI_waitForObjectTimeout
+ (JNIEnv* env, jclass, jint handle, jdouble timeout)
+{
+ bool timedOut;
+ if (!wpi::WaitForObject(handle, timeout, &timedOut) && !timedOut) {
+ interruptedEx.Throw(env, "WaitForObject interrupted");
+ return false;
+ }
+ return timedOut;
+}
+
+/*
+ * Class: edu_wpi_first_util_WPIUtilJNI
+ * Method: waitForObjects
+ * Signature: ([I)[I
+ */
+JNIEXPORT jintArray JNICALL
+Java_edu_wpi_first_util_WPIUtilJNI_waitForObjects
+ (JNIEnv* env, jclass, jintArray handles)
+{
+ JIntArrayRef handlesArr{env, handles};
+ wpi::SmallVector<WPI_Handle, 8> signaledBuf;
+ signaledBuf.resize(handlesArr.size());
+ wpi::span<const WPI_Handle> handlesArr2{
+ reinterpret_cast<const WPI_Handle*>(handlesArr.array().data()),
+ handlesArr.size()};
+
+ auto signaled = wpi::WaitForObjects(handlesArr2, signaledBuf);
+ if (signaled.empty()) {
+ interruptedEx.Throw(env, "WaitForObjects interrupted");
+ return nullptr;
+ }
+ return MakeJIntArray(env, signaled);
+}
+
+/*
+ * Class: edu_wpi_first_util_WPIUtilJNI
+ * Method: waitForObjectsTimeout
+ * Signature: ([ID)[I
+ */
+JNIEXPORT jintArray JNICALL
+Java_edu_wpi_first_util_WPIUtilJNI_waitForObjectsTimeout
+ (JNIEnv* env, jclass, jintArray handles, jdouble timeout)
+{
+ JIntArrayRef handlesArr{env, handles};
+ wpi::SmallVector<WPI_Handle, 8> signaledBuf;
+ signaledBuf.resize(handlesArr.size());
+ wpi::span<const WPI_Handle> handlesArr2{
+ reinterpret_cast<const WPI_Handle*>(handlesArr.array().data()),
+ handlesArr.size()};
+
+ bool timedOut;
+ auto signaled =
+ wpi::WaitForObjects(handlesArr2, signaledBuf, timeout, &timedOut);
+ if (signaled.empty() && !timedOut) {
+ interruptedEx.Throw(env, "WaitForObjects interrupted");
+ return nullptr;
+ }
+ return MakeJIntArray(env, signaled);
+}
+
} // extern "C"
diff --git a/wpiutil/src/main/native/cpp/json.cpp b/wpiutil/src/main/native/cpp/json.cpp
index ba9cd8e..f1cfb14 100644
--- a/wpiutil/src/main/native/cpp/json.cpp
+++ b/wpiutil/src/main/native/cpp/json.cpp
@@ -34,39 +34,51 @@
#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_, const Twine& what_arg)
- : id(id_), m(what_arg.str()) {}
+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_, const Twine& what_arg)
+parse_error parse_error::create(int id_, std::size_t byte_, std::string_view what_arg)
{
- return parse_error(id_, byte_, "[json.exception.parse_error." + Twine(id_) + "] parse error" +
- (byte_ != 0 ? (" at " + Twine(byte_)) : Twine("")) +
- ": " + 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_, const Twine& what_arg)
+invalid_iterator invalid_iterator::create(int id_, std::string_view what_arg)
{
- return invalid_iterator(id_, "[json.exception.invalid_iterator." + Twine(id_) + "] " + what_arg);
+ return invalid_iterator(id_, fmt::format("[json.exception.invalid_iterator.{}] {}", id_, what_arg));
}
-type_error type_error::create(int id_, const Twine& what_arg)
+invalid_iterator invalid_iterator::create(int id_, std::string_view what_arg, std::string_view type_info)
{
- return type_error(id_, "[json.exception.type_error." + Twine(id_) + "] " + what_arg);
+ return invalid_iterator(id_, fmt::format("[json.exception.invalid_iterator.{}] {} {}", id_, what_arg, type_info));
}
-out_of_range out_of_range::create(int id_, const Twine& what_arg)
+type_error type_error::create(int id_, std::string_view what_arg)
{
- return out_of_range(id_, "[json.exception.out_of_range." + Twine(id_) + "] " + what_arg);
+ return type_error(id_, fmt::format("[json.exception.type_error.{}] {}", id_, what_arg));
}
-other_error other_error::create(int id_, const Twine& what_arg)
+type_error type_error::create(int id_, std::string_view what_arg, std::string_view type_info)
{
- return other_error(id_, "[json.exception.other_error." + Twine(id_) + "] " + what_arg);
+ 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
@@ -353,12 +365,12 @@
JSON_CATCH (std::out_of_range&)
{
// create better exception explanation
- JSON_THROW(out_of_range::create(401, "array index " + Twine(idx) + " is out of range"));
+ 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 " + Twine(type_name())));
+ JSON_THROW(type_error::create(304, "cannot use at() with", type_name()));
}
}
@@ -374,16 +386,16 @@
JSON_CATCH (std::out_of_range&)
{
// create better exception explanation
- JSON_THROW(out_of_range::create(401, "array index " + Twine(idx) + " is out of range"));
+ 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 " + Twine(type_name())));
+ JSON_THROW(type_error::create(304, "cannot use at() with", type_name()));
}
}
-json::reference json::at(StringRef key)
+json::reference json::at(std::string_view key)
{
// at only works for objects
if (JSON_LIKELY(is_object()))
@@ -392,17 +404,17 @@
if (it == m_value.object->end())
{
// create better exception explanation
- JSON_THROW(out_of_range::create(403, "key '" + Twine(key) + "' not found"));
+ 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 " + Twine(type_name())));
+ JSON_THROW(type_error::create(304, "cannot use at() with", type_name()));
}
}
-json::const_reference json::at(StringRef key) const
+json::const_reference json::at(std::string_view key) const
{
// at only works for objects
if (JSON_LIKELY(is_object()))
@@ -411,13 +423,13 @@
if (it == m_value.object->end())
{
// create better exception explanation
- JSON_THROW(out_of_range::create(403, "key '" + Twine(key) + "' not found"));
+ 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 " + Twine(type_name())));
+ JSON_THROW(type_error::create(304, "cannot use at() with", type_name()));
}
}
@@ -445,7 +457,7 @@
return m_value.array->operator[](idx);
}
- JSON_THROW(type_error::create(305, "cannot use operator[] with " + Twine(type_name())));
+ JSON_THROW(type_error::create(305, "cannot use operator[] with", type_name()));
}
json::const_reference json::operator[](size_type idx) const
@@ -456,10 +468,10 @@
return m_value.array->operator[](idx);
}
- JSON_THROW(type_error::create(305, "cannot use operator[] with " + Twine(type_name())));
+ JSON_THROW(type_error::create(305, "cannot use operator[] with", type_name()));
}
-json::reference json::operator[](StringRef key)
+json::reference json::operator[](std::string_view key)
{
// implicitly convert null value to an empty object
if (is_null())
@@ -475,10 +487,10 @@
return m_value.object->operator[](key);
}
- JSON_THROW(type_error::create(305, "cannot use operator[] with " + Twine(type_name())));
+ JSON_THROW(type_error::create(305, "cannot use operator[] with", type_name()));
}
-json::const_reference json::operator[](StringRef key) const
+json::const_reference json::operator[](std::string_view key) const
{
// const operator[] only works for objects
if (JSON_LIKELY(is_object()))
@@ -487,10 +499,10 @@
return m_value.object->find(key)->second;
}
- JSON_THROW(type_error::create(305, "cannot use operator[] with " + Twine(type_name())));
+ JSON_THROW(type_error::create(305, "cannot use operator[] with", type_name()));
}
-json::size_type json::erase(StringRef key)
+json::size_type json::erase(std::string_view key)
{
// this erase only works for objects
if (JSON_LIKELY(is_object()))
@@ -498,7 +510,7 @@
return m_value.object->erase(key);
}
- JSON_THROW(type_error::create(307, "cannot use erase() with " + Twine(type_name())));
+ JSON_THROW(type_error::create(307, "cannot use erase() with", type_name()));
}
void json::erase(const size_type idx)
@@ -508,18 +520,18 @@
{
if (JSON_UNLIKELY(idx >= size()))
{
- JSON_THROW(out_of_range::create(401, "array index " + Twine(idx) + " is out of range"));
+ 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 " + Twine(type_name())));
+ JSON_THROW(type_error::create(307, "cannot use erase() with", type_name()));
}
}
-json::iterator json::find(StringRef key)
+json::iterator json::find(std::string_view key)
{
auto result = end();
@@ -531,7 +543,7 @@
return result;
}
-json::const_iterator json::find(StringRef key) const
+json::const_iterator json::find(std::string_view key) const
{
auto result = cend();
@@ -543,7 +555,7 @@
return result;
}
-json::size_type json::count(StringRef key) const
+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;
@@ -689,7 +701,7 @@
// push_back only works for null objects or arrays
if (JSON_UNLIKELY(not(is_null() or is_array())))
{
- JSON_THROW(type_error::create(308, "cannot use push_back() with " + Twine(type_name())));
+ JSON_THROW(type_error::create(308, "cannot use push_back() with", type_name()));
}
// transform null object into an array
@@ -711,7 +723,7 @@
// push_back only works for null objects or arrays
if (JSON_UNLIKELY(not(is_null() or is_array())))
{
- JSON_THROW(type_error::create(308, "cannot use push_back() with " + Twine(type_name())));
+ JSON_THROW(type_error::create(308, "cannot use push_back() with", type_name()));
}
// transform null object into an array
@@ -731,7 +743,7 @@
if (is_object() and init.size() == 2 and (*init.begin())->is_string())
{
std::string key = init.begin()->moved_or_copied();
- push_back(std::pair<StringRef, json>(key, (init.begin() + 1)->moved_or_copied()));
+ push_back(std::pair<std::string_view, json>(key, (init.begin() + 1)->moved_or_copied()));
}
else
{
@@ -756,7 +768,7 @@
return result;
}
- JSON_THROW(type_error::create(309, "cannot use insert() with " + Twine(type_name())));
+ 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)
@@ -776,7 +788,7 @@
return result;
}
- JSON_THROW(type_error::create(309, "cannot use insert() with " + Twine(type_name())));
+ 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)
@@ -784,7 +796,7 @@
// insert only works for arrays
if (JSON_UNLIKELY(not is_array()))
{
- JSON_THROW(type_error::create(309, "cannot use insert() with " + Twine(type_name())));
+ JSON_THROW(type_error::create(309, "cannot use insert() with", type_name()));
}
// check if iterator pos fits to this JSON value
@@ -818,7 +830,7 @@
// insert only works for arrays
if (JSON_UNLIKELY(not is_array()))
{
- JSON_THROW(type_error::create(309, "cannot use insert() with " + Twine(type_name())));
+ JSON_THROW(type_error::create(309, "cannot use insert() with", type_name()));
}
// check if iterator pos fits to this JSON value
@@ -838,7 +850,7 @@
// insert only works for objects
if (JSON_UNLIKELY(not is_object()))
{
- JSON_THROW(type_error::create(309, "cannot use insert() with " + Twine(type_name())));
+ JSON_THROW(type_error::create(309, "cannot use insert() with", type_name()));
}
// check if range iterators belong to the same JSON object
@@ -871,11 +883,11 @@
if (JSON_UNLIKELY(not is_object()))
{
- JSON_THROW(type_error::create(312, "cannot use update() with " + Twine(type_name())));
+ 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 " + Twine(j.type_name())));
+ JSON_THROW(type_error::create(312, "cannot use update() with", j.type_name()));
}
for (auto it = j.cbegin(); it != j.cend(); ++it)
@@ -896,7 +908,7 @@
if (JSON_UNLIKELY(not is_object()))
{
- JSON_THROW(type_error::create(312, "cannot use update() with " + Twine(type_name())));
+ JSON_THROW(type_error::create(312, "cannot use update() with", type_name()));
}
// check if range iterators belong to the same JSON object
@@ -1156,7 +1168,7 @@
if (JSON_UNLIKELY(static_cast<size_type>(idx) > parent.size()))
{
// avoid undefined behavior
- JSON_THROW(out_of_range::create(401, "array index " + Twine(idx) + " is out of range"));
+ JSON_THROW(out_of_range::create(401, fmt::format("array index {} is out of range", idx)));
}
else
{
@@ -1194,7 +1206,7 @@
}
else
{
- JSON_THROW(out_of_range::create(403, "key '" + Twine(last_path) + "' not found"));
+ JSON_THROW(out_of_range::create(403, fmt::format("key '{}' not found", last_path)));
}
}
else if (parent.is_array())
@@ -1227,13 +1239,13 @@
// check if desired value is present
if (JSON_UNLIKELY(it == val.m_value.object->end()))
{
- JSON_THROW(parse_error::create(105, 0, Twine(error_msg) + " must have member '" + Twine(member) + "'"));
+ 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, Twine(error_msg) + " must have string member '" + Twine(member) + "'"));
+ JSON_THROW(parse_error::create(105, 0, fmt::format("{} must have string member '{}'", error_msg, member)));
}
// no error: return value
@@ -1321,7 +1333,7 @@
// throw an exception if test fails
if (JSON_UNLIKELY(not success))
{
- JSON_THROW(other_error::create(501, "unsuccessful: " + Twine(val.dump())));
+ JSON_THROW(other_error::create(501, fmt::format("unsuccessful: {}", val.dump())));
}
break;
@@ -1331,7 +1343,7 @@
{
// op must be "add", "remove", "replace", "move", "copy", or
// "test"
- JSON_THROW(parse_error::create(105, 0, "operation value '" + Twine(op) + "' is invalid"));
+ JSON_THROW(parse_error::create(105, 0, fmt::format("operation value '{}' is invalid", op)));
}
}
}
@@ -1413,7 +1425,7 @@
for (auto it = source.cbegin(); it != source.cend(); ++it)
{
// escape the key name to be used in a JSON patch
- const auto key = json_pointer::escape(it.key());
+ const auto key = json_pointer::escape(std::string{it.key()});
if (target.find(it.key()) != target.end())
{
@@ -1437,7 +1449,7 @@
if (source.find(it.key()) == source.end())
{
// found a key that is not in this -> add it
- const auto key = json_pointer::escape(it.key());
+ const auto key = json_pointer::escape(std::string{it.key()});
result.push_back(
{
{"op", "add"}, {"path", path + "/" + key},
diff --git a/wpiutil/src/main/native/cpp/json_binary_reader.cpp b/wpiutil/src/main/native/cpp/json_binary_reader.cpp
index 9298e92..cee5e39 100644
--- a/wpiutil/src/main/native/cpp/json_binary_reader.cpp
+++ b/wpiutil/src/main/native/cpp/json_binary_reader.cpp
@@ -36,6 +36,7 @@
#include <cmath> // ldexp
+#include "fmt/format.h"
#include "wpi/raw_istream.h"
namespace wpi {
@@ -711,7 +712,7 @@
default: // anything else (0xFF is handled inside the other types)
{
- JSON_THROW(parse_error::create(112, chars_read, "error reading CBOR; last byte: 0x" + Twine::utohexstr(current)));
+ JSON_THROW(parse_error::create(112, chars_read, fmt::format("error reading CBOR; last byte: {:#02x}", current)));
}
}
}
@@ -1034,7 +1035,7 @@
default: // anything else
{
JSON_THROW(parse_error::create(112, chars_read,
- "error reading MessagePack; last byte: 0x" + Twine::utohexstr(current)));
+ fmt::format("error reading MessagePack; last byte: {:#02x}", current)));
}
}
}
@@ -1106,7 +1107,8 @@
default:
{
- JSON_THROW(parse_error::create(113, chars_read, "expected a CBOR string; last byte: 0x" + Twine::utohexstr(current)));
+ JSON_THROW(parse_error::create(113, chars_read,
+ fmt::format("expected a CBOR string; last byte: {:#02x}", current)));
}
}
}
@@ -1172,7 +1174,7 @@
default:
{
JSON_THROW(parse_error::create(113, chars_read,
- "expected a MessagePack string; last byte: 0x" + Twine::utohexstr(current)));
+ fmt::format("expected a MessagePack string; last byte: {:#02x}", current)));
}
}
}
@@ -1200,7 +1202,7 @@
return get_string(get_number<int64_t>());
default:
JSON_THROW(parse_error::create(113, chars_read,
- "expected a UBJSON string; last byte: 0x" + Twine::utohexstr(current)));
+ fmt::format("expected a UBJSON string; last byte: {:#02x}", current)));
}
}
@@ -1220,7 +1222,7 @@
if (current != '#')
{
JSON_THROW(parse_error::create(112, chars_read,
- "expected '#' after UBJSON type information; last byte: 0x" + Twine::utohexstr(current)));
+ fmt::format("expected '#' after UBJSON type information; last byte: {:#02x}", current)));
}
sz = parse_ubjson_internal();
}
@@ -1269,7 +1271,7 @@
if (JSON_UNLIKELY(current > 127))
{
JSON_THROW(parse_error::create(113, chars_read,
- "byte after 'C' must be in range 0x00..0x7F; last byte: 0x" + Twine::utohexstr(current)));
+ fmt::format("byte after 'C' must be in range 0x00..0x7F; last byte: {:#02x}", current)));
}
return std::string(1, static_cast<char>(current));
}
@@ -1285,7 +1287,7 @@
default: // anything else
JSON_THROW(parse_error::create(112, chars_read,
- "error reading UBJSON; last byte: 0x" + Twine::utohexstr(current)));
+ fmt::format("error reading UBJSON; last byte: {:#02x}", current)));
}
}
@@ -1299,7 +1301,7 @@
if (JSON_UNLIKELY(size_and_type.first > result.max_size()))
{
JSON_THROW(out_of_range::create(408,
- "excessive array size: " + Twine(size_and_type.first)));
+ fmt::format("excessive array size: {}", size_and_type.first)));
}
if (size_and_type.second != 0)
@@ -1344,7 +1346,7 @@
if (JSON_UNLIKELY(size_and_type.first > result.max_size()))
{
JSON_THROW(out_of_range::create(408,
- "excessive object size: " + Twine(size_and_type.first)));
+ fmt::format("excessive object size: {}", size_and_type.first)));
}
if (size_and_type.second != 0)
@@ -1382,7 +1384,7 @@
return binary_reader(is).parse_cbor(strict);
}
-json json::from_cbor(ArrayRef<uint8_t> arr, const bool strict)
+json json::from_cbor(span<const uint8_t> arr, const bool strict)
{
raw_mem_istream is(arr);
return from_cbor(is, strict);
@@ -1393,7 +1395,7 @@
return binary_reader(is).parse_msgpack(strict);
}
-json json::from_msgpack(ArrayRef<uint8_t> arr, const bool strict)
+json json::from_msgpack(span<const uint8_t> arr, const bool strict)
{
raw_mem_istream is(arr);
return from_msgpack(is, strict);
@@ -1404,7 +1406,7 @@
return binary_reader(is).parse_ubjson(strict);
}
-json json::from_ubjson(ArrayRef<uint8_t> arr, const bool strict)
+json json::from_ubjson(span<const uint8_t> arr, const bool strict)
{
raw_mem_istream is(arr);
return from_ubjson(is, strict);
diff --git a/wpiutil/src/main/native/cpp/json_binary_writer.cpp b/wpiutil/src/main/native/cpp/json_binary_writer.cpp
index 2c0bbee..db23f8d 100644
--- a/wpiutil/src/main/native/cpp/json_binary_writer.cpp
+++ b/wpiutil/src/main/native/cpp/json_binary_writer.cpp
@@ -34,6 +34,7 @@
#define WPI_JSON_IMPLEMENTATION
#include "wpi/json.h"
+#include "fmt/format.h"
#include "wpi/raw_ostream.h"
namespace wpi {
@@ -75,9 +76,9 @@
const bool use_type, const bool add_prefix = true);
private:
- void write_cbor_string(StringRef str);
+ void write_cbor_string(std::string_view str);
- void write_msgpack_string(StringRef str);
+ void write_msgpack_string(std::string_view str);
/*
@brief write a number to output input
@@ -630,7 +631,7 @@
o << static_cast<CharType>('S');
}
write_number_with_ubjson_prefix(j.m_value.string->size(), true);
- o << j.m_value.string;
+ o << *j.m_value.string;
break;
}
@@ -731,7 +732,7 @@
}
}
-void json::binary_writer::write_cbor_string(StringRef str)
+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();
@@ -766,7 +767,7 @@
o << str;
}
-void json::binary_writer::write_msgpack_string(StringRef 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();
@@ -812,7 +813,7 @@
std::reverse(vec.begin(), vec.end());
}
- o << ArrayRef<uint8_t>(vec.data(), sizeof(NumberType));
+ o << span{vec.data(), sizeof(NumberType)};
}
template<typename NumberType, typename std::enable_if<
@@ -862,7 +863,7 @@
}
else
{
- JSON_THROW(out_of_range::create(407, "number overflow serializing " + Twine(n)));
+ JSON_THROW(out_of_range::create(407, fmt::format("number overflow serializing {}", n)));
}
}
@@ -915,7 +916,7 @@
// LCOV_EXCL_START
else
{
- JSON_THROW(out_of_range::create(407, "number overflow serializing " + Twine(n)));
+ JSON_THROW(out_of_range::create(407, fmt::format("number overflow serializing {}", n)));
}
// LCOV_EXCL_STOP
}
@@ -1003,7 +1004,7 @@
return result;
}
-ArrayRef<uint8_t> json::to_cbor(const json& j, std::vector<uint8_t>& buf)
+span<uint8_t> json::to_cbor(const json& j, std::vector<uint8_t>& buf)
{
buf.clear();
raw_uvector_ostream os(buf);
@@ -1011,7 +1012,7 @@
return os.array();
}
-ArrayRef<uint8_t> json::to_cbor(const json& j, SmallVectorImpl<uint8_t>& buf)
+span<uint8_t> json::to_cbor(const json& j, SmallVectorImpl<uint8_t>& buf)
{
buf.clear();
raw_usvector_ostream os(buf);
@@ -1032,7 +1033,7 @@
return result;
}
-ArrayRef<uint8_t> json::to_msgpack(const json& j, std::vector<uint8_t>& buf)
+span<uint8_t> json::to_msgpack(const json& j, std::vector<uint8_t>& buf)
{
buf.clear();
raw_uvector_ostream os(buf);
@@ -1040,7 +1041,7 @@
return os.array();
}
-ArrayRef<uint8_t> json::to_msgpack(const json& j, SmallVectorImpl<uint8_t>& buf)
+span<uint8_t> json::to_msgpack(const json& j, SmallVectorImpl<uint8_t>& buf)
{
buf.clear();
raw_usvector_ostream os(buf);
@@ -1063,8 +1064,8 @@
return result;
}
-ArrayRef<uint8_t> json::to_ubjson(const json& j, std::vector<uint8_t>& buf,
- const bool use_size, const bool use_type)
+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);
@@ -1072,8 +1073,8 @@
return os.array();
}
-ArrayRef<uint8_t> json::to_ubjson(const json& j, SmallVectorImpl<uint8_t>& buf,
- const bool use_size, const bool use_type)
+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);
diff --git a/wpiutil/src/main/native/cpp/json_parser.cpp b/wpiutil/src/main/native/cpp/json_parser.cpp
index e0d7db3..db1ed11 100644
--- a/wpiutil/src/main/native/cpp/json_parser.cpp
+++ b/wpiutil/src/main/native/cpp/json_parser.cpp
@@ -38,7 +38,7 @@
#include <cmath>
#include <cstdlib>
-#include "wpi/Format.h"
+#include "fmt/format.h"
#include "wpi/SmallString.h"
#include "wpi/raw_istream.h"
#include "wpi/raw_ostream.h"
@@ -336,7 +336,7 @@
}
/// return current string value
- StringRef get_string()
+ std::string_view get_string()
{
return token_buffer;
}
@@ -1407,7 +1407,7 @@
if (c <= '\x1F')
{
// escape control characters
- ss << "<U+" << format_hex_no_prefix(c, 4, true) << '>';
+ ss << fmt::format("<U+{:04X}>", c);
}
else
{
@@ -1605,7 +1605,7 @@
if (keep and keep_tag and not value.is_discarded())
{
- result.m_value.object->try_emplace(StringRef(key.data(), key.size()), std::move(value));
+ result.m_value.object->try_emplace(std::string_view(key.data(), key.size()), std::move(value));
}
// comma -> next value
@@ -1757,8 +1757,8 @@
{
if (allow_exceptions)
{
- JSON_THROW(out_of_range::create(406, "number overflow parsing '" +
- Twine(m_lexer.get_token_string()) + "'"));
+ JSON_THROW(out_of_range::create(406,
+ fmt::format("number overflow parsing '{}'", m_lexer.get_token_string())));
}
expect(token_type::uninitialized);
}
@@ -1917,15 +1917,15 @@
JSON_THROW(parse_error::create(101, m_lexer.get_position(), error_msg));
}
-json json::parse(StringRef s,
+json json::parse(std::string_view s,
const parser_callback_t cb,
const bool allow_exceptions)
{
- raw_mem_istream is(makeArrayRef(s.data(), s.size()));
+ raw_mem_istream is(span<const char>(s.data(), s.size()));
return parse(is, cb, allow_exceptions);
}
-json json::parse(ArrayRef<uint8_t> arr,
+json json::parse(span<const uint8_t> arr,
const parser_callback_t cb,
const bool allow_exceptions)
{
@@ -1942,13 +1942,13 @@
return result;
}
-bool json::accept(StringRef s)
+bool json::accept(std::string_view s)
{
- raw_mem_istream is(makeArrayRef(s.data(), s.size()));
+ raw_mem_istream is(span<const char>(s.data(), s.size()));
return parser(is).accept(true);
}
-bool json::accept(ArrayRef<uint8_t> arr)
+bool json::accept(span<const uint8_t> arr)
{
raw_mem_istream is(arr);
return parser(is).accept(true);
diff --git a/wpiutil/src/main/native/cpp/json_pointer.cpp b/wpiutil/src/main/native/cpp/json_pointer.cpp
index 9cb1ec4..51548b3 100644
--- a/wpiutil/src/main/native/cpp/json_pointer.cpp
+++ b/wpiutil/src/main/native/cpp/json_pointer.cpp
@@ -36,7 +36,9 @@
#include <numeric> // accumulate
+#include "fmt/format.h"
#include "wpi/SmallString.h"
+#include "wpi/StringExtras.h"
namespace wpi {
@@ -50,17 +52,16 @@
});
}
-int json_pointer::array_index(const Twine& s)
+int json_pointer::array_index(std::string_view s)
{
- SmallString<128> buf;
- StringRef str = s.toNullTerminatedStringRef(buf);
+ SmallString<128> str{s};
std::size_t processed_chars = 0;
- const int res = std::stoi(str, &processed_chars);
+ 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, "unresolved reference token '" + Twine(s) + "'"));
+ JSON_THROW(detail::out_of_range::create(404, fmt::format("unresolved reference token '{}'", s)));
}
return res;
@@ -108,7 +109,7 @@
}
JSON_CATCH(std::invalid_argument&)
{
- JSON_THROW(detail::parse_error::create(109, 0, "array index '" + Twine(reference_token) + "' is not a number"));
+ JSON_THROW(detail::parse_error::create(109, 0, fmt::format("array index '{}' is not a number", reference_token)));
}
break;
}
@@ -164,8 +165,7 @@
if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))
{
JSON_THROW(detail::parse_error::create(106, 0,
- "array index '" + Twine(reference_token) +
- "' must not begin with '0'"));
+ fmt::format("array index '{}' must not begin with '0'", reference_token)));
}
if (reference_token == "-")
@@ -183,14 +183,16 @@
}
JSON_CATCH(std::invalid_argument&)
{
- JSON_THROW(detail::parse_error::create(109, 0, "array index '" + Twine(reference_token) + "' is not a number"));
+ 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, "unresolved reference token '" + Twine(reference_token) + "'"));
+ JSON_THROW(detail::out_of_range::create(404,
+ fmt::format("unresolved reference token '{}'", reference_token)));
}
}
@@ -217,16 +219,14 @@
{
// "-" always fails the range check
JSON_THROW(detail::out_of_range::create(402,
- "array index '-' (" + Twine(ptr->m_value.array->size()) +
- ") is out of range"));
+ 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,
- "array index '" + Twine(reference_token) +
- "' must not begin with '0'"));
+ fmt::format("array index '{}' must not begin with '0'", reference_token)));
}
// note: at performs range check
@@ -236,13 +236,15 @@
}
JSON_CATCH(std::invalid_argument&)
{
- JSON_THROW(detail::parse_error::create(109, 0, "array index '" + Twine(reference_token) + "' is not a number"));
+ 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, "unresolved reference token '" + Twine(reference_token) + "'"));
+ JSON_THROW(detail::out_of_range::create(404,
+ fmt::format("unresolved reference token '{}'", reference_token)));
}
}
@@ -269,16 +271,14 @@
{
// "-" cannot be used for const access
JSON_THROW(detail::out_of_range::create(402,
- "array index '-' (" + Twine(ptr->m_value.array->size()) +
- ") is out of range"));
+ 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,
- "array index '" + Twine(reference_token) +
- "' must not begin with '0'"));
+ fmt::format("array index '{}' must not begin with '0'", reference_token)));
}
// use unchecked array access
@@ -289,13 +289,15 @@
}
JSON_CATCH(std::invalid_argument&)
{
- JSON_THROW(detail::parse_error::create(109, 0, "array index '" + Twine(reference_token) + "' is not a number"));
+ 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, "unresolved reference token '" + Twine(reference_token) + "'"));
+ JSON_THROW(detail::out_of_range::create(404,
+ fmt::format("unresolved reference token '{}'", reference_token)));
}
}
@@ -322,16 +324,14 @@
{
// "-" always fails the range check
JSON_THROW(detail::out_of_range::create(402,
- "array index '-' (" + Twine(ptr->m_value.array->size()) +
- ") is out of range"));
+ 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,
- "array index '" + Twine(reference_token) +
- "' must not begin with '0'"));
+ fmt::format("array index '{}' must not begin with '0'", reference_token)));
}
// note: at performs range check
@@ -341,23 +341,23 @@
}
JSON_CATCH(std::invalid_argument&)
{
- JSON_THROW(detail::parse_error::create(109, 0, "array index '" + Twine(reference_token) + "' is not a number"));
+ 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, "unresolved reference token '" + Twine(reference_token) + "'"));
+ JSON_THROW(detail::out_of_range::create(404,
+ fmt::format("unresolved reference token '{}'", reference_token)));
}
}
return *ptr;
}
-std::vector<std::string> json_pointer::split(const Twine& reference_string)
+std::vector<std::string> json_pointer::split(std::string_view ref_str)
{
- SmallString<128> ref_str_buf;
- StringRef ref_str = reference_string.toStringRef(ref_str_buf);
std::vector<std::string> result;
// special case: empty reference string -> no reference tokens
@@ -370,8 +370,7 @@
if (JSON_UNLIKELY(ref_str[0] != '/'))
{
JSON_THROW(detail::parse_error::create(107, 1,
- "JSON pointer must be empty or begin with '/' - was: '" +
- Twine(ref_str) + "'"));
+ fmt::format("JSON pointer must be empty or begin with '/' - was: '{}'", ref_str)));
}
// extract the reference tokens:
@@ -392,11 +391,11 @@
{
// use the text between the beginning of the reference token
// (start) and the last slash (slash).
- auto reference_token = ref_str.slice(start, 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 != StringRef::npos;
+ pos != std::string_view::npos;
pos = reference_token.find_first_of('~', pos + 1))
{
assert(reference_token[pos] == '~');
@@ -411,7 +410,7 @@
}
// finally, store the reference token
- std::string ref_tok = reference_token;
+ std::string ref_tok{reference_token};
unescape(ref_tok);
result.emplace_back(std::move(ref_tok));
}
@@ -444,7 +443,7 @@
replace_substring(s, "~0", "~");
}
-void json_pointer::flatten(const Twine& reference_string,
+void json_pointer::flatten(std::string_view reference_string,
const json& value,
json& result)
{
@@ -455,15 +454,14 @@
if (value.m_value.array->empty())
{
// flatten empty array as null
- SmallString<64> buf;
- result[reference_string.toStringRef(buf)] = nullptr;
+ 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(reference_string + "/" + Twine(i),
+ flatten(fmt::format("{}/{}", reference_string, i),
value.m_value.array->operator[](i), result);
}
}
@@ -475,15 +473,14 @@
if (value.m_value.object->empty())
{
// flatten empty object as null
- SmallString<64> buf;
- result[reference_string.toStringRef(buf)] = nullptr;
+ result[reference_string] = nullptr;
}
else
{
// iterate object and use keys as reference string
for (const auto& element : *value.m_value.object)
{
- flatten(reference_string + "/" + Twine(escape(element.first())), element.second, result);
+ flatten(fmt::format("{}/{}", reference_string, escape(std::string{element.first()})), element.second, result);
}
}
break;
@@ -492,8 +489,7 @@
default:
{
// add primitive value with its reference string
- SmallString<64> buf;
- result[reference_string.toStringRef(buf)] = value;
+ result[reference_string] = value;
break;
}
}
diff --git a/wpiutil/src/main/native/cpp/json_serializer.cpp b/wpiutil/src/main/native/cpp/json_serializer.cpp
index 8ab92b1..1101f66 100644
--- a/wpiutil/src/main/native/cpp/json_serializer.cpp
+++ b/wpiutil/src/main/native/cpp/json_serializer.cpp
@@ -34,12 +34,11 @@
#define WPI_JSON_IMPLEMENTATION
#include "wpi/json.h"
-#include "wpi/Format.h"
+#include "fmt/format.h"
#include "wpi/SmallString.h"
-#include "wpi/StringExtras.h"
#include "wpi/raw_os_ostream.h"
-#include "json_serializer.h"
+#include "wpi/json_serializer.h"
namespace wpi {
@@ -1332,7 +1331,7 @@
}
}
-void json::serializer::dump_escaped(StringRef s, const bool ensure_ascii)
+void json::serializer::dump_escaped(std::string_view s, const bool ensure_ascii)
{
uint32_t codepoint;
uint8_t state = UTF8_ACCEPT;
@@ -1397,12 +1396,12 @@
{
if (codepoint <= 0xFFFF)
{
- o << '\\' << 'u' << format_hex_no_prefix(codepoint, 4);
+ o << fmt::format("\\u{:04x}", codepoint);
}
else
{
- o << '\\' << 'u' << format_hex_no_prefix(0xD7C0 + (codepoint >> 10), 4);
- o << '\\' << 'u' << format_hex_no_prefix(0xDC00 + (codepoint & 0x3FF), 4);
+ o << fmt::format("\\u{:04x}", 0xD7C0 + (codepoint >> 10));
+ o << fmt::format("\\u{:04x}", 0xDC00 + (codepoint & 0x3FF));
}
}
else
@@ -1419,7 +1418,7 @@
case UTF8_REJECT: // decode found invalid UTF-8 byte
{
- JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + Twine(i) + ": 0x" + Twine::utohexstr(byte)));
+ 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
@@ -1437,7 +1436,7 @@
if (JSON_UNLIKELY(state != UTF8_ACCEPT))
{
// we finish reading, but do not accept: string was incomplete
- JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + Twine::utohexstr(static_cast<uint8_t>(s.back()))));
+ JSON_THROW(type_error::create(316, fmt::format("incomplete UTF-8 string; last byte: {:#02x}", s.back())));
}
}
diff --git a/wpiutil/src/main/native/cpp/leb128.cpp b/wpiutil/src/main/native/cpp/leb128.cpp
index 202ee9a..9657883 100644
--- a/wpiutil/src/main/native/cpp/leb128.cpp
+++ b/wpiutil/src/main/native/cpp/leb128.cpp
@@ -1,13 +1,13 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2015-2019 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/leb128.h"
+#include "wpi/SpanExtras.h"
#include "wpi/raw_istream.h"
+#include "wpi/raw_ostream.h"
+#include "wpi/span.h"
namespace wpi {
@@ -24,11 +24,12 @@
size_t count = 0;
do {
- unsigned char byte = val & 0x7f;
+ uint8_t byte = val & 0x7f;
val >>= 7;
- if (val != 0)
+ if (val != 0) {
byte |= 0x80; // mark this byte to show that more bytes will follow
+ }
dest.push_back(byte);
count++;
@@ -37,6 +38,19 @@
return count;
}
+void WriteUleb128(raw_ostream& os, uint64_t val) {
+ do {
+ uint8_t byte = val & 0x7f;
+ val >>= 7;
+
+ if (val != 0) {
+ byte |= 0x80; // mark this byte to show that more bytes will follow
+ }
+
+ os << byte;
+ } while (val != 0);
+}
+
uint64_t ReadUleb128(const char* addr, uint64_t* ret) {
uint64_t result = 0;
int shift = 0;
@@ -47,10 +61,12 @@
addr++;
count++;
- result |= (byte & 0x7f) << shift;
+ result |= (byte & 0x7fULL) << shift;
shift += 7;
- if (!(byte & 0x80)) break;
+ if (!(byte & 0x80)) {
+ break;
+ }
}
*ret = result;
@@ -65,12 +81,16 @@
while (1) {
unsigned char byte;
is.read(reinterpret_cast<char*>(&byte), 1);
- if (is.has_error()) return false;
+ if (is.has_error()) {
+ return false;
+ }
- result |= (byte & 0x7f) << shift;
+ result |= (byte & 0x7fULL) << shift;
shift += 7;
- if (!(byte & 0x80)) break;
+ if (!(byte & 0x80)) {
+ break;
+ }
}
*ret = result;
@@ -78,4 +98,22 @@
return true;
}
+std::optional<uint64_t> Uleb128Reader::ReadOne(span<const uint8_t>* in) {
+ while (!in->empty()) {
+ uint8_t byte = in->front();
+ *in = wpi::drop_front(*in);
+
+ m_result |= (byte & 0x7fULL) << m_shift;
+ m_shift += 7;
+
+ if (!(byte & 0x80)) {
+ uint64_t result = m_result;
+ m_result = 0;
+ m_shift = 0;
+ return result;
+ }
+ }
+ return std::nullopt;
+}
+
} // namespace wpi
diff --git a/wpiutil/src/main/native/cpp/llvm/ConvertUTF.cpp b/wpiutil/src/main/native/cpp/llvm/ConvertUTF.cpp
index abe3744..3050e63 100644
--- a/wpiutil/src/main/native/cpp/llvm/ConvertUTF.cpp
+++ b/wpiutil/src/main/native/cpp/llvm/ConvertUTF.cpp
@@ -52,6 +52,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.
@@ -734,6 +739,96 @@
--------------------------------------------------------------------- */
+#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.set_size(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.set_size(len);
+
+ // Now do the actual conversion.
+ len = ::WideCharToMultiByte(codepage, 0, utf16, utf16_len, converted.data(),
+ converted.size(), NULL, NULL);
+
+ if (len == 0) {
+ return mapWindowsError(::GetLastError());
+ }
+ }
+
+ // Make the new string null terminated.
+ converted.push_back(0);
+ converted.pop_back();
+
+ return std::error_code();
+}
+
+std::error_code UTF16ToUTF8(const wchar_t *utf16, size_t utf16_len,
+ wpi::SmallVectorImpl<char> &utf8) {
+ return UTF16ToCodePage(CP_UTF8, utf16, utf16_len, utf8);
+}
+
+std::error_code UTF16ToCurCP(const wchar_t *utf16, size_t utf16_len,
+ wpi::SmallVectorImpl<char> &curcp) {
+ return UTF16ToCodePage(CP_ACP, utf16, utf16_len, curcp);
+}
+
+} // end namespace windows
+} // end namespace sys
+
+#endif // _WIN32
+
} // namespace llvm
ConvertUTF_RESTORE_WARNINGS
diff --git a/wpiutil/src/main/native/cpp/llvm/ConvertUTFWrapper.cpp b/wpiutil/src/main/native/cpp/llvm/ConvertUTFWrapper.cpp
index 3402988..db190d0 100644
--- a/wpiutil/src/main/native/cpp/llvm/ConvertUTFWrapper.cpp
+++ b/wpiutil/src/main/native/cpp/llvm/ConvertUTFWrapper.cpp
@@ -8,6 +8,7 @@
//===----------------------------------------------------------------------===//
#include "wpi/ConvertUTF.h"
+#include "wpi/SmallVector.h"
#include <string>
#include <vector>
@@ -28,13 +29,13 @@
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<UTF16> SrcUTF16,
+bool convertUTF16ToUTF8String(span<const UTF16> SrcUTF16,
SmallVectorImpl<char> &DstUTF8) {
assert(DstUTF8.empty());
@@ -80,7 +81,7 @@
return true;
}
-bool convertUTF8ToUTF16String(StringRef SrcUTF8,
+bool convertUTF8ToUTF16String(std::string_view SrcUTF8,
SmallVectorImpl<UTF16> &DstUTF16) {
assert(DstUTF16.empty());
@@ -91,8 +92,8 @@
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
diff --git a/wpiutil/src/main/native/cpp/llvm/Error.cpp b/wpiutil/src/main/native/cpp/llvm/Error.cpp
deleted file mode 100644
index 7bf9c5f..0000000
--- a/wpiutil/src/main/native/cpp/llvm/Error.cpp
+++ /dev/null
@@ -1,138 +0,0 @@
-//===----- lib/Support/Error.cpp - Error and associated utilities ---------===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-
-#include "wpi/Error.h"
-#include "wpi/Twine.h"
-#include "wpi/ErrorHandling.h"
-#include "wpi/ManagedStatic.h"
-#include <system_error>
-
-using namespace wpi;
-
-namespace {
-
- enum class ErrorErrorCode : int {
- MultipleErrors = 1,
- FileError,
- InconvertibleError
- };
-
- // FIXME: This class is only here to support the transition to wpi::Error. It
- // will be removed once this transition is complete. Clients should prefer to
- // deal with the Error value directly, rather than converting to error_code.
- class ErrorErrorCategory : public std::error_category {
- public:
- const char *name() const noexcept override { return "Error"; }
-
- std::string message(int condition) const override {
- switch (static_cast<ErrorErrorCode>(condition)) {
- case ErrorErrorCode::MultipleErrors:
- return "Multiple errors";
- case ErrorErrorCode::InconvertibleError:
- return "Inconvertible error value. An error has occurred that could "
- "not be converted to a known std::error_code. Please file a "
- "bug.";
- case ErrorErrorCode::FileError:
- return "A file error occurred.";
- }
- wpi_unreachable("Unhandled error code");
- }
- };
-
-}
-
-static ManagedStatic<ErrorErrorCategory> ErrorErrorCat;
-
-namespace wpi {
-
-void ErrorInfoBase::anchor() {}
-char ErrorInfoBase::ID = 0;
-char ErrorList::ID = 0;
-void ECError::anchor() {}
-char ECError::ID = 0;
-char StringError::ID = 0;
-char FileError::ID = 0;
-
-void logAllUnhandledErrors(Error E, raw_ostream &OS, Twine ErrorBanner) {
- if (!E)
- return;
- OS << ErrorBanner;
- handleAllErrors(std::move(E), [&](const ErrorInfoBase &EI) {
- EI.log(OS);
- OS << "\n";
- });
-}
-
-
-std::error_code ErrorList::convertToErrorCode() const {
- return std::error_code(static_cast<int>(ErrorErrorCode::MultipleErrors),
- *ErrorErrorCat);
-}
-
-std::error_code inconvertibleErrorCode() {
- return std::error_code(static_cast<int>(ErrorErrorCode::InconvertibleError),
- *ErrorErrorCat);
-}
-
-std::error_code FileError::convertToErrorCode() const {
- return std::error_code(static_cast<int>(ErrorErrorCode::FileError),
- *ErrorErrorCat);
-}
-
-Error errorCodeToError(std::error_code EC) {
- if (!EC)
- return Error::success();
- return Error(std::make_unique<ECError>(ECError(EC)));
-}
-
-std::error_code errorToErrorCode(Error Err) {
- std::error_code EC;
- handleAllErrors(std::move(Err), [&](const ErrorInfoBase &EI) {
- EC = EI.convertToErrorCode();
- });
- if (EC == inconvertibleErrorCode())
- report_fatal_error(EC.message());
- return EC;
-}
-
-StringError::StringError(std::error_code EC, const Twine &S)
- : Msg(S.str()), EC(EC) {}
-
-StringError::StringError(const Twine &S, std::error_code EC)
- : Msg(S.str()), EC(EC), PrintMsgOnly(true) {}
-
-void StringError::log(raw_ostream &OS) const {
- if (PrintMsgOnly) {
- OS << Msg;
- } else {
- OS << EC.message();
- if (!Msg.empty())
- OS << (" " + Msg);
- }
-}
-
-std::error_code StringError::convertToErrorCode() const {
- return EC;
-}
-
-Error createStringError(std::error_code EC, char const *Msg) {
- return make_error<StringError>(Msg, EC);
-}
-
-void report_fatal_error(Error Err, bool GenCrashDiag) {
- assert(Err && "report_fatal_error called with success value");
- std::string ErrMsg;
- {
- raw_string_ostream ErrStream(ErrMsg);
- logAllUnhandledErrors(std::move(Err), ErrStream);
- }
- report_fatal_error(ErrMsg);
-}
-
-} // end namespace wpi
diff --git a/wpiutil/src/main/native/cpp/llvm/ErrorHandling.cpp b/wpiutil/src/main/native/cpp/llvm/ErrorHandling.cpp
index ef79456..22ae636 100644
--- a/wpiutil/src/main/native/cpp/llvm/ErrorHandling.cpp
+++ b/wpiutil/src/main/native/cpp/llvm/ErrorHandling.cpp
@@ -14,11 +14,10 @@
#include "wpi/ErrorHandling.h"
#include "wpi/SmallVector.h"
-#include "wpi/Twine.h"
-#include "wpi/Error.h"
#include "wpi/WindowsError.h"
-#include "wpi/raw_ostream.h"
+#include "fmt/format.h"
#include <cassert>
+#include <cstdio>
#include <cstdlib>
#include <mutex>
#include <new>
@@ -68,18 +67,14 @@
}
void wpi::report_fatal_error(const char *Reason, bool GenCrashDiag) {
- report_fatal_error(Twine(Reason), GenCrashDiag);
+ report_fatal_error(std::string_view(Reason), GenCrashDiag);
}
void wpi::report_fatal_error(const std::string &Reason, bool GenCrashDiag) {
- report_fatal_error(Twine(Reason), GenCrashDiag);
+ report_fatal_error(std::string_view(Reason), GenCrashDiag);
}
-void wpi::report_fatal_error(StringRef Reason, bool GenCrashDiag) {
- report_fatal_error(Twine(Reason), GenCrashDiag);
-}
-
-void wpi::report_fatal_error(const Twine &Reason, bool GenCrashDiag) {
+void wpi::report_fatal_error(std::string_view Reason, bool GenCrashDiag) {
wpi::fatal_error_handler_t handler = nullptr;
void* handlerData = nullptr;
{
@@ -91,21 +86,9 @@
}
if (handler) {
- handler(handlerData, Reason.str(), GenCrashDiag);
+ handler(handlerData, std::string{Reason}, GenCrashDiag);
} else {
- // Blast the result out to stderr. We don't try hard to make sure this
- // succeeds (e.g. handling EINTR) and we can't use errs() here because
- // raw ostreams can call report_fatal_error.
- SmallVector<char, 64> Buffer;
- raw_svector_ostream OS(Buffer);
- OS << "LLVM ERROR: " << Reason << "\n";
- StringRef MessageStr = OS.str();
-#ifdef _WIN32
- int written = ::_write(2, MessageStr.data(), MessageStr.size());
-#else
- ssize_t written = ::write(2, MessageStr.data(), MessageStr.size());
-#endif
- (void)written; // If something went wrong, we deliberately just give up.
+ fmt::print(stderr, "LLVM ERROR: {}\n", Reason);
}
exit(1);
@@ -185,11 +168,11 @@
// wpi_unreachable is intended to be used to indicate "impossible"
// situations, and not legitimate runtime errors.
if (msg)
- errs() << msg << "\n";
- errs() << "UNREACHABLE executed";
+ fmt::print(stderr, "{}\n", msg);
+ std::fputs("UNREACHABLE executed", stderr);
if (file)
- errs() << " at " << file << ":" << line;
- errs() << "!\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/wpiutil/src/main/native/cpp/llvm/NativeFormatting.cpp b/wpiutil/src/main/native/cpp/llvm/NativeFormatting.cpp
deleted file mode 100644
index 985e269..0000000
--- a/wpiutil/src/main/native/cpp/llvm/NativeFormatting.cpp
+++ /dev/null
@@ -1,264 +0,0 @@
-//===- NativeFormatting.cpp - Low level formatting helpers -------*- C++-*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-
-#include "wpi/NativeFormatting.h"
-
-#include "wpi/ArrayRef.h"
-#include "wpi/SmallString.h"
-#include "wpi/StringExtras.h"
-#include "wpi/Format.h"
-
-#include <float.h>
-
-using namespace wpi;
-
-template<typename T, std::size_t N>
-static int format_to_buffer(T Value, char (&Buffer)[N]) {
- char *EndPtr = std::end(Buffer);
- char *CurPtr = EndPtr;
-
- do {
- *--CurPtr = '0' + char(Value % 10);
- Value /= 10;
- } while (Value);
- return EndPtr - CurPtr;
-}
-
-static void writeWithCommas(raw_ostream &S, ArrayRef<char> Buffer) {
- assert(!Buffer.empty());
-
- ArrayRef<char> ThisGroup;
- int InitialDigits = ((Buffer.size() - 1) % 3) + 1;
- ThisGroup = Buffer.take_front(InitialDigits);
- S.write(ThisGroup.data(), ThisGroup.size());
-
- Buffer = Buffer.drop_front(InitialDigits);
- assert(Buffer.size() % 3 == 0);
- while (!Buffer.empty()) {
- S << ',';
- ThisGroup = Buffer.take_front(3);
- S.write(ThisGroup.data(), 3);
- Buffer = Buffer.drop_front(3);
- }
-}
-
-template <typename T>
-static void write_unsigned_impl(raw_ostream &S, T N, size_t MinDigits,
- IntegerStyle Style, bool IsNegative) {
- static_assert(std::is_unsigned<T>::value, "Value is not unsigned!");
-
- char NumberBuffer[128];
- std::memset(NumberBuffer, '0', sizeof(NumberBuffer));
-
- size_t Len = 0;
- Len = format_to_buffer(N, NumberBuffer);
-
- if (IsNegative)
- S << '-';
-
- if (Len < MinDigits && Style != IntegerStyle::Number) {
- for (size_t I = Len; I < MinDigits; ++I)
- S << '0';
- }
-
- if (Style == IntegerStyle::Number) {
- writeWithCommas(S, ArrayRef<char>(std::end(NumberBuffer) - Len, Len));
- } else {
- S.write(std::end(NumberBuffer) - Len, Len);
- }
-}
-
-template <typename T>
-static void write_unsigned(raw_ostream &S, T N, size_t MinDigits,
- IntegerStyle Style, bool IsNegative = false) {
- // Output using 32-bit div/mod if possible.
- if (N == static_cast<uint32_t>(N))
- write_unsigned_impl(S, static_cast<uint32_t>(N), MinDigits, Style,
- IsNegative);
- else
- write_unsigned_impl(S, N, MinDigits, Style, IsNegative);
-}
-
-template <typename T>
-static void write_signed(raw_ostream &S, T N, size_t MinDigits,
- IntegerStyle Style) {
- static_assert(std::is_signed<T>::value, "Value is not signed!");
-
- using UnsignedT = typename std::make_unsigned<T>::type;
-
- if (N >= 0) {
- write_unsigned(S, static_cast<UnsignedT>(N), MinDigits, Style);
- return;
- }
-
- UnsignedT UN = -(UnsignedT)N;
- write_unsigned(S, UN, MinDigits, Style, true);
-}
-
-void wpi::write_integer(raw_ostream &S, unsigned int N, size_t MinDigits,
- IntegerStyle Style) {
- write_unsigned(S, N, MinDigits, Style);
-}
-
-void wpi::write_integer(raw_ostream &S, int N, size_t MinDigits,
- IntegerStyle Style) {
- write_signed(S, N, MinDigits, Style);
-}
-
-void wpi::write_integer(raw_ostream &S, unsigned long N, size_t MinDigits,
- IntegerStyle Style) {
- write_unsigned(S, N, MinDigits, Style);
-}
-
-void wpi::write_integer(raw_ostream &S, long N, size_t MinDigits,
- IntegerStyle Style) {
- write_signed(S, N, MinDigits, Style);
-}
-
-void wpi::write_integer(raw_ostream &S, unsigned long long N, size_t MinDigits,
- IntegerStyle Style) {
- write_unsigned(S, N, MinDigits, Style);
-}
-
-void wpi::write_integer(raw_ostream &S, long long N, size_t MinDigits,
- IntegerStyle Style) {
- write_signed(S, N, MinDigits, Style);
-}
-
-void wpi::write_hex(raw_ostream &S, uint64_t N, HexPrintStyle Style,
- std::optional<size_t> Width) {
- const size_t kMaxWidth = 128u;
-
- size_t W = std::min(kMaxWidth, Width.value_or(0u));
-
- unsigned Nibbles = (64 - countLeadingZeros(N) + 3) / 4;
- bool Prefix = (Style == HexPrintStyle::PrefixLower ||
- Style == HexPrintStyle::PrefixUpper);
- bool Upper =
- (Style == HexPrintStyle::Upper || Style == HexPrintStyle::PrefixUpper);
- unsigned PrefixChars = Prefix ? 2 : 0;
- unsigned NumChars =
- std::max(static_cast<unsigned>(W), std::max(1u, Nibbles) + PrefixChars);
-
- char NumberBuffer[kMaxWidth];
- ::memset(NumberBuffer, '0', wpi::array_lengthof(NumberBuffer));
- if (Prefix)
- NumberBuffer[1] = 'x';
- char *EndPtr = NumberBuffer + NumChars;
- char *CurPtr = EndPtr;
- while (N) {
- unsigned char x = static_cast<unsigned char>(N) % 16;
- *--CurPtr = hexdigit(x, !Upper);
- N /= 16;
- }
-
- S.write(NumberBuffer, NumChars);
-}
-
-void wpi::write_double(raw_ostream &S, double N, FloatStyle Style,
- std::optional<size_t> Precision) {
- size_t Prec = Precision.value_or(getDefaultPrecision(Style));
-
- if (std::isnan(N)) {
- S << "nan";
- return;
- } else if (std::isinf(N)) {
- S << "INF";
- return;
- }
-
- char Letter;
- if (Style == FloatStyle::Exponent)
- Letter = 'e';
- else if (Style == FloatStyle::ExponentUpper)
- Letter = 'E';
- else
- Letter = 'f';
-
- SmallString<8> Spec;
- wpi::raw_svector_ostream Out(Spec);
- Out << "%." << Prec << Letter;
-
- if (Style == FloatStyle::Exponent || Style == FloatStyle::ExponentUpper) {
-#ifdef _WIN32
-// On MSVCRT and compatible, output of %e is incompatible to Posix
-// by default. Number of exponent digits should be at least 2. "%+03d"
-// FIXME: Implement our formatter to here or Support/Format.h!
-#if defined(__MINGW32__)
- // FIXME: It should be generic to C++11.
- if (N == 0.0 && std::signbit(N)) {
- char NegativeZero[] = "-0.000000e+00";
- if (Style == FloatStyle::ExponentUpper)
- NegativeZero[strlen(NegativeZero) - 4] = 'E';
- S << NegativeZero;
- return;
- }
-#else
- int fpcl = _fpclass(N);
-
- // negative zero
- if (fpcl == _FPCLASS_NZ) {
- char NegativeZero[] = "-0.000000e+00";
- if (Style == FloatStyle::ExponentUpper)
- NegativeZero[strlen(NegativeZero) - 4] = 'E';
- S << NegativeZero;
- return;
- }
-#endif
-
- char buf[32];
- unsigned len;
- len = format(Spec.c_str(), N).snprint(buf, sizeof(buf));
- if (len <= sizeof(buf) - 2) {
- if (len >= 5 && (buf[len - 5] == 'e' || buf[len - 5] == 'E') &&
- buf[len - 3] == '0') {
- int cs = buf[len - 4];
- if (cs == '+' || cs == '-') {
- int c1 = buf[len - 2];
- int c0 = buf[len - 1];
- if (isdigit(static_cast<unsigned char>(c1)) &&
- isdigit(static_cast<unsigned char>(c0))) {
- // Trim leading '0': "...e+012" -> "...e+12\0"
- buf[len - 3] = c1;
- buf[len - 2] = c0;
- buf[--len] = 0;
- }
- }
- }
- S << buf;
- return;
- }
-#endif
- }
-
- if (Style == FloatStyle::Percent)
- N *= 100.0;
-
- char Buf[32];
- format(Spec.c_str(), N).snprint(Buf, sizeof(Buf));
- S << Buf;
- if (Style == FloatStyle::Percent)
- S << '%';
-}
-
-bool wpi::isPrefixedHexStyle(HexPrintStyle S) {
- return (S == HexPrintStyle::PrefixLower || S == HexPrintStyle::PrefixUpper);
-}
-
-size_t wpi::getDefaultPrecision(FloatStyle Style) {
- switch (Style) {
- case FloatStyle::Exponent:
- case FloatStyle::ExponentUpper:
- return 6; // Number of decimal places.
- case FloatStyle::Fixed:
- case FloatStyle::Percent:
- return 2; // Number of decimal places.
- }
- LLVM_BUILTIN_UNREACHABLE;
-}
diff --git a/wpiutil/src/main/native/cpp/llvm/Path.cpp b/wpiutil/src/main/native/cpp/llvm/Path.cpp
deleted file mode 100644
index 49f92d3..0000000
--- a/wpiutil/src/main/native/cpp/llvm/Path.cpp
+++ /dev/null
@@ -1,833 +0,0 @@
-//===-- Path.cpp - Implement OS Path Concept ------------------------------===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file implements the operating system Path API.
-//
-//===----------------------------------------------------------------------===//
-
-#include "wpi/Path.h"
-#include "wpi/ArrayRef.h"
-#include "wpi/Endian.h"
-#include "wpi/Errc.h"
-#include "wpi/ErrorHandling.h"
-#include "wpi/FileSystem.h"
-#include "wpi/SmallString.h"
-#include <cctype>
-#include <cstring>
-
-#if !defined(_MSC_VER) && !defined(__MINGW32__)
-#include <unistd.h>
-#else
-#include <io.h>
-#endif
-
-using namespace wpi;
-using namespace wpi::support::endian;
-
-namespace {
- using wpi::StringRef;
- using wpi::sys::path::is_separator;
- using wpi::sys::path::Style;
-
- inline Style real_style(Style style) {
-#ifdef _WIN32
- return (style == Style::posix) ? Style::posix : Style::windows;
-#else
- return (style == Style::windows) ? Style::windows : Style::posix;
-#endif
- }
-
- inline const char *separators(Style style) {
- if (real_style(style) == Style::windows)
- return "\\/";
- return "/";
- }
-
- inline char preferred_separator(Style style) {
- if (real_style(style) == Style::windows)
- return '\\';
- return '/';
- }
-
- StringRef find_first_component(StringRef path, Style style) {
- // Look for this first component in the following order.
- // * empty (in this case we return an empty string)
- // * either C: or {//,\\}net.
- // * {/,\}
- // * {file,directory}name
-
- if (path.empty())
- return path;
-
- if (real_style(style) == Style::windows) {
- // C:
- if (path.size() >= 2 &&
- std::isalpha(static_cast<unsigned char>(path[0])) && path[1] == ':')
- return path.substr(0, 2);
- }
-
- // //net
- if ((path.size() > 2) && is_separator(path[0], style) &&
- path[0] == path[1] && !is_separator(path[2], style)) {
- // Find the next directory separator.
- size_t end = path.find_first_of(separators(style), 2);
- return path.substr(0, end);
- }
-
- // {/,\}
- if (is_separator(path[0], style))
- return path.substr(0, 1);
-
- // * {file,directory}name
- size_t end = path.find_first_of(separators(style));
- return path.substr(0, end);
- }
-
- // Returns the first character of the filename in str. For paths ending in
- // '/', it returns the position of the '/'.
- size_t filename_pos(StringRef str, Style style) {
- if (str.size() > 0 && is_separator(str[str.size() - 1], style))
- return str.size() - 1;
-
- size_t pos = str.find_last_of(separators(style), str.size() - 1);
-
- if (real_style(style) == Style::windows) {
- if (pos == StringRef::npos)
- pos = str.find_last_of(':', str.size() - 2);
- }
-
- if (pos == StringRef::npos || (pos == 1 && is_separator(str[0], style)))
- return 0;
-
- return pos + 1;
- }
-
- // Returns the position of the root directory in str. If there is no root
- // directory in str, it returns StringRef::npos.
- size_t root_dir_start(StringRef str, Style style) {
- // case "c:/"
- if (real_style(style) == Style::windows) {
- if (str.size() > 2 && str[1] == ':' && is_separator(str[2], style))
- return 2;
- }
-
- // case "//net"
- if (str.size() > 3 && is_separator(str[0], style) && str[0] == str[1] &&
- !is_separator(str[2], style)) {
- return str.find_first_of(separators(style), 2);
- }
-
- // case "/"
- if (str.size() > 0 && is_separator(str[0], style))
- return 0;
-
- return StringRef::npos;
- }
-
- // Returns the position past the end of the "parent path" of path. The parent
- // path will not end in '/', unless the parent is the root directory. If the
- // path has no parent, 0 is returned.
- size_t parent_path_end(StringRef path, Style style) {
- size_t end_pos = filename_pos(path, style);
-
- bool filename_was_sep =
- path.size() > 0 && is_separator(path[end_pos], style);
-
- // Skip separators until we reach root dir (or the start of the string).
- size_t root_dir_pos = root_dir_start(path, style);
- while (end_pos > 0 &&
- (root_dir_pos == StringRef::npos || end_pos > root_dir_pos) &&
- is_separator(path[end_pos - 1], style))
- --end_pos;
-
- if (end_pos == root_dir_pos && !filename_was_sep) {
- // We've reached the root dir and the input path was *not* ending in a
- // sequence of slashes. Include the root dir in the parent path.
- return root_dir_pos + 1;
- }
-
- // Otherwise, just include before the last slash.
- return end_pos;
- }
-} // end unnamed namespace
-
-namespace wpi {
-namespace sys {
-namespace path {
-
-const_iterator begin(StringRef path, Style style) {
- const_iterator i;
- i.Path = path;
- i.Component = find_first_component(path, style);
- i.Position = 0;
- i.S = style;
- return i;
-}
-
-const_iterator end(StringRef path) {
- const_iterator i;
- i.Path = path;
- i.Position = path.size();
- return i;
-}
-
-const_iterator &const_iterator::operator++() {
- assert(Position < Path.size() && "Tried to increment past end!");
-
- // Increment Position to past the current component
- Position += Component.size();
-
- // Check for end.
- if (Position == Path.size()) {
- Component = StringRef();
- return *this;
- }
-
- // Both POSIX and Windows treat paths that begin with exactly two separators
- // specially.
- bool was_net = Component.size() > 2 && is_separator(Component[0], S) &&
- Component[1] == Component[0] && !is_separator(Component[2], S);
-
- // Handle separators.
- if (is_separator(Path[Position], S)) {
- // Root dir.
- if (was_net ||
- // c:/
- (real_style(S) == Style::windows && Component.endswith(":"))) {
- Component = Path.substr(Position, 1);
- return *this;
- }
-
- // Skip extra separators.
- while (Position != Path.size() && is_separator(Path[Position], S)) {
- ++Position;
- }
-
- // Treat trailing '/' as a '.', unless it is the root dir.
- if (Position == Path.size() && Component != "/") {
- --Position;
- Component = ".";
- return *this;
- }
- }
-
- // Find next component.
- size_t end_pos = Path.find_first_of(separators(S), Position);
- Component = Path.slice(Position, end_pos);
-
- return *this;
-}
-
-bool const_iterator::operator==(const const_iterator &RHS) const {
- return Path.begin() == RHS.Path.begin() && Position == RHS.Position;
-}
-
-ptrdiff_t const_iterator::operator-(const const_iterator &RHS) const {
- return Position - RHS.Position;
-}
-
-reverse_iterator rbegin(StringRef Path, Style style) {
- reverse_iterator I;
- I.Path = Path;
- I.Position = Path.size();
- I.S = style;
- return ++I;
-}
-
-reverse_iterator rend(StringRef Path) {
- reverse_iterator I;
- I.Path = Path;
- I.Component = Path.substr(0, 0);
- I.Position = 0;
- return I;
-}
-
-reverse_iterator &reverse_iterator::operator++() {
- size_t root_dir_pos = root_dir_start(Path, S);
-
- // Skip separators unless it's the root directory.
- size_t end_pos = Position;
- while (end_pos > 0 && (end_pos - 1) != root_dir_pos &&
- is_separator(Path[end_pos - 1], S))
- --end_pos;
-
- // Treat trailing '/' as a '.', unless it is the root dir.
- if (Position == Path.size() && !Path.empty() &&
- is_separator(Path.back(), S) &&
- (root_dir_pos == StringRef::npos || end_pos - 1 > root_dir_pos)) {
- --Position;
- Component = ".";
- return *this;
- }
-
- // Find next separator.
- size_t start_pos = filename_pos(Path.substr(0, end_pos), S);
- Component = Path.slice(start_pos, end_pos);
- Position = start_pos;
- return *this;
-}
-
-bool reverse_iterator::operator==(const reverse_iterator &RHS) const {
- return Path.begin() == RHS.Path.begin() && Component == RHS.Component &&
- Position == RHS.Position;
-}
-
-ptrdiff_t reverse_iterator::operator-(const reverse_iterator &RHS) const {
- return Position - RHS.Position;
-}
-
-StringRef root_path(StringRef path, Style style) {
- const_iterator b = begin(path, style), pos = b, e = end(path);
- if (b != e) {
- bool has_net =
- b->size() > 2 && is_separator((*b)[0], style) && (*b)[1] == (*b)[0];
- bool has_drive = (real_style(style) == Style::windows) && b->endswith(":");
-
- if (has_net || has_drive) {
- if ((++pos != e) && is_separator((*pos)[0], style)) {
- // {C:/,//net/}, so get the first two components.
- return path.substr(0, b->size() + pos->size());
- } else {
- // just {C:,//net}, return the first component.
- return *b;
- }
- }
-
- // POSIX style root directory.
- if (is_separator((*b)[0], style)) {
- return *b;
- }
- }
-
- return StringRef();
-}
-
-StringRef root_name(StringRef path, Style style) {
- const_iterator b = begin(path, style), e = end(path);
- if (b != e) {
- bool has_net =
- b->size() > 2 && is_separator((*b)[0], style) && (*b)[1] == (*b)[0];
- bool has_drive = (real_style(style) == Style::windows) && b->endswith(":");
-
- if (has_net || has_drive) {
- // just {C:,//net}, return the first component.
- return *b;
- }
- }
-
- // No path or no name.
- return StringRef();
-}
-
-StringRef root_directory(StringRef path, Style style) {
- const_iterator b = begin(path, style), pos = b, e = end(path);
- if (b != e) {
- bool has_net =
- b->size() > 2 && is_separator((*b)[0], style) && (*b)[1] == (*b)[0];
- bool has_drive = (real_style(style) == Style::windows) && b->endswith(":");
-
- if ((has_net || has_drive) &&
- // {C:,//net}, skip to the next component.
- (++pos != e) && is_separator((*pos)[0], style)) {
- return *pos;
- }
-
- // POSIX style root directory.
- if (!has_net && is_separator((*b)[0], style)) {
- return *b;
- }
- }
-
- // No path or no root.
- return StringRef();
-}
-
-StringRef relative_path(StringRef path, Style style) {
- StringRef root = root_path(path, style);
- return path.substr(root.size());
-}
-
-void append(SmallVectorImpl<char> &path, Style style, const Twine &a,
- const Twine &b, const Twine &c, const Twine &d) {
- SmallString<32> a_storage;
- SmallString<32> b_storage;
- SmallString<32> c_storage;
- SmallString<32> d_storage;
-
- SmallVector<StringRef, 4> components;
- if (!a.isTriviallyEmpty()) components.push_back(a.toStringRef(a_storage));
- if (!b.isTriviallyEmpty()) components.push_back(b.toStringRef(b_storage));
- if (!c.isTriviallyEmpty()) components.push_back(c.toStringRef(c_storage));
- if (!d.isTriviallyEmpty()) components.push_back(d.toStringRef(d_storage));
-
- for (auto &component : components) {
- bool path_has_sep =
- !path.empty() && is_separator(path[path.size() - 1], style);
- if (path_has_sep) {
- // Strip separators from beginning of component.
- size_t loc = component.find_first_not_of(separators(style));
- StringRef c = component.substr(loc);
-
- // Append it.
- path.append(c.begin(), c.end());
- continue;
- }
-
- bool component_has_sep =
- !component.empty() && is_separator(component[0], style);
- if (!component_has_sep &&
- !(path.empty() || has_root_name(component, style))) {
- // Add a separator.
- path.push_back(preferred_separator(style));
- }
-
- path.append(component.begin(), component.end());
- }
-}
-
-void append(SmallVectorImpl<char> &path, const Twine &a, const Twine &b,
- const Twine &c, const Twine &d) {
- append(path, Style::native, a, b, c, d);
-}
-
-void append(SmallVectorImpl<char> &path, const_iterator begin,
- const_iterator end, Style style) {
- for (; begin != end; ++begin)
- path::append(path, style, *begin);
-}
-
-StringRef parent_path(StringRef path, Style style) {
- size_t end_pos = parent_path_end(path, style);
- if (end_pos == StringRef::npos)
- return StringRef();
- else
- return path.substr(0, end_pos);
-}
-
-void remove_filename(SmallVectorImpl<char> &path, Style style) {
- size_t end_pos = parent_path_end(StringRef(path.begin(), path.size()), style);
- if (end_pos != StringRef::npos)
- path.set_size(end_pos);
-}
-
-void replace_extension(SmallVectorImpl<char> &path, const Twine &extension,
- Style style) {
- StringRef p(path.begin(), path.size());
- SmallString<32> ext_storage;
- StringRef ext = extension.toStringRef(ext_storage);
-
- // Erase existing extension.
- size_t pos = p.find_last_of('.');
- if (pos != StringRef::npos && pos >= filename_pos(p, style))
- path.set_size(pos);
-
- // Append '.' if needed.
- if (ext.size() > 0 && ext[0] != '.')
- path.push_back('.');
-
- // Append extension.
- path.append(ext.begin(), ext.end());
-}
-
-void replace_path_prefix(SmallVectorImpl<char> &Path,
- const StringRef &OldPrefix, const StringRef &NewPrefix,
- Style style) {
- if (OldPrefix.empty() && NewPrefix.empty())
- return;
-
- StringRef OrigPath(Path.begin(), Path.size());
- if (!OrigPath.startswith(OldPrefix))
- return;
-
- // If prefixes have the same size we can simply copy the new one over.
- if (OldPrefix.size() == NewPrefix.size()) {
- wpi::copy(NewPrefix, Path.begin());
- return;
- }
-
- StringRef RelPath = OrigPath.substr(OldPrefix.size());
- SmallString<256> NewPath;
- path::append(NewPath, style, NewPrefix);
- path::append(NewPath, style, RelPath);
- Path.swap(NewPath);
-}
-
-void native(const Twine &path, SmallVectorImpl<char> &result, Style style) {
- assert((!path.isSingleStringRef() ||
- path.getSingleStringRef().data() != result.data()) &&
- "path and result are not allowed to overlap!");
- // Clear result.
- result.clear();
- path.toVector(result);
- native(result, style);
-}
-
-void native(SmallVectorImpl<char> &Path, Style style) {
- if (Path.empty())
- return;
- if (real_style(style) == Style::windows) {
- std::replace(Path.begin(), Path.end(), '/', '\\');
- if (Path[0] == '~' && (Path.size() == 1 || is_separator(Path[1], style))) {
- SmallString<128> PathHome;
- home_directory(PathHome);
- PathHome.append(Path.begin() + 1, Path.end());
- Path = PathHome;
- }
- } else {
- for (auto PI = Path.begin(), PE = Path.end(); PI < PE; ++PI) {
- if (*PI == '\\') {
- auto PN = PI + 1;
- if (PN < PE && *PN == '\\')
- ++PI; // increment once, the for loop will move over the escaped slash
- else
- *PI = '/';
- }
- }
- }
-}
-
-std::string convert_to_slash(StringRef path, Style style) {
- if (real_style(style) != Style::windows)
- return path;
-
- std::string s = path.str();
- std::replace(s.begin(), s.end(), '\\', '/');
- return s;
-}
-
-StringRef filename(StringRef path, Style style) { return *rbegin(path, style); }
-
-StringRef stem(StringRef path, Style style) {
- StringRef fname = filename(path, style);
- size_t pos = fname.find_last_of('.');
- if (pos == StringRef::npos)
- return fname;
- else
- if ((fname.size() == 1 && fname == ".") ||
- (fname.size() == 2 && fname == ".."))
- return fname;
- else
- return fname.substr(0, pos);
-}
-
-StringRef extension(StringRef path, Style style) {
- StringRef fname = filename(path, style);
- size_t pos = fname.find_last_of('.');
- if (pos == StringRef::npos)
- return StringRef();
- else
- if ((fname.size() == 1 && fname == ".") ||
- (fname.size() == 2 && fname == ".."))
- return StringRef();
- else
- return fname.substr(pos);
-}
-
-bool is_separator(char value, Style style) {
- if (value == '/')
- return true;
- if (real_style(style) == Style::windows)
- return value == '\\';
- return false;
-}
-
-StringRef get_separator(Style style) {
- if (real_style(style) == Style::windows)
- return "\\";
- return "/";
-}
-
-bool has_root_name(const Twine &path, Style style) {
- SmallString<128> path_storage;
- StringRef p = path.toStringRef(path_storage);
-
- return !root_name(p, style).empty();
-}
-
-bool has_root_directory(const Twine &path, Style style) {
- SmallString<128> path_storage;
- StringRef p = path.toStringRef(path_storage);
-
- return !root_directory(p, style).empty();
-}
-
-bool has_root_path(const Twine &path, Style style) {
- SmallString<128> path_storage;
- StringRef p = path.toStringRef(path_storage);
-
- return !root_path(p, style).empty();
-}
-
-bool has_relative_path(const Twine &path, Style style) {
- SmallString<128> path_storage;
- StringRef p = path.toStringRef(path_storage);
-
- return !relative_path(p, style).empty();
-}
-
-bool has_filename(const Twine &path, Style style) {
- SmallString<128> path_storage;
- StringRef p = path.toStringRef(path_storage);
-
- return !filename(p, style).empty();
-}
-
-bool has_parent_path(const Twine &path, Style style) {
- SmallString<128> path_storage;
- StringRef p = path.toStringRef(path_storage);
-
- return !parent_path(p, style).empty();
-}
-
-bool has_stem(const Twine &path, Style style) {
- SmallString<128> path_storage;
- StringRef p = path.toStringRef(path_storage);
-
- return !stem(p, style).empty();
-}
-
-bool has_extension(const Twine &path, Style style) {
- SmallString<128> path_storage;
- StringRef p = path.toStringRef(path_storage);
-
- return !extension(p, style).empty();
-}
-
-bool is_absolute(const Twine &path, Style style) {
- SmallString<128> path_storage;
- StringRef p = path.toStringRef(path_storage);
-
- bool rootDir = has_root_directory(p, style);
- bool rootName =
- (real_style(style) != Style::windows) || has_root_name(p, style);
-
- return rootDir && rootName;
-}
-
-bool is_relative(const Twine &path, Style style) {
- return !is_absolute(path, style);
-}
-
-StringRef remove_leading_dotslash(StringRef Path, Style style) {
- // Remove leading "./" (or ".//" or "././" etc.)
- while (Path.size() > 2 && Path[0] == '.' && is_separator(Path[1], style)) {
- Path = Path.substr(2);
- while (Path.size() > 0 && is_separator(Path[0], style))
- Path = Path.substr(1);
- }
- return Path;
-}
-
-static SmallString<256> remove_dots(StringRef path, bool remove_dot_dot,
- Style style) {
- SmallVector<StringRef, 16> components;
-
- // Skip the root path, then look for traversal in the components.
- StringRef rel = path::relative_path(path, style);
- for (StringRef C :
- wpi::make_range(path::begin(rel, style), path::end(rel))) {
- if (C == ".")
- continue;
- // Leading ".." will remain in the path unless it's at the root.
- if (remove_dot_dot && C == "..") {
- if (!components.empty() && components.back() != "..") {
- components.pop_back();
- continue;
- }
- if (path::is_absolute(path, style))
- continue;
- }
- components.push_back(C);
- }
-
- SmallString<256> buffer = path::root_path(path, style);
- for (StringRef C : components)
- path::append(buffer, style, C);
- return buffer;
-}
-
-bool remove_dots(SmallVectorImpl<char> &path, bool remove_dot_dot,
- Style style) {
- StringRef p(path.data(), path.size());
-
- SmallString<256> result = remove_dots(p, remove_dot_dot, style);
- if (result == path)
- return false;
-
- path.swap(result);
- return true;
-}
-
-} // end namespace path
-
-namespace fs {
-
-std::error_code getUniqueID(const Twine Path, UniqueID &Result) {
- file_status Status;
- std::error_code EC = status(Path, Status);
- if (EC)
- return EC;
- Result = Status.getUniqueID();
- return std::error_code();
-}
-
-void make_absolute(const Twine ¤t_directory,
- SmallVectorImpl<char> &path) {
- StringRef p(path.data(), path.size());
-
- bool rootDirectory = path::has_root_directory(p);
- bool rootName =
- (real_style(Style::native) != Style::windows) || path::has_root_name(p);
-
- // Already absolute.
- if (rootName && rootDirectory)
- return;
-
- // All of the following conditions will need the current directory.
- SmallString<128> current_dir;
- current_directory.toVector(current_dir);
-
- // Relative path. Prepend the current directory.
- if (!rootName && !rootDirectory) {
- // Append path to the current directory.
- path::append(current_dir, p);
- // Set path to the result.
- path.swap(current_dir);
- return;
- }
-
- if (!rootName && rootDirectory) {
- StringRef cdrn = path::root_name(current_dir);
- SmallString<128> curDirRootName(cdrn.begin(), cdrn.end());
- path::append(curDirRootName, p);
- // Set path to the result.
- path.swap(curDirRootName);
- return;
- }
-
- if (rootName && !rootDirectory) {
- StringRef pRootName = path::root_name(p);
- StringRef bRootDirectory = path::root_directory(current_dir);
- StringRef bRelativePath = path::relative_path(current_dir);
- StringRef pRelativePath = path::relative_path(p);
-
- SmallString<128> res;
- path::append(res, pRootName, bRootDirectory, bRelativePath, pRelativePath);
- path.swap(res);
- return;
- }
-
- wpi_unreachable("All rootName and rootDirectory combinations should have "
- "occurred above!");
-}
-
-std::error_code make_absolute(SmallVectorImpl<char> &path) {
- if (path::is_absolute(path))
- return {};
-
- SmallString<128> current_dir;
- if (std::error_code ec = current_path(current_dir))
- return ec;
-
- make_absolute(current_dir, path);
- return {};
-}
-
-bool exists(const basic_file_status &status) {
- return status_known(status) && status.type() != file_type::file_not_found;
-}
-
-bool status_known(const basic_file_status &s) {
- return s.type() != file_type::status_error;
-}
-
-file_type get_file_type(const Twine &Path, bool Follow) {
- file_status st;
- if (status(Path, st, Follow))
- return file_type::status_error;
- return st.type();
-}
-
-bool is_directory(const basic_file_status &status) {
- return status.type() == file_type::directory_file;
-}
-
-std::error_code is_directory(const Twine &path, bool &result) {
- file_status st;
- if (std::error_code ec = status(path, st))
- return ec;
- result = is_directory(st);
- return std::error_code();
-}
-
-bool is_regular_file(const basic_file_status &status) {
- return status.type() == file_type::regular_file;
-}
-
-std::error_code is_regular_file(const Twine &path, bool &result) {
- file_status st;
- if (std::error_code ec = status(path, st))
- return ec;
- result = is_regular_file(st);
- return std::error_code();
-}
-
-bool is_symlink_file(const basic_file_status &status) {
- return status.type() == file_type::symlink_file;
-}
-
-std::error_code is_symlink_file(const Twine &path, bool &result) {
- file_status st;
- if (std::error_code ec = status(path, st, false))
- return ec;
- result = is_symlink_file(st);
- return std::error_code();
-}
-
-bool is_other(const basic_file_status &status) {
- return exists(status) &&
- !is_regular_file(status) &&
- !is_directory(status);
-}
-
-std::error_code is_other(const Twine &Path, bool &Result) {
- file_status FileStatus;
- if (std::error_code EC = status(Path, FileStatus))
- return EC;
- Result = is_other(FileStatus);
- return std::error_code();
-}
-
-void directory_entry::replace_filename(const Twine &Filename, file_type Type,
- basic_file_status Status) {
- SmallString<128> PathStr = path::parent_path(Path);
- path::append(PathStr, Filename);
- this->Path = PathStr.str();
- this->Type = Type;
- this->Status = Status;
-}
-
-ErrorOr<perms> getPermissions(const Twine &Path) {
- file_status Status;
- if (std::error_code EC = status(Path, Status))
- return EC;
-
- return Status.permissions();
-}
-
-} // end namespace fs
-} // end namespace sys
-} // end namespace wpi
-
-// Include the truly platform-specific parts.
-#ifdef _WIN32
-#include "Windows/Path.inc"
-#else
-#include "Unix/Path.inc"
-#endif
diff --git a/wpiutil/src/main/native/cpp/llvm/SmallPtrSet.cpp b/wpiutil/src/main/native/cpp/llvm/SmallPtrSet.cpp
index 1dab1fc..1b33f4c 100644
--- a/wpiutil/src/main/native/cpp/llvm/SmallPtrSet.cpp
+++ b/wpiutil/src/main/native/cpp/llvm/SmallPtrSet.cpp
@@ -15,6 +15,7 @@
#include "wpi/SmallPtrSet.h"
#include "wpi/DenseMapInfo.h"
#include "wpi/MathExtras.h"
+#include "wpi/MemAlloc.h"
#include "wpi/ErrorHandling.h"
#include <algorithm>
#include <cassert>
diff --git a/wpiutil/src/main/native/cpp/llvm/StringExtras.cpp b/wpiutil/src/main/native/cpp/llvm/StringExtras.cpp
deleted file mode 100644
index e4bfe8a..0000000
--- a/wpiutil/src/main/native/cpp/llvm/StringExtras.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-//===-- StringExtras.cpp - Implement the StringExtras header --------------===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file implements the StringExtras.h header
-//
-//===----------------------------------------------------------------------===//
-
-#include "wpi/StringExtras.h"
-#include "wpi/SmallVector.h"
-#include "wpi/raw_ostream.h"
-using namespace wpi;
-
-/// StrInStrNoCase - Portable version of strcasestr. Locates the first
-/// occurrence of string 's1' in string 's2', ignoring case. Returns
-/// the offset of s2 in s1 or npos if s2 cannot be found.
-StringRef::size_type wpi::StrInStrNoCase(StringRef s1, StringRef s2) {
- size_t N = s2.size(), M = s1.size();
- if (N > M)
- return StringRef::npos;
- for (size_t i = 0, e = M - N + 1; i != e; ++i)
- if (s1.substr(i, N).equals_lower(s2))
- return i;
- return StringRef::npos;
-}
-
-/// getToken - This function extracts one token from source, ignoring any
-/// leading characters that appear in the Delimiters string, and ending the
-/// token at any of the characters that appear in the Delimiters string. If
-/// there are no tokens in the source string, an empty string is returned.
-/// The function returns a pair containing the extracted token and the
-/// remaining tail string.
-std::pair<StringRef, StringRef> wpi::getToken(StringRef Source,
- StringRef Delimiters) {
- // Figure out where the token starts.
- StringRef::size_type Start = Source.find_first_not_of(Delimiters);
-
- // Find the next occurrence of the delimiter.
- StringRef::size_type End = Source.find_first_of(Delimiters, Start);
-
- return std::make_pair(Source.slice(Start, End), Source.substr(End));
-}
-
-/// SplitString - Split up the specified string according to the specified
-/// delimiters, appending the result fragments to the output list.
-void wpi::SplitString(StringRef Source,
- SmallVectorImpl<StringRef> &OutFragments,
- StringRef Delimiters) {
- std::pair<StringRef, StringRef> S = getToken(Source, Delimiters);
- while (!S.first.empty()) {
- OutFragments.push_back(S.first);
- S = getToken(S.second, Delimiters);
- }
-}
-
-void wpi::printEscapedString(StringRef Name, raw_ostream &Out) {
- for (unsigned i = 0, e = Name.size(); i != e; ++i) {
- unsigned char C = Name[i];
- if (isPrint(C) && C != '\\' && C != '"')
- Out << C;
- else
- Out << '\\' << hexdigit(C >> 4) << hexdigit(C & 0x0F);
- }
-}
-
-void wpi::printHTMLEscaped(StringRef String, raw_ostream &Out) {
- for (char C : String) {
- if (C == '&')
- Out << "&";
- else if (C == '<')
- Out << "<";
- else if (C == '>')
- Out << ">";
- else if (C == '\"')
- Out << """;
- else if (C == '\'')
- Out << "'";
- else
- Out << C;
- }
-}
-
-void wpi::printLowerCase(StringRef String, raw_ostream &Out) {
- for (const char C : String)
- Out << toLower(C);
-}
diff --git a/wpiutil/src/main/native/cpp/llvm/StringMap.cpp b/wpiutil/src/main/native/cpp/llvm/StringMap.cpp
index 5c625c7..768801d 100644
--- a/wpiutil/src/main/native/cpp/llvm/StringMap.cpp
+++ b/wpiutil/src/main/native/cpp/llvm/StringMap.cpp
@@ -19,6 +19,21 @@
using namespace wpi;
+/// HashString - Hash function for strings.
+///
+/// This is the Bernstein hash function.
+//
+// FIXME: Investigate whether a modified bernstein hash function performs
+// better: http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx
+// X*33+c -> X*33^c
+static inline unsigned HashString(std::string_view str,
+ unsigned result = 0) noexcept {
+ for (std::string_view::size_type i = 0, e = str.size(); i != e; ++i) {
+ result = result * 33 + static_cast<unsigned char>(str[i]);
+ }
+ return result;
+}
+
/// 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) {
@@ -74,7 +89,7 @@
/// 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);
@@ -113,7 +128,7 @@
// 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;
}
@@ -131,7 +146,7 @@
/// 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?
unsigned FullHashValue = HashString(Key);
@@ -156,7 +171,7 @@
// 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;
}
@@ -175,14 +190,14 @@
/// 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/wpiutil/src/main/native/cpp/llvm/StringRef.cpp b/wpiutil/src/main/native/cpp/llvm/StringRef.cpp
deleted file mode 100644
index ea44bea..0000000
--- a/wpiutil/src/main/native/cpp/llvm/StringRef.cpp
+++ /dev/null
@@ -1,507 +0,0 @@
-//===-- StringRef.cpp - Lightweight String References ---------------------===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-
-#include "wpi/StringRef.h"
-#include "wpi/Hashing.h"
-#include "wpi/StringExtras.h"
-#include "wpi/SmallVector.h"
-#include <bitset>
-#include <climits>
-#include <ostream>
-
-using namespace wpi;
-
-// MSVC emits references to this into the translation units which reference it.
-#ifndef _MSC_VER
-const size_t StringRef::npos;
-#endif
-
-// strncasecmp() is not available on non-POSIX systems, so define an
-// alternative function here.
-static int ascii_strncasecmp(const char *LHS, const char *RHS, size_t Length) noexcept {
- for (size_t I = 0; I < Length; ++I) {
- unsigned char LHC = toLower(LHS[I]);
- unsigned char RHC = toLower(RHS[I]);
- if (LHC != RHC)
- return LHC < RHC ? -1 : 1;
- }
- return 0;
-}
-
-/// compare_lower - Compare strings, ignoring case.
-int StringRef::compare_lower(StringRef RHS) const noexcept {
- if (int Res = ascii_strncasecmp(Data, RHS.Data, std::min(Length, RHS.Length)))
- return Res;
- if (Length == RHS.Length)
- return 0;
- return Length < RHS.Length ? -1 : 1;
-}
-
-/// Check if this string starts with the given \p Prefix, ignoring case.
-bool StringRef::startswith_lower(StringRef Prefix) const noexcept {
- return Length >= Prefix.Length &&
- ascii_strncasecmp(Data, Prefix.Data, Prefix.Length) == 0;
-}
-
-/// Check if this string ends with the given \p Suffix, ignoring case.
-bool StringRef::endswith_lower(StringRef Suffix) const noexcept {
- return Length >= Suffix.Length &&
- ascii_strncasecmp(end() - Suffix.Length, Suffix.Data, Suffix.Length) == 0;
-}
-
-size_t StringRef::find_lower(char C, size_t From) const noexcept {
- char L = toLower(C);
- return find_if([L](char D) { return toLower(D) == L; }, From);
-}
-
-/// compare_numeric - Compare strings, handle embedded numbers.
-int StringRef::compare_numeric(StringRef RHS) const noexcept {
- for (size_t I = 0, E = std::min(Length, RHS.Length); I != E; ++I) {
- // Check for sequences of digits.
- if (isDigit(Data[I]) && isDigit(RHS.Data[I])) {
- // The longer sequence of numbers is considered larger.
- // This doesn't really handle prefixed zeros well.
- size_t J;
- for (J = I + 1; J != E + 1; ++J) {
- bool ld = J < Length && isDigit(Data[J]);
- bool rd = J < RHS.Length && isDigit(RHS.Data[J]);
- if (ld != rd)
- return rd ? -1 : 1;
- if (!rd)
- break;
- }
- // The two number sequences have the same length (J-I), just memcmp them.
- if (int Res = compareMemory(Data + I, RHS.Data + I, J - I))
- return Res < 0 ? -1 : 1;
- // Identical number sequences, continue search after the numbers.
- I = J - 1;
- continue;
- }
- if (Data[I] != RHS.Data[I])
- return (unsigned char)Data[I] < (unsigned char)RHS.Data[I] ? -1 : 1;
- }
- if (Length == RHS.Length)
- return 0;
- return Length < RHS.Length ? -1 : 1;
-}
-
-//===----------------------------------------------------------------------===//
-// String Operations
-//===----------------------------------------------------------------------===//
-
-std::string StringRef::lower() const {
- std::string Result(size(), char());
- for (size_type i = 0, e = size(); i != e; ++i) {
- Result[i] = toLower(Data[i]);
- }
- return Result;
-}
-
-std::string StringRef::upper() const {
- std::string Result(size(), char());
- for (size_type i = 0, e = size(); i != e; ++i) {
- Result[i] = toUpper(Data[i]);
- }
- return Result;
-}
-
-//===----------------------------------------------------------------------===//
-// String Searching
-//===----------------------------------------------------------------------===//
-
-
-/// find - Search for the first string \arg Str in the string.
-///
-/// \return - The index of the first occurrence of \arg Str, or npos if not
-/// found.
-size_t StringRef::find(StringRef Str, size_t From) const noexcept {
- if (From > Length)
- return npos;
-
- const char *Start = Data + From;
- size_t Size = Length - From;
-
- const char *Needle = Str.data();
- size_t N = Str.size();
- if (N == 0)
- return From;
- if (Size < N)
- return npos;
- if (N == 1) {
- const char *Ptr = (const char *)::memchr(Start, Needle[0], Size);
- return Ptr == nullptr ? npos : Ptr - Data;
- }
-
- const char *Stop = Start + (Size - N + 1);
-
- // For short haystacks or unsupported needles fall back to the naive algorithm
- if (Size < 16 || N > 255) {
- do {
- if (std::memcmp(Start, Needle, N) == 0)
- return Start - Data;
- ++Start;
- } while (Start < Stop);
- return npos;
- }
-
- // Build the bad char heuristic table, with uint8_t to reduce cache thrashing.
- uint8_t BadCharSkip[256];
- std::memset(BadCharSkip, N, 256);
- for (unsigned i = 0; i != N-1; ++i)
- BadCharSkip[(uint8_t)Str[i]] = N-1-i;
-
- do {
- uint8_t Last = Start[N - 1];
- if (LLVM_UNLIKELY(Last == (uint8_t)Needle[N - 1]))
- if (std::memcmp(Start, Needle, N - 1) == 0)
- return Start - Data;
-
- // Otherwise skip the appropriate number of bytes.
- Start += BadCharSkip[Last];
- } while (Start < Stop);
-
- return npos;
-}
-
-size_t StringRef::find_lower(StringRef Str, size_t From) const noexcept {
- StringRef This = substr(From);
- while (This.size() >= Str.size()) {
- if (This.startswith_lower(Str))
- return From;
- This = This.drop_front();
- ++From;
- }
- return npos;
-}
-
-size_t StringRef::rfind_lower(char C, size_t From) const noexcept {
- From = std::min(From, Length);
- size_t i = From;
- while (i != 0) {
- --i;
- if (toLower(Data[i]) == toLower(C))
- return i;
- }
- return npos;
-}
-
-/// rfind - Search for the last string \arg Str in the string.
-///
-/// \return - The index of the last occurrence of \arg Str, or npos if not
-/// found.
-size_t StringRef::rfind(StringRef Str) const noexcept {
- size_t N = Str.size();
- if (N > Length)
- return npos;
- for (size_t i = Length - N + 1, e = 0; i != e;) {
- --i;
- if (substr(i, N).equals(Str))
- return i;
- }
- return npos;
-}
-
-size_t StringRef::rfind_lower(StringRef Str) const noexcept {
- size_t N = Str.size();
- if (N > Length)
- return npos;
- for (size_t i = Length - N + 1, e = 0; i != e;) {
- --i;
- if (substr(i, N).equals_lower(Str))
- return i;
- }
- return npos;
-}
-
-/// find_first_of - Find the first character in the string that is in \arg
-/// Chars, or npos if not found.
-///
-/// Note: O(size() + Chars.size())
-StringRef::size_type StringRef::find_first_of(StringRef Chars,
- size_t From) const noexcept {
- std::bitset<1 << CHAR_BIT> CharBits;
- for (size_type i = 0; i != Chars.size(); ++i)
- CharBits.set((unsigned char)Chars[i]);
-
- for (size_type i = std::min(From, Length), e = Length; i != e; ++i)
- if (CharBits.test((unsigned char)Data[i]))
- return i;
- return npos;
-}
-
-/// find_first_not_of - Find the first character in the string that is not
-/// \arg C or npos if not found.
-StringRef::size_type StringRef::find_first_not_of(char C, size_t From) const noexcept {
- for (size_type i = std::min(From, Length), e = Length; i != e; ++i)
- if (Data[i] != C)
- return i;
- return npos;
-}
-
-/// find_first_not_of - Find the first character in the string that is not
-/// in the string \arg Chars, or npos if not found.
-///
-/// Note: O(size() + Chars.size())
-StringRef::size_type StringRef::find_first_not_of(StringRef Chars,
- size_t From) const noexcept {
- std::bitset<1 << CHAR_BIT> CharBits;
- for (size_type i = 0; i != Chars.size(); ++i)
- CharBits.set((unsigned char)Chars[i]);
-
- for (size_type i = std::min(From, Length), e = Length; i != e; ++i)
- if (!CharBits.test((unsigned char)Data[i]))
- return i;
- return npos;
-}
-
-/// find_last_of - Find the last character in the string that is in \arg C,
-/// or npos if not found.
-///
-/// Note: O(size() + Chars.size())
-StringRef::size_type StringRef::find_last_of(StringRef Chars,
- size_t From) const noexcept {
- std::bitset<1 << CHAR_BIT> CharBits;
- for (size_type i = 0; i != Chars.size(); ++i)
- CharBits.set((unsigned char)Chars[i]);
-
- for (size_type i = std::min(From, Length) - 1, e = -1; i != e; --i)
- if (CharBits.test((unsigned char)Data[i]))
- return i;
- return npos;
-}
-
-/// find_last_not_of - Find the last character in the string that is not
-/// \arg C, or npos if not found.
-StringRef::size_type StringRef::find_last_not_of(char C, size_t From) const noexcept {
- for (size_type i = std::min(From, Length) - 1, e = -1; i != e; --i)
- if (Data[i] != C)
- return i;
- return npos;
-}
-
-/// find_last_not_of - Find the last character in the string that is not in
-/// \arg Chars, or npos if not found.
-///
-/// Note: O(size() + Chars.size())
-StringRef::size_type StringRef::find_last_not_of(StringRef Chars,
- size_t From) const noexcept {
- std::bitset<1 << CHAR_BIT> CharBits;
- for (size_type i = 0, e = Chars.size(); i != e; ++i)
- CharBits.set((unsigned char)Chars[i]);
-
- for (size_type i = std::min(From, Length) - 1, e = -1; i != e; --i)
- if (!CharBits.test((unsigned char)Data[i]))
- return i;
- return npos;
-}
-
-void StringRef::split(SmallVectorImpl<StringRef> &A,
- StringRef Separator, int MaxSplit,
- bool KeepEmpty) const {
- StringRef S = *this;
-
- // Count down from MaxSplit. When MaxSplit is -1, this will just split
- // "forever". This doesn't support splitting more than 2^31 times
- // intentionally; if we ever want that we can make MaxSplit a 64-bit integer
- // but that seems unlikely to be useful.
- while (MaxSplit-- != 0) {
- size_t Idx = S.find(Separator);
- if (Idx == npos)
- break;
-
- // Push this split.
- if (KeepEmpty || Idx > 0)
- A.push_back(S.slice(0, Idx));
-
- // Jump forward.
- S = S.slice(Idx + Separator.size(), npos);
- }
-
- // Push the tail.
- if (KeepEmpty || !S.empty())
- A.push_back(S);
-}
-
-void StringRef::split(SmallVectorImpl<StringRef> &A, char Separator,
- int MaxSplit, bool KeepEmpty) const {
- StringRef S = *this;
-
- // Count down from MaxSplit. When MaxSplit is -1, this will just split
- // "forever". This doesn't support splitting more than 2^31 times
- // intentionally; if we ever want that we can make MaxSplit a 64-bit integer
- // but that seems unlikely to be useful.
- while (MaxSplit-- != 0) {
- size_t Idx = S.find(Separator);
- if (Idx == npos)
- break;
-
- // Push this split.
- if (KeepEmpty || Idx > 0)
- A.push_back(S.slice(0, Idx));
-
- // Jump forward.
- S = S.slice(Idx + 1, npos);
- }
-
- // Push the tail.
- if (KeepEmpty || !S.empty())
- A.push_back(S);
-}
-
-//===----------------------------------------------------------------------===//
-// Helpful Algorithms
-//===----------------------------------------------------------------------===//
-
-/// count - Return the number of non-overlapped occurrences of \arg Str in
-/// the string.
-size_t StringRef::count(StringRef Str) const noexcept {
- size_t Count = 0;
- size_t N = Str.size();
- if (N > Length)
- return 0;
- for (size_t i = 0, e = Length - N + 1; i != e; ++i)
- if (substr(i, N).equals(Str))
- ++Count;
- return Count;
-}
-
-static unsigned GetAutoSenseRadix(StringRef &Str) noexcept {
- if (Str.empty())
- return 10;
-
- if (Str.startswith("0x") || Str.startswith("0X")) {
- Str = Str.substr(2);
- return 16;
- }
-
- if (Str.startswith("0b") || Str.startswith("0B")) {
- Str = Str.substr(2);
- return 2;
- }
-
- if (Str.startswith("0o")) {
- Str = Str.substr(2);
- return 8;
- }
-
- if (Str[0] == '0' && Str.size() > 1 && isDigit(Str[1])) {
- Str = Str.substr(1);
- return 8;
- }
-
- return 10;
-}
-
-bool wpi::consumeUnsignedInteger(StringRef &Str, unsigned Radix,
- unsigned long long &Result) noexcept {
- // Autosense radix if not specified.
- if (Radix == 0)
- Radix = GetAutoSenseRadix(Str);
-
- // Empty strings (after the radix autosense) are invalid.
- if (Str.empty()) return true;
-
- // Parse all the bytes of the string given this radix. Watch for overflow.
- StringRef Str2 = Str;
- Result = 0;
- while (!Str2.empty()) {
- unsigned CharVal;
- if (Str2[0] >= '0' && Str2[0] <= '9')
- CharVal = Str2[0] - '0';
- else if (Str2[0] >= 'a' && Str2[0] <= 'z')
- CharVal = Str2[0] - 'a' + 10;
- else if (Str2[0] >= 'A' && Str2[0] <= 'Z')
- CharVal = Str2[0] - 'A' + 10;
- else
- break;
-
- // If the parsed value is larger than the integer radix, we cannot
- // consume any more characters.
- if (CharVal >= Radix)
- break;
-
- // Add in this character.
- unsigned long long PrevResult = Result;
- Result = Result * Radix + CharVal;
-
- // Check for overflow by shifting back and seeing if bits were lost.
- if (Result / Radix < PrevResult)
- return true;
-
- Str2 = Str2.substr(1);
- }
-
- // We consider the operation a failure if no characters were consumed
- // successfully.
- if (Str.size() == Str2.size())
- return true;
-
- Str = Str2;
- return false;
-}
-
-bool wpi::consumeSignedInteger(StringRef &Str, unsigned Radix,
- long long &Result) noexcept {
- unsigned long long ULLVal;
-
- // Handle positive strings first.
- if (Str.empty() || Str.front() != '-') {
- if (consumeUnsignedInteger(Str, Radix, ULLVal) ||
- // Check for value so large it overflows a signed value.
- (long long)ULLVal < 0)
- return true;
- Result = ULLVal;
- return false;
- }
-
- // Get the positive part of the value.
- StringRef Str2 = Str.drop_front(1);
- if (consumeUnsignedInteger(Str2, Radix, ULLVal) ||
- // Reject values so large they'd overflow as negative signed, but allow
- // "-0". This negates the unsigned so that the negative isn't undefined
- // on signed overflow.
- (long long)-ULLVal > 0)
- return true;
-
- Str = Str2;
- Result = -ULLVal;
- return false;
-}
-
-/// GetAsUnsignedInteger - Workhorse method that converts a integer character
-/// sequence of radix up to 36 to an unsigned long long value.
-bool wpi::getAsUnsignedInteger(StringRef Str, unsigned Radix,
- unsigned long long &Result) noexcept {
- if (consumeUnsignedInteger(Str, Radix, Result))
- return true;
-
- // For getAsUnsignedInteger, we require the whole string to be consumed or
- // else we consider it a failure.
- return !Str.empty();
-}
-
-bool wpi::getAsSignedInteger(StringRef Str, unsigned Radix,
- long long &Result) noexcept {
- if (consumeSignedInteger(Str, Radix, Result))
- return true;
-
- // For getAsSignedInteger, we require the whole string to be consumed or else
- // we consider it a failure.
- return !Str.empty();
-}
-
-std::ostream &wpi::operator<<(std::ostream &os, StringRef string) {
- os.write(string.data(), string.size());
- return os;
-}
-
-// Implementation of StringRef hashing.
-hash_code wpi::hash_value(StringRef S) {
- return hash_combine_range(S.begin(), S.end());
-}
diff --git a/wpiutil/src/main/native/cpp/llvm/Twine.cpp b/wpiutil/src/main/native/cpp/llvm/Twine.cpp
deleted file mode 100644
index ac705ff..0000000
--- a/wpiutil/src/main/native/cpp/llvm/Twine.cpp
+++ /dev/null
@@ -1,169 +0,0 @@
-//===-- Twine.cpp - Fast Temporary String Concatenation -------------------===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-
-#include "wpi/Twine.h"
-#include "wpi/SmallString.h"
-#include "wpi/raw_ostream.h"
-using namespace wpi;
-
-std::string Twine::str() const {
- // If we're storing only a std::string, just return it.
- if (LHSKind == StdStringKind && RHSKind == EmptyKind)
- return *LHS.stdString;
-
- // Otherwise, flatten and copy the contents first.
- SmallString<256> Vec;
- return toStringRef(Vec).str();
-}
-
-void Twine::toVector(SmallVectorImpl<char> &Out) const {
- raw_svector_ostream OS(Out);
- print(OS);
-}
-
-StringRef Twine::toNullTerminatedStringRef(SmallVectorImpl<char> &Out) const {
- if (isUnary()) {
- switch (getLHSKind()) {
- case CStringKind:
- // Already null terminated, yay!
- return StringRef(LHS.cString);
- case StdStringKind: {
- const std::string *str = LHS.stdString;
- return StringRef(str->c_str(), str->size());
- }
- default:
- break;
- }
- }
- toVector(Out);
- Out.push_back(0);
- Out.pop_back();
- return StringRef(Out.data(), Out.size());
-}
-
-void Twine::printOneChild(raw_ostream &OS, Child Ptr,
- NodeKind Kind) const {
- switch (Kind) {
- case Twine::NullKind: break;
- case Twine::EmptyKind: break;
- case Twine::TwineKind:
- Ptr.twine->print(OS);
- break;
- case Twine::CStringKind:
- OS << Ptr.cString;
- break;
- case Twine::StdStringKind:
- OS << *Ptr.stdString;
- break;
- case Twine::StringRefKind:
- OS << *Ptr.stringRef;
- break;
- case Twine::SmallStringKind:
- OS << *Ptr.smallString;
- break;
- case Twine::CharKind:
- OS << Ptr.character;
- break;
- case Twine::DecUIKind:
- OS << Ptr.decUI;
- break;
- case Twine::DecIKind:
- OS << Ptr.decI;
- break;
- case Twine::DecULKind:
- OS << *Ptr.decUL;
- break;
- case Twine::DecLKind:
- OS << *Ptr.decL;
- break;
- case Twine::DecULLKind:
- OS << *Ptr.decULL;
- break;
- case Twine::DecLLKind:
- OS << *Ptr.decLL;
- break;
- case Twine::UHexKind:
- OS.write_hex(*Ptr.uHex);
- break;
- }
-}
-
-void Twine::printOneChildRepr(raw_ostream &OS, Child Ptr,
- NodeKind Kind) const {
- switch (Kind) {
- case Twine::NullKind:
- OS << "null"; break;
- case Twine::EmptyKind:
- OS << "empty"; break;
- case Twine::TwineKind:
- OS << "rope:";
- Ptr.twine->printRepr(OS);
- break;
- case Twine::CStringKind:
- OS << "cstring:\""
- << Ptr.cString << "\"";
- break;
- case Twine::StdStringKind:
- OS << "std::string:\""
- << Ptr.stdString << "\"";
- break;
- case Twine::StringRefKind:
- OS << "stringref:\""
- << Ptr.stringRef << "\"";
- break;
- case Twine::SmallStringKind:
- OS << "smallstring:\"" << *Ptr.smallString << "\"";
- break;
- case Twine::CharKind:
- OS << "char:\"" << Ptr.character << "\"";
- break;
- case Twine::DecUIKind:
- OS << "decUI:\"" << Ptr.decUI << "\"";
- break;
- case Twine::DecIKind:
- OS << "decI:\"" << Ptr.decI << "\"";
- break;
- case Twine::DecULKind:
- OS << "decUL:\"" << *Ptr.decUL << "\"";
- break;
- case Twine::DecLKind:
- OS << "decL:\"" << *Ptr.decL << "\"";
- break;
- case Twine::DecULLKind:
- OS << "decULL:\"" << *Ptr.decULL << "\"";
- break;
- case Twine::DecLLKind:
- OS << "decLL:\"" << *Ptr.decLL << "\"";
- break;
- case Twine::UHexKind:
- OS << "uhex:\"" << Ptr.uHex << "\"";
- break;
- }
-}
-
-void Twine::print(raw_ostream &OS) const {
- printOneChild(OS, LHS, getLHSKind());
- printOneChild(OS, RHS, getRHSKind());
-}
-
-void Twine::printRepr(raw_ostream &OS) const {
- OS << "(Twine ";
- printOneChildRepr(OS, LHS, getLHSKind());
- OS << " ";
- printOneChildRepr(OS, RHS, getRHSKind());
- OS << ")";
-}
-
-LLVM_DUMP_METHOD void Twine::dump() const {
- print(errs());
-}
-
-LLVM_DUMP_METHOD void Twine::dumpRepr() const {
- printRepr(errs());
-}
diff --git a/wpiutil/src/main/native/cpp/llvm/Unix/Path.inc b/wpiutil/src/main/native/cpp/llvm/Unix/Path.inc
deleted file mode 100644
index 4ec9e86..0000000
--- a/wpiutil/src/main/native/cpp/llvm/Unix/Path.inc
+++ /dev/null
@@ -1,539 +0,0 @@
-//===- llvm/Support/Unix/Path.inc - Unix Path Implementation ----*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file implements the Unix specific implementation of the Path API.
-//
-//===----------------------------------------------------------------------===//
-
-//===----------------------------------------------------------------------===//
-//=== WARNING: Implementation here must contain only generic UNIX code that
-//=== is guaranteed to work on *all* UNIX variants.
-//===----------------------------------------------------------------------===//
-
-#include "wpi/Errno.h"
-#include <fcntl.h>
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <sys/mman.h>
-#include <dirent.h>
-#include <pwd.h>
-#include <sys/param.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-using namespace wpi;
-
-namespace wpi {
-namespace sys {
-namespace fs {
-
-const file_t kInvalidFile = -1;
-
-TimePoint<> basic_file_status::getLastAccessedTime() const {
- return toTimePoint(fs_st_atime, fs_st_atime_nsec);
-}
-
-TimePoint<> basic_file_status::getLastModificationTime() const {
- return toTimePoint(fs_st_mtime, fs_st_mtime_nsec);
-}
-
-UniqueID file_status::getUniqueID() const {
- return UniqueID(fs_st_dev, fs_st_ino);
-}
-
-uint32_t file_status::getLinkCount() const {
- return fs_st_nlinks;
-}
-
-std::error_code current_path(SmallVectorImpl<char> &result) {
- result.clear();
-
- const char *pwd = ::getenv("PWD");
- wpi::sys::fs::file_status PWDStatus, DotStatus;
- if (pwd && wpi::sys::path::is_absolute(pwd) &&
- !wpi::sys::fs::status(pwd, PWDStatus) &&
- !wpi::sys::fs::status(".", DotStatus) &&
- PWDStatus.getUniqueID() == DotStatus.getUniqueID()) {
- result.append(pwd, pwd + strlen(pwd));
- return std::error_code();
- }
-
-#ifdef MAXPATHLEN
- result.reserve(MAXPATHLEN);
-#else
- result.reserve(1024);
-#endif
-
- while (true) {
- if (::getcwd(result.data(), result.capacity()) == nullptr) {
- // See if there was a real error.
- if (errno != ENOMEM)
- return std::error_code(errno, std::generic_category());
- // Otherwise there just wasn't enough space.
- result.reserve(result.capacity() * 2);
- } else
- break;
- }
-
- result.set_size(strlen(result.data()));
- return std::error_code();
-}
-
-static int convertAccessMode(AccessMode Mode) {
- switch (Mode) {
- case AccessMode::Exist:
- return F_OK;
- case AccessMode::Write:
- return W_OK;
- case AccessMode::Execute:
- return R_OK | X_OK; // scripts also need R_OK.
- default:
- return F_OK;
- }
-}
-
-std::error_code access(const Twine &Path, AccessMode Mode) {
- SmallString<128> PathStorage;
- StringRef P = Path.toNullTerminatedStringRef(PathStorage);
-
- if (::access(P.begin(), convertAccessMode(Mode)) == -1)
- return std::error_code(errno, std::generic_category());
-
- if (Mode == AccessMode::Execute) {
- // Don't say that directories are executable.
- struct stat buf;
- if (0 != stat(P.begin(), &buf))
- return errc::permission_denied;
- if (!S_ISREG(buf.st_mode))
- return errc::permission_denied;
- }
-
- return std::error_code();
-}
-
-bool equivalent(file_status A, file_status B) {
- assert(status_known(A) && status_known(B));
- return A.fs_st_dev == B.fs_st_dev &&
- A.fs_st_ino == B.fs_st_ino;
-}
-
-std::error_code equivalent(const Twine &A, const Twine &B, bool &result) {
- file_status fsA, fsB;
- if (std::error_code ec = status(A, fsA))
- return ec;
- if (std::error_code ec = status(B, fsB))
- return ec;
- result = equivalent(fsA, fsB);
- return std::error_code();
-}
-
-static file_type typeForMode(mode_t Mode) {
- if (S_ISDIR(Mode))
- return file_type::directory_file;
- else if (S_ISREG(Mode))
- return file_type::regular_file;
- else if (S_ISBLK(Mode))
- return file_type::block_file;
- else if (S_ISCHR(Mode))
- return file_type::character_file;
- else if (S_ISFIFO(Mode))
- return file_type::fifo_file;
- else if (S_ISSOCK(Mode))
- return file_type::socket_file;
- else if (S_ISLNK(Mode))
- return file_type::symlink_file;
- return file_type::type_unknown;
-}
-
-static std::error_code fillStatus(int StatRet, const struct stat &Status,
- file_status &Result) {
- if (StatRet != 0) {
- std::error_code EC(errno, std::generic_category());
- if (EC == errc::no_such_file_or_directory)
- Result = file_status(file_type::file_not_found);
- else
- Result = file_status(file_type::status_error);
- return EC;
- }
-
- uint32_t atime_nsec, mtime_nsec;
-#if defined(__APPLE__)
- atime_nsec = Status.st_atimespec.tv_nsec;
- mtime_nsec = Status.st_mtimespec.tv_nsec;
-#else
- atime_nsec = Status.st_atim.tv_nsec;
- mtime_nsec = Status.st_mtim.tv_nsec;
-#endif
-
- perms Perms = static_cast<perms>(Status.st_mode) & all_perms;
- Result = file_status(typeForMode(Status.st_mode), Perms, Status.st_dev,
- Status.st_nlink, Status.st_ino,
- Status.st_atime, atime_nsec, Status.st_mtime, mtime_nsec,
- Status.st_uid, Status.st_gid, Status.st_size);
-
- return std::error_code();
-}
-
-std::error_code status(const Twine &Path, file_status &Result, bool Follow) {
- SmallString<128> PathStorage;
- StringRef P = Path.toNullTerminatedStringRef(PathStorage);
-
- struct stat Status;
- int StatRet = (Follow ? ::stat : ::lstat)(P.begin(), &Status);
- return fillStatus(StatRet, Status, Result);
-}
-
-std::error_code status(int FD, file_status &Result) {
- struct stat Status;
- int StatRet = ::fstat(FD, &Status);
- return fillStatus(StatRet, Status, Result);
-}
-
-std::error_code mapped_file_region::init(int FD, uint64_t Offset,
- mapmode Mode) {
- assert(Size != 0);
-
- int flags = (Mode == readwrite) ? MAP_SHARED : MAP_PRIVATE;
- int prot = (Mode == readonly) ? PROT_READ : (PROT_READ | PROT_WRITE);
-#if defined(__APPLE__)
- //----------------------------------------------------------------------
- // Newer versions of MacOSX have a flag that will allow us to read from
- // binaries whose code signature is invalid without crashing by using
- // the MAP_RESILIENT_CODESIGN flag. Also if a file from removable media
- // is mapped we can avoid crashing and return zeroes to any pages we try
- // to read if the media becomes unavailable by using the
- // MAP_RESILIENT_MEDIA flag. These flags are only usable when mapping
- // with PROT_READ, so take care not to specify them otherwise.
- //----------------------------------------------------------------------
- if (Mode == readonly) {
-#if defined(MAP_RESILIENT_CODESIGN)
- flags |= MAP_RESILIENT_CODESIGN;
-#endif
-#if defined(MAP_RESILIENT_MEDIA)
- flags |= MAP_RESILIENT_MEDIA;
-#endif
- }
-#endif // #if defined (__APPLE__)
-
- Mapping = ::mmap(nullptr, Size, prot, flags, FD, Offset);
- if (Mapping == MAP_FAILED)
- return std::error_code(errno, std::generic_category());
- return std::error_code();
-}
-
-mapped_file_region::mapped_file_region(int fd, mapmode mode, size_t length,
- uint64_t offset, std::error_code &ec)
- : Size(length), Mapping(), Mode(mode) {
- (void)Mode;
- ec = init(fd, offset, mode);
- if (ec)
- Mapping = nullptr;
-}
-
-mapped_file_region::~mapped_file_region() {
- if (Mapping)
- ::munmap(Mapping, Size);
-}
-
-size_t mapped_file_region::size() const {
- assert(Mapping && "Mapping failed but used anyway!");
- return Size;
-}
-
-char *mapped_file_region::data() const {
- assert(Mapping && "Mapping failed but used anyway!");
- return reinterpret_cast<char*>(Mapping);
-}
-
-const char *mapped_file_region::const_data() const {
- assert(Mapping && "Mapping failed but used anyway!");
- return reinterpret_cast<const char*>(Mapping);
-}
-
-int mapped_file_region::alignment() {
- return ::sysconf(_SC_PAGE_SIZE);
-}
-
-std::error_code detail::directory_iterator_construct(detail::DirIterState &it,
- StringRef path,
- bool follow_symlinks) {
- SmallString<128> path_null(path);
- DIR *directory = ::opendir(path_null.c_str());
- if (!directory)
- return std::error_code(errno, std::generic_category());
-
- it.IterationHandle = reinterpret_cast<intptr_t>(directory);
- // Add something for replace_filename to replace.
- path::append(path_null, ".");
- it.CurrentEntry = directory_entry(path_null.str(), follow_symlinks);
- return directory_iterator_increment(it);
-}
-
-std::error_code detail::directory_iterator_destruct(detail::DirIterState &it) {
- if (it.IterationHandle)
- ::closedir(reinterpret_cast<DIR *>(it.IterationHandle));
- it.IterationHandle = 0;
- it.CurrentEntry = directory_entry();
- return std::error_code();
-}
-
-static file_type direntType(dirent* Entry) {
- // Most platforms provide the file type in the dirent: Linux/BSD/Mac.
- // The DTTOIF macro lets us reuse our status -> type conversion.
- return typeForMode(DTTOIF(Entry->d_type));
-}
-
-std::error_code detail::directory_iterator_increment(detail::DirIterState &It) {
- errno = 0;
- dirent *CurDir = ::readdir(reinterpret_cast<DIR *>(It.IterationHandle));
- if (CurDir == nullptr && errno != 0) {
- return std::error_code(errno, std::generic_category());
- } else if (CurDir != nullptr) {
- StringRef Name(CurDir->d_name);
- if ((Name.size() == 1 && Name[0] == '.') ||
- (Name.size() == 2 && Name[0] == '.' && Name[1] == '.'))
- return directory_iterator_increment(It);
- It.CurrentEntry.replace_filename(Name, direntType(CurDir));
- } else
- return directory_iterator_destruct(It);
-
- return std::error_code();
-}
-
-ErrorOr<basic_file_status> directory_entry::status() const {
- file_status s;
- if (auto EC = fs::status(Path, s, FollowSymlinks))
- return EC;
- return s;
-}
-
-#if !defined(F_GETPATH)
-static bool hasProcSelfFD() {
- // If we have a /proc filesystem mounted, we can quickly establish the
- // real name of the file with readlink
- static const bool Result = (::access("/proc/self/fd", R_OK) == 0);
- return Result;
-}
-#endif
-
-static int nativeOpenFlags(CreationDisposition Disp, OpenFlags Flags,
- FileAccess Access) {
- int Result = 0;
- if (Access == FA_Read)
- Result |= O_RDONLY;
- else if (Access == FA_Write)
- Result |= O_WRONLY;
- else if (Access == (FA_Read | FA_Write))
- Result |= O_RDWR;
-
- // This is for compatibility with old code that assumed F_Append implied
- // would open an existing file. See Windows/Path.inc for a longer comment.
- if (Flags & F_Append)
- Disp = CD_OpenAlways;
-
- if (Disp == CD_CreateNew) {
- Result |= O_CREAT; // Create if it doesn't exist.
- Result |= O_EXCL; // Fail if it does.
- } else if (Disp == CD_CreateAlways) {
- Result |= O_CREAT; // Create if it doesn't exist.
- Result |= O_TRUNC; // Truncate if it does.
- } else if (Disp == CD_OpenAlways) {
- Result |= O_CREAT; // Create if it doesn't exist.
- } else if (Disp == CD_OpenExisting) {
- // Nothing special, just don't add O_CREAT and we get these semantics.
- }
-
- if (Flags & F_Append)
- Result |= O_APPEND;
-
-#ifdef O_CLOEXEC
- if (!(Flags & OF_ChildInherit))
- Result |= O_CLOEXEC;
-#endif
-
- return Result;
-}
-
-std::error_code openFile(const Twine &Name, int &ResultFD,
- CreationDisposition Disp, FileAccess Access,
- OpenFlags Flags, unsigned Mode) {
- int OpenFlags = nativeOpenFlags(Disp, Flags, Access);
-
- SmallString<128> Storage;
- StringRef P = Name.toNullTerminatedStringRef(Storage);
- // Call ::open in a lambda to avoid overload resolution in RetryAfterSignal
- // when open is overloaded, such as in Bionic.
- auto Open = [&]() { return ::open(P.begin(), OpenFlags, Mode); };
- if ((ResultFD = sys::RetryAfterSignal(-1, Open)) < 0)
- return std::error_code(errno, std::generic_category());
-#ifndef O_CLOEXEC
- if (!(Flags & OF_ChildInherit)) {
- int r = fcntl(ResultFD, F_SETFD, FD_CLOEXEC);
- (void)r;
- assert(r == 0 && "fcntl(F_SETFD, FD_CLOEXEC) failed");
- }
-#endif
- return std::error_code();
-}
-
-Expected<int> openNativeFile(const Twine &Name, CreationDisposition Disp,
- FileAccess Access, OpenFlags Flags,
- unsigned Mode) {
-
- int FD;
- std::error_code EC = openFile(Name, FD, Disp, Access, Flags, Mode);
- if (EC)
- return errorCodeToError(EC);
- return FD;
-}
-
-std::error_code openFileForRead(const Twine &Name, int &ResultFD,
- OpenFlags Flags,
- SmallVectorImpl<char> *RealPath) {
- std::error_code EC =
- openFile(Name, ResultFD, CD_OpenExisting, FA_Read, Flags, 0666);
- if (EC)
- return EC;
-
- // Attempt to get the real name of the file, if the user asked
- if(!RealPath)
- return std::error_code();
- RealPath->clear();
-#if defined(F_GETPATH)
- // When F_GETPATH is available, it is the quickest way to get
- // the real path name.
- char Buffer[MAXPATHLEN];
- if (::fcntl(ResultFD, F_GETPATH, Buffer) != -1)
- RealPath->append(Buffer, Buffer + strlen(Buffer));
-#else
- char Buffer[PATH_MAX];
- if (hasProcSelfFD()) {
- char ProcPath[64];
- snprintf(ProcPath, sizeof(ProcPath), "/proc/self/fd/%d", ResultFD);
- ssize_t CharCount = ::readlink(ProcPath, Buffer, sizeof(Buffer));
- if (CharCount > 0)
- RealPath->append(Buffer, Buffer + CharCount);
- } else {
- SmallString<128> Storage;
- StringRef P = Name.toNullTerminatedStringRef(Storage);
-
- // Use ::realpath to get the real path name
- if (::realpath(P.begin(), Buffer) != nullptr)
- RealPath->append(Buffer, Buffer + strlen(Buffer));
- }
-#endif
- return std::error_code();
-}
-
-Expected<file_t> openNativeFileForRead(const Twine &Name, OpenFlags Flags,
- SmallVectorImpl<char> *RealPath) {
- file_t ResultFD;
- std::error_code EC = openFileForRead(Name, ResultFD, Flags, RealPath);
- if (EC)
- return errorCodeToError(EC);
- return ResultFD;
-}
-
-void closeFile(file_t &F) {
- ::close(F);
- F = kInvalidFile;
-}
-
-} // end namespace fs
-
-namespace path {
-
-bool home_directory(SmallVectorImpl<char> &result) {
- char *RequestedDir = getenv("HOME");
- if (!RequestedDir) {
- struct passwd *pw = getpwuid(getuid());
- if (pw && pw->pw_dir)
- RequestedDir = pw->pw_dir;
- }
- if (!RequestedDir)
- return false;
-
- result.clear();
- result.append(RequestedDir, RequestedDir + strlen(RequestedDir));
- return true;
-}
-
-static bool getDarwinConfDir(bool TempDir, SmallVectorImpl<char> &Result) {
- #if defined(_CS_DARWIN_USER_TEMP_DIR) && defined(_CS_DARWIN_USER_CACHE_DIR)
- // On Darwin, use DARWIN_USER_TEMP_DIR or DARWIN_USER_CACHE_DIR.
- // macros defined in <unistd.h> on darwin >= 9
- int ConfName = TempDir ? _CS_DARWIN_USER_TEMP_DIR
- : _CS_DARWIN_USER_CACHE_DIR;
- size_t ConfLen = confstr(ConfName, nullptr, 0);
- if (ConfLen > 0) {
- do {
- Result.resize(ConfLen);
- ConfLen = confstr(ConfName, Result.data(), Result.size());
- } while (ConfLen > 0 && ConfLen != Result.size());
-
- if (ConfLen > 0) {
- assert(Result.back() == 0);
- Result.pop_back();
- return true;
- }
-
- Result.clear();
- }
- #endif
- return false;
-}
-
-static const char *getEnvTempDir() {
- // Check whether the temporary directory is specified by an environment
- // variable.
- const char *EnvironmentVariables[] = {"TMPDIR", "TMP", "TEMP", "TEMPDIR"};
- for (const char *Env : EnvironmentVariables) {
- if (const char *Dir = std::getenv(Env))
- return Dir;
- }
-
- return nullptr;
-}
-
-static const char *getDefaultTempDir(bool ErasedOnReboot) {
-#ifdef P_tmpdir
- if ((bool)P_tmpdir)
- return P_tmpdir;
-#endif
-
- if (ErasedOnReboot)
- return "/tmp";
- return "/var/tmp";
-}
-
-void system_temp_directory(bool ErasedOnReboot, SmallVectorImpl<char> &Result) {
- Result.clear();
-
- if (ErasedOnReboot) {
- // There is no env variable for the cache directory.
- if (const char *RequestedDir = getEnvTempDir()) {
- Result.append(RequestedDir, RequestedDir + strlen(RequestedDir));
- return;
- }
- }
-
- if (getDarwinConfDir(ErasedOnReboot, Result))
- return;
-
- const char *RequestedDir = getDefaultTempDir(ErasedOnReboot);
- Result.append(RequestedDir, RequestedDir + strlen(RequestedDir));
-}
-
-} // end namespace path
-} // end namespace sys
-} // end namespace wpi
diff --git a/wpiutil/src/main/native/cpp/llvm/Windows/Path.inc b/wpiutil/src/main/native/cpp/llvm/Windows/Path.inc
deleted file mode 100644
index e9b5280..0000000
--- a/wpiutil/src/main/native/cpp/llvm/Windows/Path.inc
+++ /dev/null
@@ -1,971 +0,0 @@
-//===- llvm/Support/Windows/Path.inc - Windows Path Impl --------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file implements the Windows specific implementation of the Path API.
-//
-//===----------------------------------------------------------------------===//
-
-//===----------------------------------------------------------------------===//
-//=== WARNING: Implementation here must contain only generic Windows code that
-//=== is guaranteed to work on *all* Windows variants.
-//===----------------------------------------------------------------------===//
-
-#include "wpi/STLExtras.h"
-#include "wpi/ConvertUTF.h"
-#include "wpi/WindowsError.h"
-#include <fcntl.h>
-#include <io.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-// These two headers must be included last, and make sure shlobj is required
-// after Windows.h to make sure it picks up our definition of _WIN32_WINNT
-#include "WindowsSupport.h"
-#include <shellapi.h>
-#include <shlobj.h>
-
-#undef max
-
-// MinGW doesn't define this.
-#ifndef _ERRNO_T_DEFINED
-#define _ERRNO_T_DEFINED
-typedef int errno_t;
-#endif
-
-#ifdef _MSC_VER
-# pragma comment(lib, "shell32.lib")
-# pragma comment(lib, "ole32.lib")
-#pragma warning(push)
-#pragma warning(disable: 4244 4267 4146)
-#endif
-
-using namespace wpi;
-
-using wpi::sys::windows::UTF8ToUTF16;
-using wpi::sys::windows::CurCPToUTF16;
-using wpi::sys::windows::UTF16ToUTF8;
-using wpi::sys::path::widenPath;
-
-static bool is_separator(const wchar_t value) {
- switch (value) {
- case L'\\':
- case L'/':
- return true;
- default:
- return false;
- }
-}
-
-namespace wpi {
-namespace sys {
-namespace path {
-
-// Convert a UTF-8 path to UTF-16. Also, if the absolute equivalent of the
-// path is longer than CreateDirectory can tolerate, make it absolute and
-// prefixed by '\\?\'.
-std::error_code widenPath(const Twine &Path8,
- SmallVectorImpl<wchar_t> &Path16) {
- const size_t MaxDirLen = MAX_PATH - 12; // Must leave room for 8.3 filename.
-
- // Several operations would convert Path8 to SmallString; more efficient to
- // do it once up front.
- SmallString<128> Path8Str;
- Path8.toVector(Path8Str);
-
- // If we made this path absolute, how much longer would it get?
- size_t CurPathLen;
- if (wpi::sys::path::is_absolute(Twine(Path8Str)))
- CurPathLen = 0; // No contribution from current_path needed.
- else {
- CurPathLen = ::GetCurrentDirectoryW(0, NULL);
- if (CurPathLen == 0)
- return mapWindowsError(::GetLastError());
- }
-
- // Would the absolute path be longer than our limit?
- if ((Path8Str.size() + CurPathLen) >= MaxDirLen &&
- !Path8Str.startswith("\\\\?\\")) {
- SmallString<2*MAX_PATH> FullPath("\\\\?\\");
- if (CurPathLen) {
- SmallString<80> CurPath;
- if (std::error_code EC = wpi::sys::fs::current_path(CurPath))
- return EC;
- FullPath.append(CurPath);
- }
- // Traverse the requested path, canonicalizing . and .. (because the \\?\
- // prefix is documented to treat them as real components). Ignore
- // separators, which can be returned from the iterator if the path has a
- // drive name. We don't need to call native() on the result since append()
- // always attaches preferred_separator.
- for (wpi::sys::path::const_iterator I = wpi::sys::path::begin(Path8Str),
- E = wpi::sys::path::end(Path8Str);
- I != E; ++I) {
- if (I->size() == 1 && is_separator((*I)[0]))
- continue;
- if (I->size() == 1 && *I == ".")
- continue;
- if (I->size() == 2 && *I == "..")
- wpi::sys::path::remove_filename(FullPath);
- else
- wpi::sys::path::append(FullPath, *I);
- }
- return UTF8ToUTF16(FullPath, Path16);
- }
-
- // Just use the caller's original path.
- return UTF8ToUTF16(Path8Str, Path16);
-}
-} // end namespace path
-
-namespace fs {
-
-const file_t kInvalidFile = INVALID_HANDLE_VALUE;
-
-UniqueID file_status::getUniqueID() const {
- // The file is uniquely identified by the volume serial number along
- // with the 64-bit file identifier.
- uint64_t FileID = (static_cast<uint64_t>(FileIndexHigh) << 32ULL) |
- static_cast<uint64_t>(FileIndexLow);
-
- return UniqueID(VolumeSerialNumber, FileID);
-}
-
-TimePoint<> basic_file_status::getLastAccessedTime() const {
- FILETIME Time;
- Time.dwLowDateTime = LastAccessedTimeLow;
- Time.dwHighDateTime = LastAccessedTimeHigh;
- return toTimePoint(Time);
-}
-
-TimePoint<> basic_file_status::getLastModificationTime() const {
- FILETIME Time;
- Time.dwLowDateTime = LastWriteTimeLow;
- Time.dwHighDateTime = LastWriteTimeHigh;
- return toTimePoint(Time);
-}
-
-uint32_t file_status::getLinkCount() const {
- return NumLinks;
-}
-
-std::error_code current_path(SmallVectorImpl<char> &result) {
- SmallVector<wchar_t, MAX_PATH> cur_path;
- DWORD len = MAX_PATH;
-
- do {
- cur_path.reserve(len);
- len = ::GetCurrentDirectoryW(cur_path.capacity(), cur_path.data());
-
- // A zero return value indicates a failure other than insufficient space.
- if (len == 0)
- return mapWindowsError(::GetLastError());
-
- // If there's insufficient space, the len returned is larger than the len
- // given.
- } while (len > cur_path.capacity());
-
- // On success, GetCurrentDirectoryW returns the number of characters not
- // including the null-terminator.
- cur_path.set_size(len);
- return UTF16ToUTF8(cur_path.begin(), cur_path.size(), result);
-}
-
-static std::error_code realPathFromHandle(HANDLE H,
- SmallVectorImpl<wchar_t> &Buffer) {
- DWORD CountChars = ::GetFinalPathNameByHandleW(
- H, Buffer.begin(), Buffer.capacity() - 1, FILE_NAME_NORMALIZED);
- if (CountChars > Buffer.capacity()) {
- // The buffer wasn't big enough, try again. In this case the return value
- // *does* indicate the size of the null terminator.
- Buffer.reserve(CountChars);
- CountChars = ::GetFinalPathNameByHandleW(
- H, Buffer.data(), Buffer.capacity() - 1, FILE_NAME_NORMALIZED);
- }
- if (CountChars == 0)
- return mapWindowsError(GetLastError());
- Buffer.set_size(CountChars);
- return std::error_code();
-}
-
-static std::error_code realPathFromHandle(HANDLE H,
- SmallVectorImpl<char> &RealPath) {
- RealPath.clear();
- SmallVector<wchar_t, MAX_PATH> Buffer;
- if (std::error_code EC = realPathFromHandle(H, Buffer))
- return EC;
-
- const wchar_t *Data = Buffer.data();
- DWORD CountChars = Buffer.size();
- if (CountChars >= 4) {
- if (0 == ::memcmp(Data, L"\\\\?\\", 8)) {
- CountChars -= 4;
- Data += 4;
- }
- }
-
- // Convert the result from UTF-16 to UTF-8.
- return UTF16ToUTF8(Data, CountChars, RealPath);
-}
-
-static std::error_code setDeleteDisposition(HANDLE Handle, bool Delete) {
- FILE_DISPOSITION_INFO Disposition;
- Disposition.DeleteFile = Delete;
- if (!SetFileInformationByHandle(Handle, FileDispositionInfo, &Disposition,
- sizeof(Disposition)))
- return mapWindowsError(::GetLastError());
- return std::error_code();
-}
-
-std::error_code access(const Twine &Path, AccessMode Mode) {
- SmallVector<wchar_t, 128> PathUtf16;
-
- if (std::error_code EC = widenPath(Path, PathUtf16))
- return EC;
-
- DWORD Attributes = ::GetFileAttributesW(PathUtf16.begin());
-
- if (Attributes == INVALID_FILE_ATTRIBUTES) {
- // See if the file didn't actually exist.
- DWORD LastError = ::GetLastError();
- if (LastError != ERROR_FILE_NOT_FOUND &&
- LastError != ERROR_PATH_NOT_FOUND)
- return mapWindowsError(LastError);
- return errc::no_such_file_or_directory;
- }
-
- if (Mode == AccessMode::Write && (Attributes & FILE_ATTRIBUTE_READONLY))
- return errc::permission_denied;
-
- return std::error_code();
-}
-
-bool equivalent(file_status A, file_status B) {
- assert(status_known(A) && status_known(B));
- return A.FileIndexHigh == B.FileIndexHigh &&
- A.FileIndexLow == B.FileIndexLow &&
- A.FileSizeHigh == B.FileSizeHigh &&
- A.FileSizeLow == B.FileSizeLow &&
- A.LastAccessedTimeHigh == B.LastAccessedTimeHigh &&
- A.LastAccessedTimeLow == B.LastAccessedTimeLow &&
- A.LastWriteTimeHigh == B.LastWriteTimeHigh &&
- A.LastWriteTimeLow == B.LastWriteTimeLow &&
- A.VolumeSerialNumber == B.VolumeSerialNumber;
-}
-
-std::error_code equivalent(const Twine &A, const Twine &B, bool &result) {
- file_status fsA, fsB;
- if (std::error_code ec = status(A, fsA))
- return ec;
- if (std::error_code ec = status(B, fsB))
- return ec;
- result = equivalent(fsA, fsB);
- return std::error_code();
-}
-
-static bool isReservedName(StringRef path) {
- // This list of reserved names comes from MSDN, at:
- // http://msdn.microsoft.com/en-us/library/aa365247%28v=vs.85%29.aspx
- static const char *const sReservedNames[] = { "nul", "con", "prn", "aux",
- "com1", "com2", "com3", "com4",
- "com5", "com6", "com7", "com8",
- "com9", "lpt1", "lpt2", "lpt3",
- "lpt4", "lpt5", "lpt6", "lpt7",
- "lpt8", "lpt9" };
-
- // First, check to see if this is a device namespace, which always
- // starts with \\.\, since device namespaces are not legal file paths.
- if (path.startswith("\\\\.\\"))
- return true;
-
- // Then compare against the list of ancient reserved names.
- for (size_t i = 0; i < array_lengthof(sReservedNames); ++i) {
- if (path.equals_lower(sReservedNames[i]))
- return true;
- }
-
- // The path isn't what we consider reserved.
- return false;
-}
-
-static file_type file_type_from_attrs(DWORD Attrs) {
- return (Attrs & FILE_ATTRIBUTE_DIRECTORY) ? file_type::directory_file
- : file_type::regular_file;
-}
-
-static perms perms_from_attrs(DWORD Attrs) {
- return (Attrs & FILE_ATTRIBUTE_READONLY) ? (all_read | all_exe) : all_all;
-}
-
-static std::error_code getStatus(HANDLE FileHandle, file_status &Result) {
- if (FileHandle == INVALID_HANDLE_VALUE)
- goto handle_status_error;
-
- switch (::GetFileType(FileHandle)) {
- default:
- Result = file_status(file_type::type_unknown);
- return std::error_code();
- case FILE_TYPE_UNKNOWN: {
- DWORD Err = ::GetLastError();
- if (Err != NO_ERROR)
- return mapWindowsError(Err);
- Result = file_status(file_type::type_unknown);
- return std::error_code();
- }
- case FILE_TYPE_DISK:
- break;
- case FILE_TYPE_CHAR:
- Result = file_status(file_type::character_file);
- return std::error_code();
- case FILE_TYPE_PIPE:
- Result = file_status(file_type::fifo_file);
- return std::error_code();
- }
-
- BY_HANDLE_FILE_INFORMATION Info;
- if (!::GetFileInformationByHandle(FileHandle, &Info))
- goto handle_status_error;
-
- Result = file_status(
- file_type_from_attrs(Info.dwFileAttributes),
- perms_from_attrs(Info.dwFileAttributes), Info.nNumberOfLinks,
- Info.ftLastAccessTime.dwHighDateTime, Info.ftLastAccessTime.dwLowDateTime,
- Info.ftLastWriteTime.dwHighDateTime, Info.ftLastWriteTime.dwLowDateTime,
- Info.dwVolumeSerialNumber, Info.nFileSizeHigh, Info.nFileSizeLow,
- Info.nFileIndexHigh, Info.nFileIndexLow);
- return std::error_code();
-
-handle_status_error:
- DWORD LastError = ::GetLastError();
- if (LastError == ERROR_FILE_NOT_FOUND ||
- LastError == ERROR_PATH_NOT_FOUND)
- Result = file_status(file_type::file_not_found);
- else if (LastError == ERROR_SHARING_VIOLATION)
- Result = file_status(file_type::type_unknown);
- else
- Result = file_status(file_type::status_error);
- return mapWindowsError(LastError);
-}
-
-std::error_code status(const Twine &path, file_status &result, bool Follow) {
- SmallString<128> path_storage;
- SmallVector<wchar_t, 128> path_utf16;
-
- StringRef path8 = path.toStringRef(path_storage);
- if (isReservedName(path8)) {
- result = file_status(file_type::character_file);
- return std::error_code();
- }
-
- if (std::error_code ec = widenPath(path8, path_utf16))
- return ec;
-
- DWORD attr = ::GetFileAttributesW(path_utf16.begin());
- if (attr == INVALID_FILE_ATTRIBUTES)
- return getStatus(INVALID_HANDLE_VALUE, result);
-
- DWORD Flags = FILE_FLAG_BACKUP_SEMANTICS;
- // Handle reparse points.
- if (!Follow && (attr & FILE_ATTRIBUTE_REPARSE_POINT))
- Flags |= FILE_FLAG_OPEN_REPARSE_POINT;
-
- ScopedFileHandle h(
- ::CreateFileW(path_utf16.begin(), 0, // Attributes only.
- FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
- NULL, OPEN_EXISTING, Flags, 0));
- if (!h)
- return getStatus(INVALID_HANDLE_VALUE, result);
-
- return getStatus(h, result);
-}
-
-std::error_code status(int FD, file_status &Result) {
- HANDLE FileHandle = reinterpret_cast<HANDLE>(_get_osfhandle(FD));
- return getStatus(FileHandle, Result);
-}
-
-std::error_code mapped_file_region::init(int FD, uint64_t Offset,
- mapmode Mode) {
- this->Mode = Mode;
- HANDLE OrigFileHandle = reinterpret_cast<HANDLE>(_get_osfhandle(FD));
- if (OrigFileHandle == INVALID_HANDLE_VALUE)
- return make_error_code(errc::bad_file_descriptor);
-
- DWORD flprotect;
- switch (Mode) {
- case readonly: flprotect = PAGE_READONLY; break;
- case readwrite: flprotect = PAGE_READWRITE; break;
- case priv: flprotect = PAGE_WRITECOPY; break;
- }
-
- HANDLE FileMappingHandle =
- ::CreateFileMappingW(OrigFileHandle, 0, flprotect,
- Hi_32(Size),
- Lo_32(Size),
- 0);
- if (FileMappingHandle == NULL) {
- std::error_code ec = mapWindowsError(GetLastError());
- return ec;
- }
-
- DWORD dwDesiredAccess;
- switch (Mode) {
- case readonly: dwDesiredAccess = FILE_MAP_READ; break;
- case readwrite: dwDesiredAccess = FILE_MAP_WRITE; break;
- case priv: dwDesiredAccess = FILE_MAP_COPY; break;
- }
- Mapping = ::MapViewOfFile(FileMappingHandle,
- dwDesiredAccess,
- Offset >> 32,
- Offset & 0xffffffff,
- Size);
- if (Mapping == NULL) {
- std::error_code ec = mapWindowsError(GetLastError());
- ::CloseHandle(FileMappingHandle);
- return ec;
- }
-
- if (Size == 0) {
- MEMORY_BASIC_INFORMATION mbi;
- SIZE_T Result = VirtualQuery(Mapping, &mbi, sizeof(mbi));
- if (Result == 0) {
- std::error_code ec = mapWindowsError(GetLastError());
- ::UnmapViewOfFile(Mapping);
- ::CloseHandle(FileMappingHandle);
- return ec;
- }
- Size = mbi.RegionSize;
- }
-
- // Close the file mapping handle, as it's kept alive by the file mapping. But
- // neither the file mapping nor the file mapping handle keep the file handle
- // alive, so we need to keep a reference to the file in case all other handles
- // are closed and the file is deleted, which may cause invalid data to be read
- // from the file.
- ::CloseHandle(FileMappingHandle);
- if (!::DuplicateHandle(::GetCurrentProcess(), OrigFileHandle,
- ::GetCurrentProcess(), &FileHandle, 0, 0,
- DUPLICATE_SAME_ACCESS)) {
- std::error_code ec = mapWindowsError(GetLastError());
- ::UnmapViewOfFile(Mapping);
- return ec;
- }
-
- return std::error_code();
-}
-
-mapped_file_region::mapped_file_region(int fd, mapmode mode, size_t length,
- uint64_t offset, std::error_code &ec)
- : Size(length), Mapping() {
- ec = init(fd, offset, mode);
- if (ec)
- Mapping = 0;
-}
-
-static bool hasFlushBufferKernelBug() {
- static bool Ret{GetWindowsOSVersion() < wpi::VersionTuple(10, 0, 0, 17763)};
- return Ret;
-}
-
-static bool isEXE(StringRef Magic) {
- static const char PEMagic[] = {'P', 'E', '\0', '\0'};
- if (Magic.startswith(StringRef("MZ")) && Magic.size() >= 0x3c + 4) {
- uint32_t off = read32le(Magic.data() + 0x3c);
- // PE/COFF file, either EXE or DLL.
- if (Magic.substr(off).startswith(StringRef(PEMagic, sizeof(PEMagic))))
- return true;
- }
- return false;
-}
-
-mapped_file_region::~mapped_file_region() {
- if (Mapping) {
-
- bool Exe = isEXE(StringRef((char *)Mapping, Size));
-
- ::UnmapViewOfFile(Mapping);
-
- if (Mode == mapmode::readwrite && Exe && hasFlushBufferKernelBug()) {
- // There is a Windows kernel bug, the exact trigger conditions of which
- // are not well understood. When triggered, dirty pages are not properly
- // flushed and subsequent process's attempts to read a file can return
- // invalid data. Calling FlushFileBuffers on the write handle is
- // sufficient to ensure that this bug is not triggered.
- // The bug only occurs when writing an executable and executing it right
- // after, under high I/O pressure.
- ::FlushFileBuffers(FileHandle);
- }
-
- ::CloseHandle(FileHandle);
- }
-}
-
-size_t mapped_file_region::size() const {
- assert(Mapping && "Mapping failed but used anyway!");
- return Size;
-}
-
-char *mapped_file_region::data() const {
- assert(Mapping && "Mapping failed but used anyway!");
- return reinterpret_cast<char*>(Mapping);
-}
-
-const char *mapped_file_region::const_data() const {
- assert(Mapping && "Mapping failed but used anyway!");
- return reinterpret_cast<const char*>(Mapping);
-}
-
-int mapped_file_region::alignment() {
- SYSTEM_INFO SysInfo;
- ::GetSystemInfo(&SysInfo);
- return SysInfo.dwAllocationGranularity;
-}
-
-static basic_file_status status_from_find_data(WIN32_FIND_DATAW *FindData) {
- return basic_file_status(file_type_from_attrs(FindData->dwFileAttributes),
- perms_from_attrs(FindData->dwFileAttributes),
- FindData->ftLastAccessTime.dwHighDateTime,
- FindData->ftLastAccessTime.dwLowDateTime,
- FindData->ftLastWriteTime.dwHighDateTime,
- FindData->ftLastWriteTime.dwLowDateTime,
- FindData->nFileSizeHigh, FindData->nFileSizeLow);
-}
-
-std::error_code detail::directory_iterator_construct(detail::DirIterState &IT,
- StringRef Path,
- bool FollowSymlinks) {
- SmallVector<wchar_t, 128> PathUTF16;
-
- if (std::error_code EC = widenPath(Path, PathUTF16))
- return EC;
-
- // Convert path to the format that Windows is happy with.
- if (PathUTF16.size() > 0 &&
- !is_separator(PathUTF16[Path.size() - 1]) &&
- PathUTF16[Path.size() - 1] != L':') {
- PathUTF16.push_back(L'\\');
- PathUTF16.push_back(L'*');
- } else {
- PathUTF16.push_back(L'*');
- }
-
- // Get the first directory entry.
- WIN32_FIND_DATAW FirstFind;
- ScopedFindHandle FindHandle(::FindFirstFileExW(
- c_str(PathUTF16), FindExInfoBasic, &FirstFind, FindExSearchNameMatch,
- NULL, FIND_FIRST_EX_LARGE_FETCH));
- if (!FindHandle)
- return mapWindowsError(::GetLastError());
-
- size_t FilenameLen = ::wcslen(FirstFind.cFileName);
- while ((FilenameLen == 1 && FirstFind.cFileName[0] == L'.') ||
- (FilenameLen == 2 && FirstFind.cFileName[0] == L'.' &&
- FirstFind.cFileName[1] == L'.'))
- if (!::FindNextFileW(FindHandle, &FirstFind)) {
- DWORD LastError = ::GetLastError();
- // Check for end.
- if (LastError == ERROR_NO_MORE_FILES)
- return detail::directory_iterator_destruct(IT);
- return mapWindowsError(LastError);
- } else
- FilenameLen = ::wcslen(FirstFind.cFileName);
-
- // Construct the current directory entry.
- SmallString<128> DirectoryEntryNameUTF8;
- if (std::error_code EC =
- UTF16ToUTF8(FirstFind.cFileName, ::wcslen(FirstFind.cFileName),
- DirectoryEntryNameUTF8))
- return EC;
-
- IT.IterationHandle = intptr_t(FindHandle.take());
- SmallString<128> DirectoryEntryPath(Path);
- path::append(DirectoryEntryPath, DirectoryEntryNameUTF8);
- IT.CurrentEntry =
- directory_entry(DirectoryEntryPath, FollowSymlinks,
- file_type_from_attrs(FirstFind.dwFileAttributes),
- status_from_find_data(&FirstFind));
-
- return std::error_code();
-}
-
-std::error_code detail::directory_iterator_destruct(detail::DirIterState &IT) {
- if (IT.IterationHandle != 0)
- // Closes the handle if it's valid.
- ScopedFindHandle close(HANDLE(IT.IterationHandle));
- IT.IterationHandle = 0;
- IT.CurrentEntry = directory_entry();
- return std::error_code();
-}
-
-std::error_code detail::directory_iterator_increment(detail::DirIterState &IT) {
- WIN32_FIND_DATAW FindData;
- if (!::FindNextFileW(HANDLE(IT.IterationHandle), &FindData)) {
- DWORD LastError = ::GetLastError();
- // Check for end.
- if (LastError == ERROR_NO_MORE_FILES)
- return detail::directory_iterator_destruct(IT);
- return mapWindowsError(LastError);
- }
-
- size_t FilenameLen = ::wcslen(FindData.cFileName);
- if ((FilenameLen == 1 && FindData.cFileName[0] == L'.') ||
- (FilenameLen == 2 && FindData.cFileName[0] == L'.' &&
- FindData.cFileName[1] == L'.'))
- return directory_iterator_increment(IT);
-
- SmallString<128> DirectoryEntryPathUTF8;
- if (std::error_code EC =
- UTF16ToUTF8(FindData.cFileName, ::wcslen(FindData.cFileName),
- DirectoryEntryPathUTF8))
- return EC;
-
- IT.CurrentEntry.replace_filename(
- Twine(DirectoryEntryPathUTF8),
- file_type_from_attrs(FindData.dwFileAttributes),
- status_from_find_data(&FindData));
- return std::error_code();
-}
-
-ErrorOr<basic_file_status> directory_entry::status() const {
- return Status;
-}
-
-static std::error_code nativeFileToFd(Expected<HANDLE> H, int &ResultFD,
- OpenFlags Flags) {
- int CrtOpenFlags = 0;
- if (Flags & OF_Append)
- CrtOpenFlags |= _O_APPEND;
-
- if (Flags & OF_Text)
- CrtOpenFlags |= _O_TEXT;
-
- ResultFD = -1;
- if (!H)
- return errorToErrorCode(H.takeError());
-
- ResultFD = ::_open_osfhandle(intptr_t(*H), CrtOpenFlags);
- if (ResultFD == -1) {
- ::CloseHandle(*H);
- return mapWindowsError(ERROR_INVALID_HANDLE);
- }
- return std::error_code();
-}
-
-static DWORD nativeDisposition(CreationDisposition Disp, OpenFlags Flags) {
- // This is a compatibility hack. Really we should respect the creation
- // disposition, but a lot of old code relied on the implicit assumption that
- // OF_Append implied it would open an existing file. Since the disposition is
- // now explicit and defaults to CD_CreateAlways, this assumption would cause
- // any usage of OF_Append to append to a new file, even if the file already
- // existed. A better solution might have two new creation dispositions:
- // CD_AppendAlways and CD_AppendNew. This would also address the problem of
- // OF_Append being used on a read-only descriptor, which doesn't make sense.
- if (Flags & OF_Append)
- return OPEN_ALWAYS;
-
- switch (Disp) {
- case CD_CreateAlways:
- return CREATE_ALWAYS;
- case CD_CreateNew:
- return CREATE_NEW;
- case CD_OpenAlways:
- return OPEN_ALWAYS;
- case CD_OpenExisting:
- return OPEN_EXISTING;
- }
- wpi_unreachable("unreachable!");
-}
-
-static DWORD nativeAccess(FileAccess Access, OpenFlags Flags) {
- DWORD Result = 0;
- if (Access & FA_Read)
- Result |= GENERIC_READ;
- if (Access & FA_Write)
- Result |= GENERIC_WRITE;
- if (Flags & OF_Delete)
- Result |= DELETE;
- if (Flags & OF_UpdateAtime)
- Result |= FILE_WRITE_ATTRIBUTES;
- return Result;
-}
-
-static std::error_code openNativeFileInternal(const Twine &Name,
- file_t &ResultFile, DWORD Disp,
- DWORD Access, DWORD Flags,
- bool Inherit = false) {
- SmallVector<wchar_t, 128> PathUTF16;
- if (std::error_code EC = widenPath(Name, PathUTF16))
- return EC;
-
- SECURITY_ATTRIBUTES SA;
- SA.nLength = sizeof(SA);
- SA.lpSecurityDescriptor = nullptr;
- SA.bInheritHandle = Inherit;
-
- HANDLE H =
- ::CreateFileW(PathUTF16.begin(), Access,
- FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, &SA,
- Disp, Flags, NULL);
- if (H == INVALID_HANDLE_VALUE) {
- DWORD LastError = ::GetLastError();
- std::error_code EC = mapWindowsError(LastError);
- // Provide a better error message when trying to open directories.
- // This only runs if we failed to open the file, so there is probably
- // no performances issues.
- if (LastError != ERROR_ACCESS_DENIED)
- return EC;
- if (is_directory(Name))
- return make_error_code(errc::is_a_directory);
- return EC;
- }
- ResultFile = H;
- return std::error_code();
-}
-
-Expected<file_t> openNativeFile(const Twine &Name, CreationDisposition Disp,
- FileAccess Access, OpenFlags Flags,
- unsigned Mode) {
- // Verify that we don't have both "append" and "excl".
- assert((!(Disp == CD_CreateNew) || !(Flags & OF_Append)) &&
- "Cannot specify both 'CreateNew' and 'Append' file creation flags!");
-
- DWORD NativeDisp = nativeDisposition(Disp, Flags);
- DWORD NativeAccess = nativeAccess(Access, Flags);
-
- bool Inherit = false;
- if (Flags & OF_ChildInherit)
- Inherit = true;
-
- file_t Result;
- std::error_code EC = openNativeFileInternal(
- Name, Result, NativeDisp, NativeAccess, FILE_ATTRIBUTE_NORMAL, Inherit);
- if (EC)
- return errorCodeToError(EC);
-
- if (Flags & OF_UpdateAtime) {
- FILETIME FileTime;
- SYSTEMTIME SystemTime;
- GetSystemTime(&SystemTime);
- if (SystemTimeToFileTime(&SystemTime, &FileTime) == 0 ||
- SetFileTime(Result, NULL, &FileTime, NULL) == 0) {
- DWORD LastError = ::GetLastError();
- ::CloseHandle(Result);
- return errorCodeToError(mapWindowsError(LastError));
- }
- }
-
- if (Flags & OF_Delete) {
- if ((EC = setDeleteDisposition(Result, true))) {
- ::CloseHandle(Result);
- return errorCodeToError(EC);
- }
- }
- return Result;
-}
-
-std::error_code openFile(const Twine &Name, int &ResultFD,
- CreationDisposition Disp, FileAccess Access,
- OpenFlags Flags, unsigned int Mode) {
- Expected<file_t> Result = openNativeFile(Name, Disp, Access, Flags);
- if (!Result)
- return errorToErrorCode(Result.takeError());
-
- return nativeFileToFd(*Result, ResultFD, Flags);
-}
-
-static std::error_code directoryRealPath(const Twine &Name,
- SmallVectorImpl<char> &RealPath) {
- file_t File;
- std::error_code EC = openNativeFileInternal(
- Name, File, OPEN_EXISTING, GENERIC_READ, FILE_FLAG_BACKUP_SEMANTICS);
- if (EC)
- return EC;
-
- EC = realPathFromHandle(File, RealPath);
- ::CloseHandle(File);
- return EC;
-}
-
-std::error_code openFileForRead(const Twine &Name, int &ResultFD,
- OpenFlags Flags,
- SmallVectorImpl<char> *RealPath) {
- Expected<HANDLE> NativeFile = openNativeFileForRead(Name, Flags, RealPath);
- return nativeFileToFd(std::move(NativeFile), ResultFD, OF_None);
-}
-
-Expected<file_t> openNativeFileForRead(const Twine &Name, OpenFlags Flags,
- SmallVectorImpl<char> *RealPath) {
- Expected<file_t> Result =
- openNativeFile(Name, CD_OpenExisting, FA_Read, Flags);
-
- // Fetch the real name of the file, if the user asked
- if (Result && RealPath)
- realPathFromHandle(*Result, *RealPath);
-
- return Result;
-}
-
-void closeFile(file_t &F) {
- ::CloseHandle(F);
- F = kInvalidFile;
-}
-
-
-} // end namespace fs
-
-namespace path {
-static bool getKnownFolderPath(KNOWNFOLDERID folderId,
- SmallVectorImpl<char> &result) {
- wchar_t *path = nullptr;
- if (::SHGetKnownFolderPath(folderId, KF_FLAG_CREATE, nullptr, &path) != S_OK)
- return false;
-
- bool ok = !UTF16ToUTF8(path, ::wcslen(path), result);
- ::CoTaskMemFree(path);
- return ok;
-}
-
-bool home_directory(SmallVectorImpl<char> &result) {
- return getKnownFolderPath(FOLDERID_Profile, result);
-}
-
-static bool getTempDirEnvVar(const wchar_t *Var, SmallVectorImpl<char> &Res) {
- SmallVector<wchar_t, 1024> Buf;
- size_t Size = 1024;
- do {
- Buf.reserve(Size);
- Size = GetEnvironmentVariableW(Var, Buf.data(), Buf.capacity());
- if (Size == 0)
- return false;
-
- // Try again with larger buffer.
- } while (Size > Buf.capacity());
- Buf.set_size(Size);
-
- return !windows::UTF16ToUTF8(Buf.data(), Size, Res);
-}
-
-static bool getTempDirEnvVar(SmallVectorImpl<char> &Res) {
- const wchar_t *EnvironmentVariables[] = {L"TMP", L"TEMP", L"USERPROFILE"};
- for (auto *Env : EnvironmentVariables) {
- if (getTempDirEnvVar(Env, Res))
- return true;
- }
- return false;
-}
-
-void system_temp_directory(bool ErasedOnReboot, SmallVectorImpl<char> &Result) {
- (void)ErasedOnReboot;
- Result.clear();
-
- // Check whether the temporary directory is specified by an environment var.
- // This matches GetTempPath logic to some degree. GetTempPath is not used
- // directly as it cannot handle evn var longer than 130 chars on Windows 7
- // (fixed on Windows 8).
- if (getTempDirEnvVar(Result)) {
- assert(!Result.empty() && "Unexpected empty path");
- native(Result); // Some Unix-like shells use Unix path separator in $TMP.
- fs::make_absolute(Result); // Make it absolute if not already.
- return;
- }
-
- // Fall back to a system default.
- const char *DefaultResult = "C:\\Temp";
- Result.append(DefaultResult, DefaultResult + strlen(DefaultResult));
-}
-} // end namespace path
-
-namespace windows {
-std::error_code CodePageToUTF16(unsigned codepage,
- wpi::StringRef original,
- wpi::SmallVectorImpl<wchar_t> &utf16) {
- if (!original.empty()) {
- int len = ::MultiByteToWideChar(codepage, MB_ERR_INVALID_CHARS, original.begin(),
- original.size(), utf16.begin(), 0);
-
- if (len == 0) {
- return mapWindowsError(::GetLastError());
- }
-
- utf16.reserve(len + 1);
- utf16.set_size(len);
-
- len = ::MultiByteToWideChar(codepage, MB_ERR_INVALID_CHARS, original.begin(),
- original.size(), utf16.begin(), utf16.size());
-
- if (len == 0) {
- return mapWindowsError(::GetLastError());
- }
- }
-
- // Make utf16 null terminated.
- utf16.push_back(0);
- utf16.pop_back();
-
- return std::error_code();
-}
-
-std::error_code UTF8ToUTF16(wpi::StringRef utf8,
- wpi::SmallVectorImpl<wchar_t> &utf16) {
- return CodePageToUTF16(CP_UTF8, utf8, utf16);
-}
-
-std::error_code CurCPToUTF16(wpi::StringRef curcp,
- wpi::SmallVectorImpl<wchar_t> &utf16) {
- return CodePageToUTF16(CP_ACP, curcp, utf16);
-}
-
-static
-std::error_code UTF16ToCodePage(unsigned codepage, const wchar_t *utf16,
- size_t utf16_len,
- wpi::SmallVectorImpl<char> &converted) {
- if (utf16_len) {
- // Get length.
- int len = ::WideCharToMultiByte(codepage, 0, utf16, utf16_len, converted.begin(),
- 0, NULL, NULL);
-
- if (len == 0) {
- return mapWindowsError(::GetLastError());
- }
-
- converted.reserve(len);
- converted.set_size(len);
-
- // Now do the actual conversion.
- len = ::WideCharToMultiByte(codepage, 0, utf16, utf16_len, converted.data(),
- converted.size(), NULL, NULL);
-
- if (len == 0) {
- return mapWindowsError(::GetLastError());
- }
- }
-
- // Make the new string null terminated.
- converted.push_back(0);
- converted.pop_back();
-
- return std::error_code();
-}
-
-std::error_code UTF16ToUTF8(const wchar_t *utf16, size_t utf16_len,
- wpi::SmallVectorImpl<char> &utf8) {
- return UTF16ToCodePage(CP_UTF8, utf16, utf16_len, utf8);
-}
-
-std::error_code UTF16ToCurCP(const wchar_t *utf16, size_t utf16_len,
- wpi::SmallVectorImpl<char> &curcp) {
- return UTF16ToCodePage(CP_ACP, utf16, utf16_len, curcp);
-}
-
-} // end namespace windows
-} // end namespace sys
-} // end namespace wpi
-
-#ifdef _MSC_VER
-#pragma warning(pop)
-#endif
diff --git a/wpiutil/src/main/native/cpp/llvm/Windows/WindowsSupport.h b/wpiutil/src/main/native/cpp/llvm/Windows/WindowsSupport.h
index d830e33..7307337 100644
--- a/wpiutil/src/main/native/cpp/llvm/Windows/WindowsSupport.h
+++ b/wpiutil/src/main/native/cpp/llvm/Windows/WindowsSupport.h
@@ -36,13 +36,12 @@
#include "wpi/SmallVector.h"
#include "wpi/StringExtras.h"
-#include "wpi/StringRef.h"
-#include "wpi/Twine.h"
#include "wpi/Chrono.h"
#include "wpi/Compiler.h"
#include "wpi/VersionTuple.h"
#include <cassert>
#include <string>
+#include <string_view>
#include <system_error>
#define WIN32_NO_STATUS
#include <windows.h>
diff --git a/wpiutil/src/main/native/cpp/llvm/raw_ostream.cpp b/wpiutil/src/main/native/cpp/llvm/raw_ostream.cpp
index 9f2942c..622e0bf 100644
--- a/wpiutil/src/main/native/cpp/llvm/raw_ostream.cpp
+++ b/wpiutil/src/main/native/cpp/llvm/raw_ostream.cpp
@@ -16,17 +16,14 @@
#endif
#include "wpi/raw_ostream.h"
-#include "wpi/STLExtras.h"
#include "wpi/SmallString.h"
#include "wpi/SmallVector.h"
#include "wpi/StringExtras.h"
#include "wpi/Compiler.h"
#include "wpi/ErrorHandling.h"
-#include "wpi/FileSystem.h"
-#include "wpi/Format.h"
#include "wpi/MathExtras.h"
-#include "wpi/NativeFormatting.h"
#include "wpi/WindowsError.h"
+#include "wpi/fs.h"
#include <algorithm>
#include <cctype>
#include <cerrno>
@@ -67,6 +64,14 @@
using namespace wpi;
+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.
@@ -113,32 +118,7 @@
assert(OutBufStart <= OutBufEnd && "Invalid size!");
}
-raw_ostream &raw_ostream::operator<<(unsigned long N) {
- write_integer(*this, static_cast<uint64_t>(N), 0, IntegerStyle::Integer);
- return *this;
-}
-
-raw_ostream &raw_ostream::operator<<(long N) {
- write_integer(*this, static_cast<int64_t>(N), 0, IntegerStyle::Integer);
- return *this;
-}
-
-raw_ostream &raw_ostream::operator<<(unsigned long long N) {
- write_integer(*this, static_cast<uint64_t>(N), 0, IntegerStyle::Integer);
- return *this;
-}
-
-raw_ostream &raw_ostream::operator<<(long long N) {
- write_integer(*this, static_cast<int64_t>(N), 0, IntegerStyle::Integer);
- return *this;
-}
-
-raw_ostream &raw_ostream::write_hex(unsigned long long N) {
- wpi::write_hex(*this, N, HexPrintStyle::Lower);
- return *this;
-}
-
-raw_ostream &raw_ostream::write_escaped(StringRef Str,
+raw_ostream &raw_ostream::write_escaped(std::string_view Str,
bool UseHexEscapes) {
for (unsigned char c : Str) {
switch (c) {
@@ -178,16 +158,6 @@
return *this;
}
-raw_ostream &raw_ostream::operator<<(const void *P) {
- wpi::write_hex(*this, (uintptr_t)P, HexPrintStyle::PrefixLower);
- return *this;
-}
-
-raw_ostream &raw_ostream::operator<<(double N) {
- wpi::write_double(*this, N, FloatStyle::Exponent);
- return *this;
-}
-
void raw_ostream::flush_nonempty() {
assert(OutBufCur > OutBufStart && "Invalid call to flush_nonempty.");
size_t Length = OutBufCur - OutBufStart;
@@ -277,170 +247,6 @@
OutBufCur += Size;
}
-// Formatted output.
-raw_ostream &raw_ostream::operator<<(const format_object_base &Fmt) {
- // If we have more than a few bytes left in our output buffer, try
- // formatting directly onto its end.
- size_t NextBufferSize = 127;
- size_t BufferBytesLeft = OutBufEnd - OutBufCur;
- if (BufferBytesLeft > 3) {
- size_t BytesUsed = Fmt.print(OutBufCur, BufferBytesLeft);
-
- // Common case is that we have plenty of space.
- if (BytesUsed <= BufferBytesLeft) {
- OutBufCur += BytesUsed;
- return *this;
- }
-
- // Otherwise, we overflowed and the return value tells us the size to try
- // again with.
- NextBufferSize = BytesUsed;
- }
-
- // If we got here, we didn't have enough space in the output buffer for the
- // string. Try printing into a SmallVector that is resized to have enough
- // space. Iterate until we win.
- SmallVector<char, 128> V;
-
- while (true) {
- V.resize(NextBufferSize);
-
- // Try formatting into the SmallVector.
- size_t BytesUsed = Fmt.print(V.data(), NextBufferSize);
-
- // If BytesUsed fit into the vector, we win.
- if (BytesUsed <= NextBufferSize)
- return write(V.data(), BytesUsed);
-
- // Otherwise, try again with a new size.
- assert(BytesUsed > NextBufferSize && "Didn't grow buffer!?");
- NextBufferSize = BytesUsed;
- }
-}
-
-raw_ostream &raw_ostream::operator<<(const FormattedString &FS) {
- if (FS.Str.size() >= FS.Width || FS.Justify == FormattedString::JustifyNone) {
- this->operator<<(FS.Str);
- return *this;
- }
- const size_t Difference = FS.Width - FS.Str.size();
- switch (FS.Justify) {
- case FormattedString::JustifyLeft:
- this->operator<<(FS.Str);
- this->indent(Difference);
- break;
- case FormattedString::JustifyRight:
- this->indent(Difference);
- this->operator<<(FS.Str);
- break;
- case FormattedString::JustifyCenter: {
- int PadAmount = Difference / 2;
- this->indent(PadAmount);
- this->operator<<(FS.Str);
- this->indent(Difference - PadAmount);
- break;
- }
- default:
- wpi_unreachable("Bad Justification");
- }
- return *this;
-}
-
-raw_ostream &raw_ostream::operator<<(const FormattedNumber &FN) {
- if (FN.Hex) {
- HexPrintStyle Style;
- if (FN.Upper && FN.HexPrefix)
- Style = HexPrintStyle::PrefixUpper;
- else if (FN.Upper && !FN.HexPrefix)
- Style = HexPrintStyle::Upper;
- else if (!FN.Upper && FN.HexPrefix)
- Style = HexPrintStyle::PrefixLower;
- else
- Style = HexPrintStyle::Lower;
- wpi::write_hex(*this, FN.HexValue, Style, FN.Width);
- } else {
- wpi::SmallString<16> Buffer;
- wpi::raw_svector_ostream Stream(Buffer);
- wpi::write_integer(Stream, FN.DecValue, 0, IntegerStyle::Integer);
- if (Buffer.size() < FN.Width)
- indent(FN.Width - Buffer.size());
- (*this) << Buffer;
- }
- return *this;
-}
-
-raw_ostream &raw_ostream::operator<<(const FormattedBytes &FB) {
- if (FB.Bytes.empty())
- return *this;
-
- size_t LineIndex = 0;
- auto Bytes = FB.Bytes;
- const size_t Size = Bytes.size();
- HexPrintStyle HPS = FB.Upper ? HexPrintStyle::Upper : HexPrintStyle::Lower;
- uint64_t OffsetWidth = 0;
- if (FB.FirstByteOffset.has_value()) {
- // Figure out how many nibbles are needed to print the largest offset
- // represented by this data set, so that we can align the offset field
- // to the right width.
- size_t Lines = Size / FB.NumPerLine;
- uint64_t MaxOffset = *FB.FirstByteOffset + Lines * FB.NumPerLine;
- unsigned Power = 0;
- if (MaxOffset > 0)
- Power = wpi::Log2_64_Ceil(MaxOffset);
- OffsetWidth = std::max<uint64_t>(4, wpi::alignTo(Power, 4) / 4);
- }
-
- // The width of a block of data including all spaces for group separators.
- unsigned NumByteGroups =
- alignTo(FB.NumPerLine, FB.ByteGroupSize) / FB.ByteGroupSize;
- unsigned BlockCharWidth = FB.NumPerLine * 2 + NumByteGroups - 1;
-
- while (!Bytes.empty()) {
- indent(FB.IndentLevel);
-
- if (FB.FirstByteOffset.has_value()) {
- uint64_t Offset = FB.FirstByteOffset.value();
- wpi::write_hex(*this, Offset + LineIndex, HPS, OffsetWidth);
- *this << ": ";
- }
-
- auto Line = Bytes.take_front(FB.NumPerLine);
-
- size_t CharsPrinted = 0;
- // Print the hex bytes for this line in groups
- for (size_t I = 0; I < Line.size(); ++I, CharsPrinted += 2) {
- if (I && (I % FB.ByteGroupSize) == 0) {
- ++CharsPrinted;
- *this << " ";
- }
- wpi::write_hex(*this, Line[I], HPS, 2);
- }
-
- if (FB.ASCII) {
- // Print any spaces needed for any bytes that we didn't print on this
- // line so that the ASCII bytes are correctly aligned.
- assert(BlockCharWidth >= CharsPrinted);
- indent(BlockCharWidth - CharsPrinted + 2);
- *this << "|";
-
- // Print the ASCII char values for each byte on this line
- for (uint8_t Byte : Line) {
- if (isPrint(Byte))
- *this << static_cast<char>(Byte);
- else
- *this << '.';
- }
- *this << '|';
- }
-
- Bytes = Bytes.drop_front(Line.size());
- LineIndex += Line.size();
- if (LineIndex < Size)
- *this << '\n';
- }
- return *this;
-}
-
template <char C>
static raw_ostream &write_padding(raw_ostream &OS, unsigned NumChars) {
static const char Chars[] = {C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C,
@@ -475,21 +281,13 @@
void raw_ostream::anchor() {}
//===----------------------------------------------------------------------===//
-// Formatted Output
-//===----------------------------------------------------------------------===//
-
-// Out of line virtual method.
-void format_object_base::home() {
-}
-
-//===----------------------------------------------------------------------===//
// raw_fd_ostream
//===----------------------------------------------------------------------===//
-static int getFD(StringRef Filename, std::error_code &EC,
- sys::fs::CreationDisposition Disp, sys::fs::FileAccess Access,
- sys::fs::OpenFlags Flags) {
- assert((Access & sys::fs::FA_Write) &&
+static int getFD(std::string_view Filename, std::error_code &EC,
+ fs::CreationDisposition Disp, fs::FileAccess Access,
+ fs::OpenFlags Flags) {
+ assert((Access & fs::FA_Write) &&
"Cannot make a raw_ostream from a read-only descriptor!");
// Handle "-" as stdout. Note that when we do this, we consider ourself
@@ -498,7 +296,7 @@
EC = std::error_code();
// If user requested binary then put stdout into binary mode if
// possible.
- if (!(Flags & sys::fs::OF_Text)) {
+ if (!(Flags & fs::OF_Text)) {
#if defined(_WIN32)
_setmode(_fileno(stdout), _O_BINARY);
#endif
@@ -506,39 +304,43 @@
return STDOUT_FILENO;
}
- int FD;
- if (Access & sys::fs::FA_Read)
- EC = sys::fs::openFileForReadWrite(Filename, FD, Disp, Flags);
- else
- EC = sys::fs::openFileForWrite(Filename, FD, Disp, Flags);
+ 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 {
+ 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;
return FD;
}
-raw_fd_ostream::raw_fd_ostream(StringRef Filename, std::error_code &EC)
- : raw_fd_ostream(Filename, EC, sys::fs::CD_CreateAlways, sys::fs::FA_Write,
- sys::fs::OF_None) {}
+raw_fd_ostream::raw_fd_ostream(std::string_view Filename, std::error_code &EC)
+ : raw_fd_ostream(Filename, EC, fs::CD_CreateAlways, fs::FA_Write,
+ fs::OF_None) {}
-raw_fd_ostream::raw_fd_ostream(StringRef Filename, std::error_code &EC,
- sys::fs::CreationDisposition Disp)
- : raw_fd_ostream(Filename, EC, Disp, sys::fs::FA_Write, sys::fs::OF_None) {}
+raw_fd_ostream::raw_fd_ostream(std::string_view Filename, std::error_code &EC,
+ fs::CreationDisposition Disp)
+ : raw_fd_ostream(Filename, EC, Disp, fs::FA_Write, fs::OF_None) {}
-raw_fd_ostream::raw_fd_ostream(StringRef Filename, std::error_code &EC,
- sys::fs::FileAccess Access)
- : raw_fd_ostream(Filename, EC, sys::fs::CD_CreateAlways, Access,
- sys::fs::OF_None) {}
+raw_fd_ostream::raw_fd_ostream(std::string_view Filename, std::error_code &EC,
+ fs::FileAccess Access)
+ : raw_fd_ostream(Filename, EC, fs::CD_CreateAlways, Access,
+ fs::OF_None) {}
-raw_fd_ostream::raw_fd_ostream(StringRef Filename, std::error_code &EC,
- sys::fs::OpenFlags Flags)
- : raw_fd_ostream(Filename, EC, sys::fs::CD_CreateAlways, sys::fs::FA_Write,
+raw_fd_ostream::raw_fd_ostream(std::string_view Filename, std::error_code &EC,
+ fs::OpenFlags Flags)
+ : raw_fd_ostream(Filename, EC, fs::CD_CreateAlways, fs::FA_Write,
Flags) {}
-raw_fd_ostream::raw_fd_ostream(StringRef Filename, std::error_code &EC,
- sys::fs::CreationDisposition Disp,
- sys::fs::FileAccess Access,
- sys::fs::OpenFlags Flags)
+raw_fd_ostream::raw_fd_ostream(std::string_view Filename, std::error_code &EC,
+ fs::CreationDisposition Disp,
+ fs::FileAccess Access,
+ fs::OpenFlags Flags)
: raw_fd_ostream(getFD(Filename, EC, Disp, Access, Flags), true) {}
/// FD is the file descriptor that this writes to. If ShouldClose is true, this
@@ -618,7 +420,7 @@
// 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.
@@ -661,7 +463,7 @@
// 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
@@ -783,7 +585,7 @@
raw_ostream &wpi::outs() {
// Set buffer settings to model stdout behavior.
std::error_code EC;
- static raw_fd_ostream* S = new raw_fd_ostream("-", EC, sys::fs::F_None);
+ static raw_fd_ostream* S = new raw_fd_ostream("-", EC, fs::F_None);
assert(!EC);
return *S;
}
diff --git a/wpiutil/src/main/native/cpp/mpack.cpp b/wpiutil/src/main/native/cpp/mpack.cpp
new file mode 100644
index 0000000..fbcb6a4
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/mpack.cpp
@@ -0,0 +1,7251 @@
+/**
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2015-2021 Nicholas Fraser and the MPack authors
+ *
+ * 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.
+ *
+ */
+
+/*
+ * This is the MPack 1.1 amalgamation package.
+ *
+ * http://github.com/ludocode/mpack
+ */
+
+#define MPACK_INTERNAL 1
+#define MPACK_EMIT_INLINE_DEFS 0
+
+#include "wpi/mpack.h"
+
+
+/* mpack/mpack-platform.c.c */
+
+
+// We define MPACK_EMIT_INLINE_DEFS and include mpack.h to emit
+// standalone definitions of all (non-static) inline functions in MPack.
+
+#define MPACK_INTERNAL 1
+#define MPACK_EMIT_INLINE_DEFS 0
+
+/* #include "mpack-platform.h" */
+/* #include "mpack.h" */
+
+MPACK_SILENCE_WARNINGS_BEGIN
+namespace mpack {
+
+#if MPACK_DEBUG
+
+#if MPACK_STDIO
+void mpack_assert_fail_format(const char* format, ...) {
+ char buffer[512];
+ va_list args;
+ va_start(args, format);
+ vsnprintf(buffer, sizeof(buffer), format, args);
+ va_end(args);
+ buffer[sizeof(buffer) - 1] = 0;
+ mpack_assert_fail_wrapper(buffer);
+}
+
+void mpack_break_hit_format(const char* format, ...) {
+ char buffer[512];
+ va_list args;
+ va_start(args, format);
+ vsnprintf(buffer, sizeof(buffer), format, args);
+ va_end(args);
+ buffer[sizeof(buffer) - 1] = 0;
+ mpack_break_hit(buffer);
+}
+#endif
+
+#if !MPACK_CUSTOM_ASSERT
+void mpack_assert_fail(const char* message) {
+ MPACK_UNUSED(message);
+
+ #if MPACK_STDIO
+ fprintf(stderr, "%s\n", message);
+ #endif
+}
+#endif
+
+// We split the assert failure from the wrapper so that a
+// custom assert function can return.
+void mpack_assert_fail_wrapper(const char* message) {
+
+ #ifdef MPACK_GCOV
+ // gcov marks even __builtin_unreachable() as an uncovered line. this
+ // silences it.
+ (mpack_assert_fail(message), __builtin_unreachable());
+
+ #else
+ mpack_assert_fail(message);
+
+ // mpack_assert_fail() is not supposed to return. in case it does, we
+ // abort.
+
+ #if !MPACK_NO_BUILTINS
+ #if defined(__GNUC__) || defined(__clang__)
+ __builtin_trap();
+ #elif defined(WIN32)
+ __debugbreak();
+ #endif
+ #endif
+
+ #if (defined(__GNUC__) || defined(__clang__)) && !MPACK_NO_BUILTINS
+ __builtin_abort();
+ #elif MPACK_STDLIB
+ abort();
+ #endif
+
+ MPACK_UNREACHABLE;
+ #endif
+}
+
+#if !MPACK_CUSTOM_BREAK
+
+// If we have a custom assert handler, break wraps it by default.
+// This allows users of MPack to only implement mpack_assert_fail() without
+// having to worry about the difference between assert and break.
+//
+// MPACK_CUSTOM_BREAK is available to define a separate break handler
+// (which is needed by the unit test suite), but this is not offered in
+// mpack-config.h for simplicity.
+
+#if MPACK_CUSTOM_ASSERT
+void mpack_break_hit(const char* message) {
+ mpack_assert_fail_wrapper(message);
+}
+#else
+void mpack_break_hit(const char* message) {
+ MPACK_UNUSED(message);
+
+ #if MPACK_STDIO
+ fprintf(stderr, "%s\n", message);
+ #endif
+
+ #if defined(__GNUC__) || defined(__clang__) && !MPACK_NO_BUILTINS
+ __builtin_trap();
+ #elif defined(WIN32) && !MPACK_NO_BUILTINS
+ __debugbreak();
+ #elif MPACK_STDLIB
+ abort();
+ #endif
+}
+#endif
+
+#endif
+
+#endif
+
+
+
+// The below are adapted from the C wikibook:
+// https://en.wikibooks.org/wiki/C_Programming/Strings
+
+#ifndef mpack_memcmp
+int mpack_memcmp(const void* s1, const void* s2, size_t n) {
+ const unsigned char *us1 = (const unsigned char *) s1;
+ const unsigned char *us2 = (const unsigned char *) s2;
+ while (n-- != 0) {
+ if (*us1 != *us2)
+ return (*us1 < *us2) ? -1 : +1;
+ us1++;
+ us2++;
+ }
+ return 0;
+}
+#endif
+
+#ifndef mpack_memcpy
+void* mpack_memcpy(void* MPACK_RESTRICT s1, const void* MPACK_RESTRICT s2, size_t n) {
+ char* MPACK_RESTRICT dst = (char *)s1;
+ const char* MPACK_RESTRICT src = (const char *)s2;
+ while (n-- != 0)
+ *dst++ = *src++;
+ return s1;
+}
+#endif
+
+#ifndef mpack_memmove
+void* mpack_memmove(void* s1, const void* s2, size_t n) {
+ char *p1 = (char *)s1;
+ const char *p2 = (const char *)s2;
+ if (p2 < p1 && p1 < p2 + n) {
+ p2 += n;
+ p1 += n;
+ while (n-- != 0)
+ *--p1 = *--p2;
+ } else
+ while (n-- != 0)
+ *p1++ = *p2++;
+ return s1;
+}
+#endif
+
+#ifndef mpack_memset
+void* mpack_memset(void* s, int c, size_t n) {
+ unsigned char *us = (unsigned char *)s;
+ unsigned char uc = (unsigned char)c;
+ while (n-- != 0)
+ *us++ = uc;
+ return s;
+}
+#endif
+
+#ifndef mpack_strlen
+size_t mpack_strlen(const char* s) {
+ const char* p = s;
+ while (*p != '\0')
+ p++;
+ return (size_t)(p - s);
+}
+#endif
+
+
+
+#if defined(MPACK_MALLOC) && !defined(MPACK_REALLOC)
+void* mpack_realloc(void* old_ptr, size_t used_size, size_t new_size) {
+ if (new_size == 0) {
+ if (old_ptr)
+ MPACK_FREE(old_ptr);
+ return NULL;
+ }
+
+ void* new_ptr = MPACK_MALLOC(new_size);
+ if (new_ptr == NULL)
+ return NULL;
+
+ mpack_memcpy(new_ptr, old_ptr, used_size);
+ MPACK_FREE(old_ptr);
+ return new_ptr;
+}
+#endif
+
+MPACK_SILENCE_WARNINGS_END
+
+/* mpack/mpack-common.c.c */
+
+#define MPACK_INTERNAL 1
+
+/* #include "mpack-common.h" */
+
+MPACK_SILENCE_WARNINGS_BEGIN
+
+const char* mpack_error_to_string(mpack_error_t error) {
+ #if MPACK_STRINGS
+ switch (error) {
+ #define MPACK_ERROR_STRING_CASE(e) case e: return #e
+ MPACK_ERROR_STRING_CASE(mpack_ok);
+ MPACK_ERROR_STRING_CASE(mpack_error_io);
+ MPACK_ERROR_STRING_CASE(mpack_error_invalid);
+ MPACK_ERROR_STRING_CASE(mpack_error_unsupported);
+ MPACK_ERROR_STRING_CASE(mpack_error_type);
+ MPACK_ERROR_STRING_CASE(mpack_error_too_big);
+ MPACK_ERROR_STRING_CASE(mpack_error_memory);
+ MPACK_ERROR_STRING_CASE(mpack_error_bug);
+ MPACK_ERROR_STRING_CASE(mpack_error_data);
+ MPACK_ERROR_STRING_CASE(mpack_error_eof);
+ #undef MPACK_ERROR_STRING_CASE
+ }
+ mpack_assert(0, "unrecognized error %i", (int)error);
+ return "(unknown mpack_error_t)";
+ #else
+ MPACK_UNUSED(error);
+ return "";
+ #endif
+}
+
+const char* mpack_type_to_string(mpack_type_t type) {
+ #if MPACK_STRINGS
+ switch (type) {
+ #define MPACK_TYPE_STRING_CASE(e) case e: return #e
+ MPACK_TYPE_STRING_CASE(mpack_type_missing);
+ MPACK_TYPE_STRING_CASE(mpack_type_nil);
+ MPACK_TYPE_STRING_CASE(mpack_type_bool);
+ MPACK_TYPE_STRING_CASE(mpack_type_float);
+ MPACK_TYPE_STRING_CASE(mpack_type_double);
+ MPACK_TYPE_STRING_CASE(mpack_type_int);
+ MPACK_TYPE_STRING_CASE(mpack_type_uint);
+ MPACK_TYPE_STRING_CASE(mpack_type_str);
+ MPACK_TYPE_STRING_CASE(mpack_type_bin);
+ MPACK_TYPE_STRING_CASE(mpack_type_array);
+ MPACK_TYPE_STRING_CASE(mpack_type_map);
+ #if MPACK_EXTENSIONS
+ MPACK_TYPE_STRING_CASE(mpack_type_ext);
+ #endif
+ #undef MPACK_TYPE_STRING_CASE
+ }
+ mpack_assert(0, "unrecognized type %i", (int)type);
+ return "(unknown mpack_type_t)";
+ #else
+ MPACK_UNUSED(type);
+ return "";
+ #endif
+}
+
+int mpack_tag_cmp(mpack_tag_t left, mpack_tag_t right) {
+
+ // positive numbers may be stored as int; convert to uint
+ if (left.type == mpack_type_int && left.v.i >= 0) {
+ left.type = mpack_type_uint;
+ left.v.u = (uint64_t)left.v.i;
+ }
+ if (right.type == mpack_type_int && right.v.i >= 0) {
+ right.type = mpack_type_uint;
+ right.v.u = (uint64_t)right.v.i;
+ }
+
+ if (left.type != right.type)
+ return ((int)left.type < (int)right.type) ? -1 : 1;
+
+ switch (left.type) {
+ case mpack_type_missing: // fallthrough
+ case mpack_type_nil:
+ return 0;
+
+ case mpack_type_bool:
+ return (int)left.v.b - (int)right.v.b;
+
+ case mpack_type_int:
+ if (left.v.i == right.v.i)
+ return 0;
+ return (left.v.i < right.v.i) ? -1 : 1;
+
+ case mpack_type_uint:
+ if (left.v.u == right.v.u)
+ return 0;
+ return (left.v.u < right.v.u) ? -1 : 1;
+
+ case mpack_type_array:
+ case mpack_type_map:
+ if (left.v.n == right.v.n)
+ return 0;
+ return (left.v.n < right.v.n) ? -1 : 1;
+
+ case mpack_type_str:
+ case mpack_type_bin:
+ if (left.v.l == right.v.l)
+ return 0;
+ return (left.v.l < right.v.l) ? -1 : 1;
+
+ #if MPACK_EXTENSIONS
+ case mpack_type_ext:
+ if (left.exttype == right.exttype) {
+ if (left.v.l == right.v.l)
+ return 0;
+ return (left.v.l < right.v.l) ? -1 : 1;
+ }
+ return (int)left.exttype - (int)right.exttype;
+ #endif
+
+ // floats should not normally be compared for equality. we compare
+ // with memcmp() to silence compiler warnings, but this will return
+ // equal if both are NaNs with the same representation (though we may
+ // want this, for instance if you are for some bizarre reason using
+ // floats as map keys.) i'm not sure what the right thing to
+ // do is here. check for NaN first? always return false if the type
+ // is float? use operator== and pragmas to silence compiler warning?
+ // please send me your suggestions.
+ // note also that we don't convert floats to doubles, so when this is
+ // used for ordering purposes, all floats are ordered before all
+ // doubles.
+ case mpack_type_float:
+ return mpack_memcmp(&left.v.f, &right.v.f, sizeof(left.v.f));
+ case mpack_type_double:
+ return mpack_memcmp(&left.v.d, &right.v.d, sizeof(left.v.d));
+ }
+
+ mpack_assert(0, "unrecognized type %i", (int)left.type);
+ return false;
+}
+
+#if MPACK_DEBUG && MPACK_STDIO
+static char mpack_hex_char(uint8_t hex_value) {
+ // Older compilers (e.g. GCC 4.4.7) promote the result of this ternary to
+ // int and warn under -Wconversion, so we have to cast it back to char.
+ return (char)((hex_value < 10) ? (char)('0' + hex_value) : (char)('a' + (hex_value - 10)));
+}
+
+static void mpack_tag_debug_complete_bin_ext(mpack_tag_t tag, size_t string_length, char* buffer, size_t buffer_size,
+ const char* prefix, size_t prefix_size)
+{
+ // If at any point in this function we run out of space in the buffer, we
+ // bail out. The outer tag print wrapper will make sure we have a
+ // null-terminator.
+
+ if (string_length == 0 || string_length >= buffer_size)
+ return;
+ buffer += string_length;
+ buffer_size -= string_length;
+
+ size_t total = mpack_tag_bytes(&tag);
+ if (total == 0) {
+ strncpy(buffer, ">", buffer_size);
+ return;
+ }
+
+ strncpy(buffer, ": ", buffer_size);
+ if (buffer_size < 2)
+ return;
+ buffer += 2;
+ buffer_size -= 2;
+
+ size_t hex_bytes = 0;
+ size_t i;
+ for (i = 0; i < MPACK_PRINT_BYTE_COUNT && i < prefix_size && buffer_size > 2; ++i) {
+ uint8_t byte = (uint8_t)prefix[i];
+ buffer[0] = mpack_hex_char((uint8_t)(byte >> 4));
+ buffer[1] = mpack_hex_char((uint8_t)(byte & 0xfu));
+ buffer += 2;
+ buffer_size -= 2;
+ ++hex_bytes;
+ }
+
+ if (buffer_size != 0)
+ mpack_snprintf(buffer, buffer_size, "%s>", (total > hex_bytes) ? "..." : "");
+}
+
+static void mpack_tag_debug_pseudo_json_bin(mpack_tag_t tag, char* buffer, size_t buffer_size,
+ 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);
+ mpack_tag_debug_complete_bin_ext(tag, length, buffer, buffer_size, prefix, prefix_size);
+}
+
+#if MPACK_EXTENSIONS
+static void mpack_tag_debug_pseudo_json_ext(mpack_tag_t tag, char* buffer, size_t buffer_size,
+ 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",
+ mpack_tag_ext_exttype(&tag), mpack_tag_ext_length(&tag));
+ mpack_tag_debug_complete_bin_ext(tag, length, buffer, buffer_size, prefix, prefix_size);
+}
+#endif
+
+static void mpack_tag_debug_pseudo_json_impl(mpack_tag_t tag, char* buffer, size_t buffer_size,
+ const char* prefix, size_t prefix_size)
+{
+ switch (tag.type) {
+ case mpack_type_missing:
+ mpack_snprintf(buffer, buffer_size, "<missing!>");
+ return;
+ case mpack_type_nil:
+ mpack_snprintf(buffer, buffer_size, "null");
+ return;
+ case mpack_type_bool:
+ mpack_snprintf(buffer, buffer_size, tag.v.b ? "true" : "false");
+ return;
+ case mpack_type_int:
+ mpack_snprintf(buffer, buffer_size, "%" PRIi64, tag.v.i);
+ return;
+ case mpack_type_uint:
+ mpack_snprintf(buffer, buffer_size, "%" PRIu64, tag.v.u);
+ return;
+ case mpack_type_float:
+ #if MPACK_FLOAT
+ mpack_snprintf(buffer, buffer_size, "%f", tag.v.f);
+ #else
+ mpack_snprintf(buffer, buffer_size, "<float>");
+ #endif
+ return;
+ case mpack_type_double:
+ #if MPACK_DOUBLE
+ mpack_snprintf(buffer, buffer_size, "%f", tag.v.d);
+ #else
+ mpack_snprintf(buffer, buffer_size, "<double>");
+ #endif
+ return;
+
+ case mpack_type_str:
+ mpack_snprintf(buffer, buffer_size, "<string of %u bytes>", tag.v.l);
+ return;
+ case mpack_type_bin:
+ mpack_tag_debug_pseudo_json_bin(tag, buffer, buffer_size, prefix, prefix_size);
+ return;
+ #if MPACK_EXTENSIONS
+ case mpack_type_ext:
+ mpack_tag_debug_pseudo_json_ext(tag, buffer, buffer_size, prefix, prefix_size);
+ return;
+ #endif
+
+ case mpack_type_array:
+ mpack_snprintf(buffer, buffer_size, "<array of %u elements>", tag.v.n);
+ return;
+ case mpack_type_map:
+ mpack_snprintf(buffer, buffer_size, "<map of %u key-value pairs>", tag.v.n);
+ return;
+ }
+
+ mpack_snprintf(buffer, buffer_size, "<unknown!>");
+}
+
+void mpack_tag_debug_pseudo_json(mpack_tag_t tag, char* buffer, size_t buffer_size,
+ const char* prefix, size_t prefix_size)
+{
+ mpack_assert(buffer_size > 0, "buffer size cannot be zero!");
+ buffer[0] = 0;
+
+ mpack_tag_debug_pseudo_json_impl(tag, buffer, buffer_size, prefix, prefix_size);
+
+ // We always null-terminate the buffer manually just in case the snprintf()
+ // function doesn't null-terminate when the string doesn't fit.
+ buffer[buffer_size - 1] = 0;
+}
+
+static void mpack_tag_debug_describe_impl(mpack_tag_t tag, char* buffer, size_t buffer_size) {
+ switch (tag.type) {
+ case mpack_type_missing:
+ mpack_snprintf(buffer, buffer_size, "missing");
+ return;
+ case mpack_type_nil:
+ mpack_snprintf(buffer, buffer_size, "nil");
+ return;
+ case mpack_type_bool:
+ mpack_snprintf(buffer, buffer_size, tag.v.b ? "true" : "false");
+ return;
+ case mpack_type_int:
+ mpack_snprintf(buffer, buffer_size, "int %" PRIi64, tag.v.i);
+ return;
+ case mpack_type_uint:
+ mpack_snprintf(buffer, buffer_size, "uint %" PRIu64, tag.v.u);
+ return;
+ case mpack_type_float:
+ #if MPACK_FLOAT
+ mpack_snprintf(buffer, buffer_size, "float %f", tag.v.f);
+ #else
+ mpack_snprintf(buffer, buffer_size, "float");
+ #endif
+ return;
+ case mpack_type_double:
+ #if MPACK_DOUBLE
+ mpack_snprintf(buffer, buffer_size, "double %f", tag.v.d);
+ #else
+ mpack_snprintf(buffer, buffer_size, "double");
+ #endif
+ return;
+ case mpack_type_str:
+ mpack_snprintf(buffer, buffer_size, "str of %u bytes", tag.v.l);
+ return;
+ case mpack_type_bin:
+ mpack_snprintf(buffer, buffer_size, "bin of %u bytes", tag.v.l);
+ return;
+ #if MPACK_EXTENSIONS
+ case mpack_type_ext:
+ mpack_snprintf(buffer, buffer_size, "ext of type %i, %u 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);
+ return;
+ case mpack_type_map:
+ mpack_snprintf(buffer, buffer_size, "map of %u key-value pairs", tag.v.n);
+ return;
+ }
+
+ mpack_snprintf(buffer, buffer_size, "unknown!");
+}
+
+void mpack_tag_debug_describe(mpack_tag_t tag, char* buffer, size_t buffer_size) {
+ mpack_assert(buffer_size > 0, "buffer size cannot be zero!");
+ buffer[0] = 0;
+
+ mpack_tag_debug_describe_impl(tag, buffer, buffer_size);
+
+ // We always null-terminate the buffer manually just in case the snprintf()
+ // function doesn't null-terminate when the string doesn't fit.
+ buffer[buffer_size - 1] = 0;
+}
+#endif
+
+
+
+#if MPACK_READ_TRACKING || MPACK_WRITE_TRACKING
+
+#ifndef MPACK_TRACKING_INITIAL_CAPACITY
+// seems like a reasonable number. we grow by doubling, and it only
+// needs to be as long as the maximum depth of the message.
+#define MPACK_TRACKING_INITIAL_CAPACITY 8
+#endif
+
+mpack_error_t mpack_track_init(mpack_track_t* track) {
+ track->count = 0;
+ track->capacity = MPACK_TRACKING_INITIAL_CAPACITY;
+ track->elements = (mpack_track_element_t*)MPACK_MALLOC(sizeof(mpack_track_element_t) * track->capacity);
+ if (track->elements == NULL)
+ return mpack_error_memory;
+ return mpack_ok;
+}
+
+mpack_error_t mpack_track_grow(mpack_track_t* track) {
+ mpack_assert(track->elements, "null track elements!");
+ mpack_assert(track->count == track->capacity, "incorrect growing?");
+
+ size_t new_capacity = track->capacity * 2;
+
+ mpack_track_element_t* new_elements = (mpack_track_element_t*)mpack_realloc(track->elements,
+ sizeof(mpack_track_element_t) * track->count, sizeof(mpack_track_element_t) * new_capacity);
+ if (new_elements == NULL)
+ return mpack_error_memory;
+
+ track->elements = new_elements;
+ track->capacity = new_capacity;
+ return mpack_ok;
+}
+
+mpack_error_t mpack_track_push(mpack_track_t* track, mpack_type_t type, uint32_t count) {
+ mpack_assert(track->elements, "null track elements!");
+ mpack_log("track pushing %s count %i\n", mpack_type_to_string(type), (int)count);
+
+ // grow if needed
+ if (track->count == track->capacity) {
+ mpack_error_t error = mpack_track_grow(track);
+ if (error != mpack_ok)
+ return error;
+ }
+
+ // insert new track
+ track->elements[track->count].type = type;
+ track->elements[track->count].left = count;
+ track->elements[track->count].builder = false;
+ track->elements[track->count].key_needs_value = false;
+ ++track->count;
+ return mpack_ok;
+}
+
+// TODO dedupe this
+mpack_error_t mpack_track_push_builder(mpack_track_t* track, mpack_type_t type) {
+ mpack_assert(track->elements, "null track elements!");
+ mpack_log("track pushing %s builder\n", mpack_type_to_string(type));
+
+ // grow if needed
+ if (track->count == track->capacity) {
+ mpack_error_t error = mpack_track_grow(track);
+ if (error != mpack_ok)
+ return error;
+ }
+
+ // insert new track
+ track->elements[track->count].type = type;
+ track->elements[track->count].left = 0;
+ track->elements[track->count].builder = true;
+ track->elements[track->count].key_needs_value = false;
+ ++track->count;
+ return mpack_ok;
+}
+
+static mpack_error_t mpack_track_pop_impl(mpack_track_t* track, mpack_type_t type, bool builder) {
+ mpack_assert(track->elements, "null track elements!");
+ mpack_log("track popping %s\n", mpack_type_to_string(type));
+
+ if (track->count == 0) {
+ mpack_break("attempting to close a %s but nothing was opened!", mpack_type_to_string(type));
+ return mpack_error_bug;
+ }
+
+ mpack_track_element_t* element = &track->elements[track->count - 1];
+
+ if (element->type != type) {
+ mpack_break("attempting to close a %s but the open element is a %s!",
+ mpack_type_to_string(type), mpack_type_to_string(element->type));
+ return mpack_error_bug;
+ }
+
+ if (element->key_needs_value) {
+ mpack_assert(type == mpack_type_map, "key_needs_value can only be true for maps!");
+ mpack_break("attempting to close a %s but an odd number of elements were written",
+ mpack_type_to_string(type));
+ return mpack_error_bug;
+ }
+
+ if (element->left != 0) {
+ mpack_break("attempting to close a %s but there are %i %s left",
+ mpack_type_to_string(type), element->left,
+ (type == mpack_type_map || type == mpack_type_array) ? "elements" : "bytes");
+ return mpack_error_bug;
+ }
+
+ if (element->builder != builder) {
+ mpack_break("attempting to pop a %sbuilder but the open element is %sa builder",
+ builder ? "" : "non-",
+ element->builder ? "" : "not ");
+ return mpack_error_bug;
+ }
+
+ --track->count;
+ return mpack_ok;
+}
+
+mpack_error_t mpack_track_pop(mpack_track_t* track, mpack_type_t type) {
+ return mpack_track_pop_impl(track, type, false);
+}
+
+mpack_error_t mpack_track_pop_builder(mpack_track_t* track, mpack_type_t type) {
+ return mpack_track_pop_impl(track, type, true);
+}
+
+mpack_error_t mpack_track_peek_element(mpack_track_t* track, bool read) {
+ MPACK_UNUSED(read);
+ mpack_assert(track->elements, "null track elements!");
+
+ // if there are no open elements, that's fine, we can read/write elements at will
+ if (track->count == 0)
+ return mpack_ok;
+
+ mpack_track_element_t* element = &track->elements[track->count - 1];
+
+ if (element->type != mpack_type_map && element->type != mpack_type_array) {
+ mpack_break("elements cannot be %s within an %s", read ? "read" : "written",
+ mpack_type_to_string(element->type));
+ return mpack_error_bug;
+ }
+
+ if (!element->builder && element->left == 0 && !element->key_needs_value) {
+ mpack_break("too many elements %s for %s", read ? "read" : "written",
+ mpack_type_to_string(element->type));
+ return mpack_error_bug;
+ }
+
+ return mpack_ok;
+}
+
+mpack_error_t mpack_track_element(mpack_track_t* track, bool read) {
+ mpack_error_t error = mpack_track_peek_element(track, read);
+ if (track->count == 0 || error != mpack_ok)
+ return error;
+
+ mpack_track_element_t* element = &track->elements[track->count - 1];
+
+ if (element->type == mpack_type_map) {
+ if (!element->key_needs_value) {
+ element->key_needs_value = true;
+ return mpack_ok; // don't decrement
+ }
+ element->key_needs_value = false;
+ }
+
+ if (!element->builder)
+ --element->left;
+ return mpack_ok;
+}
+
+mpack_error_t mpack_track_bytes(mpack_track_t* track, bool read, size_t count) {
+ MPACK_UNUSED(read);
+ mpack_assert(track->elements, "null track elements!");
+
+ if (count > MPACK_UINT32_MAX) {
+ mpack_break("%s more bytes than could possibly fit in a str/bin/ext!",
+ read ? "reading" : "writing");
+ return mpack_error_bug;
+ }
+
+ if (track->count == 0) {
+ mpack_break("bytes cannot be %s with no open bin, str or ext", read ? "read" : "written");
+ return mpack_error_bug;
+ }
+
+ mpack_track_element_t* element = &track->elements[track->count - 1];
+
+ if (element->type == mpack_type_map || element->type == mpack_type_array) {
+ mpack_break("bytes cannot be %s within an %s", read ? "read" : "written",
+ mpack_type_to_string(element->type));
+ return mpack_error_bug;
+ }
+
+ if (element->left < count) {
+ mpack_break("too many bytes %s for %s", read ? "read" : "written",
+ mpack_type_to_string(element->type));
+ return mpack_error_bug;
+ }
+
+ element->left -= (uint32_t)count;
+ return mpack_ok;
+}
+
+mpack_error_t mpack_track_str_bytes_all(mpack_track_t* track, bool read, size_t count) {
+ mpack_error_t error = mpack_track_bytes(track, read, count);
+ if (error != mpack_ok)
+ return error;
+
+ mpack_track_element_t* element = &track->elements[track->count - 1];
+
+ if (element->type != mpack_type_str) {
+ mpack_break("the open type must be a string, not a %s", mpack_type_to_string(element->type));
+ return mpack_error_bug;
+ }
+
+ if (element->left != 0) {
+ mpack_break("not all bytes were read; the wrong byte count was requested for a string read.");
+ return mpack_error_bug;
+ }
+
+ return mpack_ok;
+}
+
+mpack_error_t mpack_track_check_empty(mpack_track_t* track) {
+ if (track->count != 0) {
+ mpack_break("unclosed %s", mpack_type_to_string(track->elements[0].type));
+ return mpack_error_bug;
+ }
+ return mpack_ok;
+}
+
+mpack_error_t mpack_track_destroy(mpack_track_t* track, bool cancel) {
+ mpack_error_t error = cancel ? mpack_ok : mpack_track_check_empty(track);
+ if (track->elements) {
+ MPACK_FREE(track->elements);
+ track->elements = NULL;
+ }
+ return error;
+}
+#endif
+
+
+
+static bool mpack_utf8_check_impl(const uint8_t* str, size_t count, bool allow_null) {
+ while (count > 0) {
+ uint8_t lead = str[0];
+
+ // NUL
+ if (!allow_null && lead == '\0') // we don't allow NUL bytes in MPack C-strings
+ return false;
+
+ // ASCII
+ if (lead <= 0x7F) {
+ ++str;
+ --count;
+
+ // 2-byte sequence
+ } else if ((lead & 0xE0) == 0xC0) {
+ if (count < 2) // truncated sequence
+ return false;
+
+ uint8_t cont = str[1];
+ if ((cont & 0xC0) != 0x80) // not a continuation byte
+ return false;
+
+ str += 2;
+ count -= 2;
+
+ uint32_t z = ((uint32_t)(lead & ~0xE0) << 6) |
+ (uint32_t)(cont & ~0xC0);
+
+ if (z < 0x80) // overlong sequence
+ return false;
+
+ // 3-byte sequence
+ } else if ((lead & 0xF0) == 0xE0) {
+ if (count < 3) // truncated sequence
+ return false;
+
+ uint8_t cont1 = str[1];
+ if ((cont1 & 0xC0) != 0x80) // not a continuation byte
+ return false;
+ uint8_t cont2 = str[2];
+ if ((cont2 & 0xC0) != 0x80) // not a continuation byte
+ return false;
+
+ str += 3;
+ count -= 3;
+
+ uint32_t z = ((uint32_t)(lead & ~0xF0) << 12) |
+ ((uint32_t)(cont1 & ~0xC0) << 6) |
+ (uint32_t)(cont2 & ~0xC0);
+
+ if (z < 0x800) // overlong sequence
+ return false;
+ if (z >= 0xD800 && z <= 0xDFFF) // surrogate
+ return false;
+
+ // 4-byte sequence
+ } else if ((lead & 0xF8) == 0xF0) {
+ if (count < 4) // truncated sequence
+ return false;
+
+ uint8_t cont1 = str[1];
+ if ((cont1 & 0xC0) != 0x80) // not a continuation byte
+ return false;
+ uint8_t cont2 = str[2];
+ if ((cont2 & 0xC0) != 0x80) // not a continuation byte
+ return false;
+ uint8_t cont3 = str[3];
+ if ((cont3 & 0xC0) != 0x80) // not a continuation byte
+ return false;
+
+ str += 4;
+ count -= 4;
+
+ uint32_t z = ((uint32_t)(lead & ~0xF8) << 18) |
+ ((uint32_t)(cont1 & ~0xC0) << 12) |
+ ((uint32_t)(cont2 & ~0xC0) << 6) |
+ (uint32_t)(cont3 & ~0xC0);
+
+ if (z < 0x10000) // overlong sequence
+ return false;
+ if (z > 0x10FFFF) // codepoint limit
+ return false;
+
+ } else {
+ return false; // continuation byte without a lead, or lead for a 5-byte sequence or longer
+ }
+ }
+ return true;
+}
+
+bool mpack_utf8_check(const char* str, size_t bytes) {
+ return mpack_utf8_check_impl((const uint8_t*)str, bytes, true);
+}
+
+bool mpack_utf8_check_no_null(const char* str, size_t bytes) {
+ return mpack_utf8_check_impl((const uint8_t*)str, bytes, false);
+}
+
+bool mpack_str_check_no_null(const char* str, size_t bytes) {
+ size_t i;
+ for (i = 0; i < bytes; ++i)
+ if (str[i] == '\0')
+ return false;
+ return true;
+}
+
+#if MPACK_DEBUG && MPACK_STDIO
+void mpack_print_append(mpack_print_t* print, const char* data, size_t count) {
+
+ // copy whatever fits into the buffer
+ size_t copy = print->size - print->count;
+ if (copy > count)
+ copy = count;
+ mpack_memcpy(print->buffer + print->count, data, copy);
+ print->count += copy;
+ data += copy;
+ count -= copy;
+
+ // if we don't need to flush or can't flush there's nothing else to do
+ if (count == 0 || print->callback == NULL)
+ return;
+
+ // flush the buffer
+ print->callback(print->context, print->buffer, print->count);
+
+ if (count > print->size / 2) {
+ // flush the rest of the data
+ print->count = 0;
+ print->callback(print->context, data, count);
+ } else {
+ // copy the rest of the data into the buffer
+ mpack_memcpy(print->buffer, data, count);
+ print->count = count;
+ }
+
+}
+
+void mpack_print_flush(mpack_print_t* print) {
+ if (print->count > 0 && print->callback != NULL) {
+ print->callback(print->context, print->buffer, print->count);
+ print->count = 0;
+ }
+}
+
+void mpack_print_file_callback(void* context, const char* data, size_t count) {
+ FILE* file = (FILE*)context;
+ fwrite(data, 1, count, file);
+}
+#endif
+
+MPACK_SILENCE_WARNINGS_END
+
+/* mpack/mpack-writer.c.c */
+
+#define MPACK_INTERNAL 1
+
+/* #include "mpack-writer.h" */
+
+MPACK_SILENCE_WARNINGS_BEGIN
+
+#if MPACK_WRITER
+
+#if MPACK_BUILDER
+static void mpack_builder_flush(mpack_writer_t* writer);
+#endif
+
+#if MPACK_WRITE_TRACKING
+static void mpack_writer_flag_if_error(mpack_writer_t* writer, mpack_error_t error) {
+ if (error != mpack_ok)
+ mpack_writer_flag_error(writer, error);
+}
+
+void mpack_writer_track_push(mpack_writer_t* writer, mpack_type_t type, uint32_t count) {
+ if (writer->error == mpack_ok)
+ mpack_writer_flag_if_error(writer, mpack_track_push(&writer->track, type, count));
+}
+
+void mpack_writer_track_push_builder(mpack_writer_t* writer, mpack_type_t type) {
+ if (writer->error == mpack_ok)
+ mpack_writer_flag_if_error(writer, mpack_track_push_builder(&writer->track, type));
+}
+
+void mpack_writer_track_pop(mpack_writer_t* writer, mpack_type_t type) {
+ if (writer->error == mpack_ok)
+ mpack_writer_flag_if_error(writer, mpack_track_pop(&writer->track, type));
+}
+
+void mpack_writer_track_pop_builder(mpack_writer_t* writer, mpack_type_t type) {
+ if (writer->error == mpack_ok)
+ mpack_writer_flag_if_error(writer, mpack_track_pop_builder(&writer->track, type));
+}
+
+void mpack_writer_track_bytes(mpack_writer_t* writer, size_t count) {
+ if (writer->error == mpack_ok)
+ mpack_writer_flag_if_error(writer, mpack_track_bytes(&writer->track, false, count));
+}
+#endif
+
+// This should probably be renamed. It's not solely used for tracking.
+static inline void mpack_writer_track_element(mpack_writer_t* writer) {
+ (void)writer;
+
+ #if MPACK_WRITE_TRACKING
+ if (writer->error == mpack_ok)
+ mpack_writer_flag_if_error(writer, mpack_track_element(&writer->track, false));
+ #endif
+
+ #if MPACK_BUILDER
+ if (writer->builder.current_build != NULL) {
+ mpack_build_t* build = writer->builder.current_build;
+ // We only track this write if it's not nested within another non-build
+ // map or array.
+ 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);
+ } else if (build->key_needs_value) {
+ build->key_needs_value = false;
+ ++build->count;
+ } else {
+ build->key_needs_value = true;
+ }
+ }
+ }
+ #endif
+}
+
+static void mpack_writer_clear(mpack_writer_t* writer) {
+ #if MPACK_COMPATIBILITY
+ writer->version = mpack_version_current;
+ #endif
+ writer->flush = NULL;
+ writer->error_fn = NULL;
+ writer->teardown = NULL;
+ writer->context = NULL;
+
+ writer->buffer = NULL;
+ writer->position = NULL;
+ writer->end = NULL;
+ writer->error = mpack_ok;
+
+ #if MPACK_WRITE_TRACKING
+ mpack_memset(&writer->track, 0, sizeof(writer->track));
+ #endif
+
+ #if MPACK_BUILDER
+ writer->builder.current_build = NULL;
+ writer->builder.latest_build = NULL;
+ writer->builder.current_page = NULL;
+ writer->builder.pages = NULL;
+ writer->builder.stash_buffer = NULL;
+ writer->builder.stash_position = NULL;
+ writer->builder.stash_end = NULL;
+ #endif
+}
+
+void mpack_writer_init(mpack_writer_t* writer, char* buffer, size_t size) {
+ mpack_assert(buffer != NULL, "cannot initialize writer with empty buffer");
+ mpack_writer_clear(writer);
+ writer->buffer = buffer;
+ writer->position = buffer;
+ writer->end = writer->buffer + size;
+
+ #if MPACK_WRITE_TRACKING
+ mpack_writer_flag_if_error(writer, mpack_track_init(&writer->track));
+ #endif
+
+ mpack_log("===========================\n");
+ mpack_log("initializing writer with buffer size %i\n", (int)size);
+}
+
+void mpack_writer_init_error(mpack_writer_t* writer, mpack_error_t error) {
+ mpack_writer_clear(writer);
+ writer->error = error;
+
+ mpack_log("===========================\n");
+ mpack_log("initializing writer in error state %i\n", (int)error);
+}
+
+void mpack_writer_set_flush(mpack_writer_t* writer, mpack_writer_flush_t flush) {
+ MPACK_STATIC_ASSERT(MPACK_WRITER_MINIMUM_BUFFER_SIZE >= MPACK_MAXIMUM_TAG_SIZE,
+ "minimum buffer size must fit any tag!");
+ MPACK_STATIC_ASSERT(31 + MPACK_TAG_SIZE_FIXSTR >= MPACK_WRITER_MINIMUM_BUFFER_SIZE,
+ "minimum buffer size must fit the largest possible fixstr!");
+
+ if (mpack_writer_buffer_size(writer) < MPACK_WRITER_MINIMUM_BUFFER_SIZE) {
+ mpack_break("buffer size is %i, but minimum buffer size for flush is %i",
+ (int)mpack_writer_buffer_size(writer), MPACK_WRITER_MINIMUM_BUFFER_SIZE);
+ mpack_writer_flag_error(writer, mpack_error_bug);
+ return;
+ }
+
+ writer->flush = flush;
+}
+
+#ifdef MPACK_MALLOC
+typedef struct mpack_growable_writer_t {
+ char** target_data;
+ size_t* target_size;
+} mpack_growable_writer_t;
+
+static char* mpack_writer_get_reserved(mpack_writer_t* writer) {
+ // This is in a separate function in order to avoid false strict aliasing
+ // warnings. We aren't actually violating strict aliasing (the reserved
+ // space is only ever dereferenced as an mpack_growable_writer_t.)
+ return (char*)writer->reserved;
+}
+
+static void mpack_growable_writer_flush(mpack_writer_t* writer, const char* data, size_t count) {
+
+ // This is an intrusive flush function which modifies the writer's buffer
+ // in response to a flush instead of emptying it in order to add more
+ // capacity for data. This removes the need to copy data from a fixed buffer
+ // into a growable one, improving performance.
+ //
+ // There are three ways flush can be called:
+ // - flushing the buffer during writing (used is zero, count is all data, data is buffer)
+ // - flushing extra data during writing (used is all flushed data, count is extra data, data is not buffer)
+ // - flushing during teardown (used and count are both all flushed data, data is buffer)
+ //
+ // In the first two cases, we grow the buffer by at least double, enough
+ // to ensure that new data will fit. We ignore the teardown flush.
+
+ if (data == writer->buffer) {
+
+ // teardown, do nothing
+ if (mpack_writer_buffer_used(writer) == count)
+ return;
+
+ // otherwise leave the data in the buffer and just grow
+ writer->position = writer->buffer + count;
+ count = 0;
+ }
+
+ size_t used = mpack_writer_buffer_used(writer);
+ size_t size = mpack_writer_buffer_size(writer);
+
+ mpack_log("flush size %i used %i data %p buffer %p\n",
+ (int)count, (int)used, data, writer->buffer);
+
+ mpack_assert(data == writer->buffer || used + count > size,
+ "extra flush for %i but there is %i space left in the buffer! (%i/%i)",
+ (int)count, (int)mpack_writer_buffer_left(writer), (int)used, (int)size);
+
+ // grow to fit the data
+ // TODO: this really needs to correctly test for overflow
+ size_t new_size = size * 2;
+ while (new_size < used + count)
+ new_size *= 2;
+
+ mpack_log("flush growing buffer size from %i to %i\n", (int)size, (int)new_size);
+
+ // grow the buffer
+ char* new_buffer = (char*)mpack_realloc(writer->buffer, used, new_size);
+ if (new_buffer == NULL) {
+ mpack_writer_flag_error(writer, mpack_error_memory);
+ return;
+ }
+ writer->position = new_buffer + used;
+ writer->buffer = new_buffer;
+ writer->end = writer->buffer + new_size;
+
+ // append the extra data
+ if (count > 0) {
+ mpack_memcpy(writer->position, data, count);
+ writer->position += count;
+ }
+
+ mpack_log("new buffer %p, used %i\n", new_buffer, (int)mpack_writer_buffer_used(writer));
+}
+
+static void mpack_growable_writer_teardown(mpack_writer_t* writer) {
+ mpack_growable_writer_t* growable_writer = (mpack_growable_writer_t*)mpack_writer_get_reserved(writer);
+
+ if (mpack_writer_error(writer) == mpack_ok) {
+
+ // shrink the buffer to an appropriate size if the data is
+ // much smaller than the buffer
+ if (mpack_writer_buffer_used(writer) < mpack_writer_buffer_size(writer) / 2) {
+ size_t used = mpack_writer_buffer_used(writer);
+
+ // We always return a non-null pointer that must be freed, even if
+ // nothing was written. malloc() and realloc() do not necessarily
+ // do this so we enforce it ourselves.
+ size_t size = (used != 0) ? used : 1;
+
+ char* buffer = (char*)mpack_realloc(writer->buffer, used, size);
+ if (!buffer) {
+ MPACK_FREE(writer->buffer);
+ mpack_writer_flag_error(writer, mpack_error_memory);
+ return;
+ }
+ writer->buffer = buffer;
+ writer->end = (writer->position = writer->buffer + used);
+ }
+
+ *growable_writer->target_data = writer->buffer;
+ *growable_writer->target_size = mpack_writer_buffer_used(writer);
+ writer->buffer = NULL;
+
+ } else if (writer->buffer) {
+ MPACK_FREE(writer->buffer);
+ writer->buffer = NULL;
+ }
+
+ writer->context = NULL;
+}
+
+void mpack_writer_init_growable(mpack_writer_t* writer, char** target_data, size_t* target_size) {
+ mpack_assert(target_data != NULL, "cannot initialize writer without a destination for the data");
+ mpack_assert(target_size != NULL, "cannot initialize writer without a destination for the size");
+
+ *target_data = NULL;
+ *target_size = 0;
+
+ MPACK_STATIC_ASSERT(sizeof(mpack_growable_writer_t) <= sizeof(writer->reserved),
+ "not enough reserved space for growable writer!");
+ mpack_growable_writer_t* growable_writer = (mpack_growable_writer_t*)mpack_writer_get_reserved(writer);
+
+ growable_writer->target_data = target_data;
+ growable_writer->target_size = target_size;
+
+ size_t capacity = MPACK_BUFFER_SIZE;
+ char* buffer = (char*)MPACK_MALLOC(capacity);
+ if (buffer == NULL) {
+ mpack_writer_init_error(writer, mpack_error_memory);
+ return;
+ }
+
+ mpack_writer_init(writer, buffer, capacity);
+ mpack_writer_set_flush(writer, mpack_growable_writer_flush);
+ mpack_writer_set_teardown(writer, mpack_growable_writer_teardown);
+}
+#endif
+
+#if MPACK_STDIO
+static void mpack_file_writer_flush(mpack_writer_t* writer, const char* buffer, size_t count) {
+ FILE* file = (FILE*)writer->context;
+ size_t written = fwrite((const void*)buffer, 1, count, file);
+ if (written != count)
+ mpack_writer_flag_error(writer, mpack_error_io);
+}
+
+static void mpack_file_writer_teardown(mpack_writer_t* writer) {
+ MPACK_FREE(writer->buffer);
+ writer->buffer = NULL;
+ writer->context = NULL;
+}
+
+static void mpack_file_writer_teardown_close(mpack_writer_t* writer) {
+ FILE* file = (FILE*)writer->context;
+
+ if (file) {
+ int ret = fclose(file);
+ if (ret != 0)
+ mpack_writer_flag_error(writer, mpack_error_io);
+ }
+
+ mpack_file_writer_teardown(writer);
+}
+
+void mpack_writer_init_stdfile(mpack_writer_t* writer, FILE* file, bool close_when_done) {
+ mpack_assert(file != NULL, "file is NULL");
+
+ size_t capacity = MPACK_BUFFER_SIZE;
+ char* buffer = (char*)MPACK_MALLOC(capacity);
+ if (buffer == NULL) {
+ mpack_writer_init_error(writer, mpack_error_memory);
+ if (close_when_done) {
+ fclose(file);
+ }
+ return;
+ }
+
+ mpack_writer_init(writer, buffer, capacity);
+ mpack_writer_set_context(writer, file);
+ mpack_writer_set_flush(writer, mpack_file_writer_flush);
+ mpack_writer_set_teardown(writer, close_when_done ?
+ mpack_file_writer_teardown_close :
+ mpack_file_writer_teardown);
+}
+
+void mpack_writer_init_filename(mpack_writer_t* writer, const char* filename) {
+ mpack_assert(filename != NULL, "filename is NULL");
+
+ FILE* file = fopen(filename, "wb");
+ if (file == NULL) {
+ mpack_writer_init_error(writer, mpack_error_io);
+ return;
+ }
+
+ mpack_writer_init_stdfile(writer, file, true);
+}
+#endif
+
+void mpack_writer_flag_error(mpack_writer_t* writer, mpack_error_t error) {
+ mpack_log("writer %p setting error %i: %s\n", (void*)writer, (int)error, mpack_error_to_string(error));
+
+ if (writer->error == mpack_ok) {
+ writer->error = error;
+ if (writer->error_fn)
+ writer->error_fn(writer, writer->error);
+ }
+}
+
+MPACK_STATIC_INLINE void mpack_writer_flush_unchecked(mpack_writer_t* writer) {
+ // This is a bit ugly; we reset used before calling flush so that
+ // a flush function can distinguish between flushing the buffer
+ // versus flushing external data. see mpack_growable_writer_flush()
+ size_t used = mpack_writer_buffer_used(writer);
+ writer->position = writer->buffer;
+ writer->flush(writer, writer->buffer, used);
+}
+
+void mpack_writer_flush_message(mpack_writer_t* writer) {
+ if (writer->error != mpack_ok)
+ return;
+
+ #if MPACK_WRITE_TRACKING
+ // You cannot flush while there are elements open.
+ mpack_writer_flag_if_error(writer, mpack_track_check_empty(&writer->track));
+ if (writer->error != mpack_ok)
+ return;
+ #endif
+
+ #if MPACK_BUILDER
+ if (writer->builder.current_build != NULL) {
+ mpack_break("cannot call mpack_writer_flush_message() while there are elements open!");
+ mpack_writer_flag_error(writer, mpack_error_bug);
+ return;
+ }
+ #endif
+
+ if (writer->flush == NULL) {
+ mpack_break("cannot call mpack_writer_flush_message() without a flush function!");
+ mpack_writer_flag_error(writer, mpack_error_bug);
+ return;
+ }
+
+ if (mpack_writer_buffer_used(writer) > 0)
+ mpack_writer_flush_unchecked(writer);
+}
+
+// Ensures there are at least count bytes free in the buffer. This
+// will flag an error if the flush function fails to make enough
+// room in the buffer.
+MPACK_NOINLINE static bool mpack_writer_ensure(mpack_writer_t* writer, size_t count) {
+ mpack_assert(count != 0, "cannot ensure zero bytes!");
+ mpack_assert(count <= MPACK_WRITER_MINIMUM_BUFFER_SIZE,
+ "cannot ensure %i bytes, this is more than the minimum buffer size %i!",
+ (int)count, (int)MPACK_WRITER_MINIMUM_BUFFER_SIZE);
+ mpack_assert(count > mpack_writer_buffer_left(writer),
+ "request to ensure %i bytes but there are already %i left in the buffer!",
+ (int)count, (int)mpack_writer_buffer_left(writer));
+
+ mpack_log("ensuring %i bytes, %i left\n", (int)count, (int)mpack_writer_buffer_left(writer));
+
+ if (mpack_writer_error(writer) != mpack_ok)
+ return false;
+
+ #if MPACK_BUILDER
+ // if we have a build in progress, we just ask the builder for a page.
+ // either it will have space for a tag, or it will flag a memory error.
+ if (writer->builder.current_build != NULL) {
+ mpack_builder_flush(writer);
+ return mpack_writer_error(writer) == mpack_ok;
+ }
+ #endif
+
+ if (writer->flush == NULL) {
+ mpack_writer_flag_error(writer, mpack_error_too_big);
+ return false;
+ }
+
+ mpack_writer_flush_unchecked(writer);
+ if (mpack_writer_error(writer) != mpack_ok)
+ return false;
+
+ if (mpack_writer_buffer_left(writer) >= count)
+ return true;
+
+ mpack_writer_flag_error(writer, mpack_error_io);
+ return false;
+}
+
+// Writes encoded bytes to the buffer when we already know the data
+// does not fit in the buffer (i.e. it straddles the edge of the
+// buffer.) If there is a flush function, it is guaranteed to be
+// called; otherwise mpack_error_too_big is raised.
+MPACK_NOINLINE static void mpack_write_native_straddle(mpack_writer_t* writer, const char* p, size_t count) {
+ mpack_assert(count == 0 || p != NULL, "data pointer for %i bytes is NULL", (int)count);
+
+ if (mpack_writer_error(writer) != mpack_ok)
+ return;
+ mpack_log("big write for %i bytes from %p, %i space left in buffer\n",
+ (int)count, p, (int)mpack_writer_buffer_left(writer));
+ mpack_assert(count > mpack_writer_buffer_left(writer),
+ "big write requested for %i bytes, but there is %i available "
+ "space in buffer. should have called mpack_write_native() instead",
+ (int)count, (int)(mpack_writer_buffer_left(writer)));
+
+ #if MPACK_BUILDER
+ // if we have a build in progress, we can't flush. we need to copy all
+ // bytes into as many build buffer pages as it takes.
+ if (writer->builder.current_build != NULL) {
+ while (true) {
+ size_t step = (size_t)(writer->end - writer->position);
+ if (step > count)
+ step = count;
+ mpack_memcpy(writer->position, p, step);
+ writer->position += step;
+ p += step;
+ count -= step;
+
+ if (count == 0)
+ return;
+
+ mpack_builder_flush(writer);
+ if (mpack_writer_error(writer) != mpack_ok)
+ return;
+ mpack_assert(writer->position != writer->end);
+ }
+ }
+ #endif
+
+ // we'll need a flush function
+ if (!writer->flush) {
+ mpack_writer_flag_error(writer, mpack_error_too_big);
+ return;
+ }
+
+ // flush the buffer
+ mpack_writer_flush_unchecked(writer);
+ if (mpack_writer_error(writer) != mpack_ok)
+ return;
+
+ // note that an intrusive flush function (such as mpack_growable_writer_flush())
+ // may have changed size and/or reset used to a non-zero value. we treat both as
+ // though they may have changed, and there may still be data in the buffer.
+
+ // flush the extra data directly if it doesn't fit in the buffer
+ if (count > mpack_writer_buffer_left(writer)) {
+ writer->flush(writer, p, count);
+ if (mpack_writer_error(writer) != mpack_ok)
+ return;
+ } else {
+ mpack_memcpy(writer->position, p, count);
+ writer->position += count;
+ }
+}
+
+// Writes encoded bytes to the buffer, flushing if necessary.
+MPACK_STATIC_INLINE void mpack_write_native(mpack_writer_t* writer, const char* p, size_t count) {
+ mpack_assert(count == 0 || p != NULL, "data pointer for %i bytes is NULL", (int)count);
+
+ if (mpack_writer_buffer_left(writer) < count) {
+ mpack_write_native_straddle(writer, p, count);
+ } else {
+ mpack_memcpy(writer->position, p, count);
+ writer->position += count;
+ }
+}
+
+mpack_error_t mpack_writer_destroy(mpack_writer_t* writer) {
+
+ // clean up tracking, asserting if we're not already in an error state
+ #if MPACK_WRITE_TRACKING
+ mpack_track_destroy(&writer->track, writer->error != mpack_ok);
+ #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));
+ writer->flush = NULL;
+ }
+
+ if (writer->teardown) {
+ writer->teardown(writer);
+ writer->teardown = NULL;
+ }
+
+ return writer->error;
+}
+
+void mpack_write_tag(mpack_writer_t* writer, mpack_tag_t value) {
+ switch (value.type) {
+ case mpack_type_missing:
+ mpack_break("cannot write a missing value!");
+ mpack_writer_flag_error(writer, mpack_error_bug);
+ return;
+
+ case mpack_type_nil: mpack_write_nil (writer); return;
+ case mpack_type_bool: mpack_write_bool (writer, value.v.b); return;
+ case mpack_type_int: mpack_write_int (writer, value.v.i); return;
+ case mpack_type_uint: mpack_write_uint (writer, value.v.u); return;
+
+ case mpack_type_float:
+ #if MPACK_FLOAT
+ mpack_write_float
+ #else
+ mpack_write_raw_float
+ #endif
+ (writer, value.v.f);
+ return;
+ case mpack_type_double:
+ #if MPACK_DOUBLE
+ mpack_write_double
+ #else
+ mpack_write_raw_double
+ #endif
+ (writer, value.v.d);
+ return;
+
+ case mpack_type_str: mpack_start_str(writer, value.v.l); return;
+ case mpack_type_bin: mpack_start_bin(writer, value.v.l); return;
+
+ #if MPACK_EXTENSIONS
+ case mpack_type_ext:
+ mpack_start_ext(writer, mpack_tag_ext_exttype(&value), mpack_tag_ext_length(&value));
+ return;
+ #endif
+
+ case mpack_type_array: mpack_start_array(writer, value.v.n); return;
+ case mpack_type_map: mpack_start_map(writer, value.v.n); return;
+ }
+
+ mpack_break("unrecognized type %i", (int)value.type);
+ mpack_writer_flag_error(writer, mpack_error_bug);
+}
+
+MPACK_STATIC_INLINE void mpack_write_byte_element(mpack_writer_t* writer, char value) {
+ mpack_writer_track_element(writer);
+ if (MPACK_LIKELY(mpack_writer_buffer_left(writer) >= 1) || mpack_writer_ensure(writer, 1))
+ *(writer->position++) = value;
+}
+
+void mpack_write_nil(mpack_writer_t* writer) {
+ mpack_write_byte_element(writer, (char)0xc0);
+}
+
+void mpack_write_bool(mpack_writer_t* writer, bool value) {
+ mpack_write_byte_element(writer, (char)(0xc2 | (value ? 1 : 0)));
+}
+
+void mpack_write_true(mpack_writer_t* writer) {
+ mpack_write_byte_element(writer, (char)0xc3);
+}
+
+void mpack_write_false(mpack_writer_t* writer) {
+ mpack_write_byte_element(writer, (char)0xc2);
+}
+
+void mpack_write_object_bytes(mpack_writer_t* writer, const char* data, size_t bytes) {
+ mpack_writer_track_element(writer);
+ mpack_write_native(writer, data, bytes);
+}
+
+/*
+ * Encode functions
+ */
+
+MPACK_STATIC_INLINE void mpack_encode_fixuint(char* p, uint8_t value) {
+ mpack_assert(value <= 127);
+ mpack_store_u8(p, value);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_u8(char* p, uint8_t value) {
+ mpack_assert(value > 127);
+ mpack_store_u8(p, 0xcc);
+ mpack_store_u8(p + 1, value);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_u16(char* p, uint16_t value) {
+ mpack_assert(value > MPACK_UINT8_MAX);
+ mpack_store_u8(p, 0xcd);
+ mpack_store_u16(p + 1, value);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_u32(char* p, uint32_t value) {
+ mpack_assert(value > MPACK_UINT16_MAX);
+ mpack_store_u8(p, 0xce);
+ mpack_store_u32(p + 1, value);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_u64(char* p, uint64_t value) {
+ mpack_assert(value > MPACK_UINT32_MAX);
+ mpack_store_u8(p, 0xcf);
+ mpack_store_u64(p + 1, value);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_fixint(char* p, int8_t value) {
+ // this can encode positive or negative fixints
+ mpack_assert(value >= -32);
+ mpack_store_i8(p, value);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_i8(char* p, int8_t value) {
+ mpack_assert(value < -32);
+ mpack_store_u8(p, 0xd0);
+ mpack_store_i8(p + 1, value);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_i16(char* p, int16_t value) {
+ mpack_assert(value < MPACK_INT8_MIN);
+ mpack_store_u8(p, 0xd1);
+ mpack_store_i16(p + 1, value);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_i32(char* p, int32_t value) {
+ mpack_assert(value < MPACK_INT16_MIN);
+ mpack_store_u8(p, 0xd2);
+ mpack_store_i32(p + 1, value);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_i64(char* p, int64_t value) {
+ mpack_assert(value < MPACK_INT32_MIN);
+ mpack_store_u8(p, 0xd3);
+ mpack_store_i64(p + 1, value);
+}
+
+#if MPACK_FLOAT
+MPACK_STATIC_INLINE void mpack_encode_float(char* p, float value) {
+ mpack_store_u8(p, 0xca);
+ mpack_store_float(p + 1, value);
+}
+#else
+MPACK_STATIC_INLINE void mpack_encode_raw_float(char* p, uint32_t value) {
+ mpack_store_u8(p, 0xca);
+ mpack_store_u32(p + 1, value);
+}
+#endif
+
+#if MPACK_DOUBLE
+MPACK_STATIC_INLINE void mpack_encode_double(char* p, double value) {
+ mpack_store_u8(p, 0xcb);
+ mpack_store_double(p + 1, value);
+}
+#else
+MPACK_STATIC_INLINE void mpack_encode_raw_double(char* p, uint64_t value) {
+ mpack_store_u8(p, 0xcb);
+ mpack_store_u64(p + 1, value);
+}
+#endif
+
+MPACK_STATIC_INLINE void mpack_encode_fixarray(char* p, uint8_t count) {
+ mpack_assert(count <= 15);
+ mpack_store_u8(p, (uint8_t)(0x90 | count));
+}
+
+MPACK_STATIC_INLINE void mpack_encode_array16(char* p, uint16_t count) {
+ mpack_assert(count > 15);
+ mpack_store_u8(p, 0xdc);
+ mpack_store_u16(p + 1, count);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_array32(char* p, uint32_t count) {
+ mpack_assert(count > MPACK_UINT16_MAX);
+ mpack_store_u8(p, 0xdd);
+ mpack_store_u32(p + 1, count);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_fixmap(char* p, uint8_t count) {
+ mpack_assert(count <= 15);
+ mpack_store_u8(p, (uint8_t)(0x80 | count));
+}
+
+MPACK_STATIC_INLINE void mpack_encode_map16(char* p, uint16_t count) {
+ mpack_assert(count > 15);
+ mpack_store_u8(p, 0xde);
+ mpack_store_u16(p + 1, count);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_map32(char* p, uint32_t count) {
+ mpack_assert(count > MPACK_UINT16_MAX);
+ mpack_store_u8(p, 0xdf);
+ mpack_store_u32(p + 1, count);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_fixstr(char* p, uint8_t count) {
+ mpack_assert(count <= 31);
+ mpack_store_u8(p, (uint8_t)(0xa0 | count));
+}
+
+MPACK_STATIC_INLINE void mpack_encode_str8(char* p, uint8_t count) {
+ mpack_assert(count > 31);
+ mpack_store_u8(p, 0xd9);
+ mpack_store_u8(p + 1, count);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_str16(char* p, uint16_t count) {
+ // we might be encoding a raw in compatibility mode, so we
+ // allow count to be in the range [32, MPACK_UINT8_MAX].
+ mpack_assert(count > 31);
+ mpack_store_u8(p, 0xda);
+ mpack_store_u16(p + 1, count);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_str32(char* p, uint32_t count) {
+ mpack_assert(count > MPACK_UINT16_MAX);
+ mpack_store_u8(p, 0xdb);
+ mpack_store_u32(p + 1, count);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_bin8(char* p, uint8_t count) {
+ mpack_store_u8(p, 0xc4);
+ mpack_store_u8(p + 1, count);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_bin16(char* p, uint16_t count) {
+ mpack_assert(count > MPACK_UINT8_MAX);
+ mpack_store_u8(p, 0xc5);
+ mpack_store_u16(p + 1, count);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_bin32(char* p, uint32_t count) {
+ mpack_assert(count > MPACK_UINT16_MAX);
+ mpack_store_u8(p, 0xc6);
+ mpack_store_u32(p + 1, count);
+}
+
+#if MPACK_EXTENSIONS
+MPACK_STATIC_INLINE void mpack_encode_fixext1(char* p, int8_t exttype) {
+ mpack_store_u8(p, 0xd4);
+ mpack_store_i8(p + 1, exttype);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_fixext2(char* p, int8_t exttype) {
+ mpack_store_u8(p, 0xd5);
+ mpack_store_i8(p + 1, exttype);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_fixext4(char* p, int8_t exttype) {
+ mpack_store_u8(p, 0xd6);
+ mpack_store_i8(p + 1, exttype);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_fixext8(char* p, int8_t exttype) {
+ mpack_store_u8(p, 0xd7);
+ mpack_store_i8(p + 1, exttype);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_fixext16(char* p, int8_t exttype) {
+ mpack_store_u8(p, 0xd8);
+ mpack_store_i8(p + 1, exttype);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_ext8(char* p, int8_t exttype, uint8_t count) {
+ mpack_assert(count != 1 && count != 2 && count != 4 && count != 8 && count != 16);
+ mpack_store_u8(p, 0xc7);
+ mpack_store_u8(p + 1, count);
+ mpack_store_i8(p + 2, exttype);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_ext16(char* p, int8_t exttype, uint16_t count) {
+ mpack_assert(count > MPACK_UINT8_MAX);
+ mpack_store_u8(p, 0xc8);
+ mpack_store_u16(p + 1, count);
+ mpack_store_i8(p + 3, exttype);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_ext32(char* p, int8_t exttype, uint32_t count) {
+ mpack_assert(count > MPACK_UINT16_MAX);
+ mpack_store_u8(p, 0xc9);
+ mpack_store_u32(p + 1, count);
+ mpack_store_i8(p + 5, exttype);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_timestamp_4(char* p, uint32_t seconds) {
+ mpack_encode_fixext4(p, MPACK_EXTTYPE_TIMESTAMP);
+ mpack_store_u32(p + MPACK_TAG_SIZE_FIXEXT4, seconds);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_timestamp_8(char* p, int64_t seconds, uint32_t nanoseconds) {
+ mpack_assert(nanoseconds <= MPACK_TIMESTAMP_NANOSECONDS_MAX);
+ mpack_encode_fixext8(p, MPACK_EXTTYPE_TIMESTAMP);
+ uint64_t encoded = ((uint64_t)nanoseconds << 34) | (uint64_t)seconds;
+ mpack_store_u64(p + MPACK_TAG_SIZE_FIXEXT8, encoded);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_timestamp_12(char* p, int64_t seconds, uint32_t nanoseconds) {
+ mpack_assert(nanoseconds <= MPACK_TIMESTAMP_NANOSECONDS_MAX);
+ mpack_encode_ext8(p, MPACK_EXTTYPE_TIMESTAMP, 12);
+ mpack_store_u32(p + MPACK_TAG_SIZE_EXT8, nanoseconds);
+ mpack_store_i64(p + MPACK_TAG_SIZE_EXT8 + 4, seconds);
+}
+#endif
+
+
+
+/*
+ * Write functions
+ */
+
+// This is a macro wrapper to the encode functions to encode
+// directly into the buffer. If mpack_writer_ensure() fails
+// it will flag an error so we don't have to do anything.
+#define MPACK_WRITE_ENCODED(encode_fn, size, ...) do { \
+ if (MPACK_LIKELY(mpack_writer_buffer_left(writer) >= size) || mpack_writer_ensure(writer, size)) { \
+ MPACK_EXPAND(encode_fn(writer->position, __VA_ARGS__)); \
+ writer->position += size; \
+ } \
+} while (0)
+
+void mpack_write_u8(mpack_writer_t* writer, uint8_t value) {
+ #if MPACK_OPTIMIZE_FOR_SIZE
+ mpack_write_u64(writer, value);
+ #else
+ mpack_writer_track_element(writer);
+ if (value <= 127) {
+ MPACK_WRITE_ENCODED(mpack_encode_fixuint, MPACK_TAG_SIZE_FIXUINT, value);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_u8, MPACK_TAG_SIZE_U8, value);
+ }
+ #endif
+}
+
+void mpack_write_u16(mpack_writer_t* writer, uint16_t value) {
+ #if MPACK_OPTIMIZE_FOR_SIZE
+ mpack_write_u64(writer, value);
+ #else
+ mpack_writer_track_element(writer);
+ if (value <= 127) {
+ MPACK_WRITE_ENCODED(mpack_encode_fixuint, MPACK_TAG_SIZE_FIXUINT, (uint8_t)value);
+ } else if (value <= MPACK_UINT8_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_u8, MPACK_TAG_SIZE_U8, (uint8_t)value);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_u16, MPACK_TAG_SIZE_U16, value);
+ }
+ #endif
+}
+
+void mpack_write_u32(mpack_writer_t* writer, uint32_t value) {
+ #if MPACK_OPTIMIZE_FOR_SIZE
+ mpack_write_u64(writer, value);
+ #else
+ mpack_writer_track_element(writer);
+ if (value <= 127) {
+ MPACK_WRITE_ENCODED(mpack_encode_fixuint, MPACK_TAG_SIZE_FIXUINT, (uint8_t)value);
+ } else if (value <= MPACK_UINT8_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_u8, MPACK_TAG_SIZE_U8, (uint8_t)value);
+ } else if (value <= MPACK_UINT16_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_u16, MPACK_TAG_SIZE_U16, (uint16_t)value);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_u32, MPACK_TAG_SIZE_U32, value);
+ }
+ #endif
+}
+
+void mpack_write_u64(mpack_writer_t* writer, uint64_t value) {
+ mpack_writer_track_element(writer);
+
+ if (value <= 127) {
+ MPACK_WRITE_ENCODED(mpack_encode_fixuint, MPACK_TAG_SIZE_FIXUINT, (uint8_t)value);
+ } else if (value <= MPACK_UINT8_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_u8, MPACK_TAG_SIZE_U8, (uint8_t)value);
+ } else if (value <= MPACK_UINT16_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_u16, MPACK_TAG_SIZE_U16, (uint16_t)value);
+ } else if (value <= MPACK_UINT32_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_u32, MPACK_TAG_SIZE_U32, (uint32_t)value);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_u64, MPACK_TAG_SIZE_U64, value);
+ }
+}
+
+void mpack_write_i8(mpack_writer_t* writer, int8_t value) {
+ #if MPACK_OPTIMIZE_FOR_SIZE
+ mpack_write_i64(writer, value);
+ #else
+ mpack_writer_track_element(writer);
+ if (value >= -32) {
+ // we encode positive and negative fixints together
+ MPACK_WRITE_ENCODED(mpack_encode_fixint, MPACK_TAG_SIZE_FIXINT, (int8_t)value);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_i8, MPACK_TAG_SIZE_I8, (int8_t)value);
+ }
+ #endif
+}
+
+void mpack_write_i16(mpack_writer_t* writer, int16_t value) {
+ #if MPACK_OPTIMIZE_FOR_SIZE
+ mpack_write_i64(writer, value);
+ #else
+ mpack_writer_track_element(writer);
+ if (value >= -32) {
+ if (value <= 127) {
+ // we encode positive and negative fixints together
+ MPACK_WRITE_ENCODED(mpack_encode_fixint, MPACK_TAG_SIZE_FIXINT, (int8_t)value);
+ } else if (value <= MPACK_UINT8_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_u8, MPACK_TAG_SIZE_U8, (uint8_t)value);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_u16, MPACK_TAG_SIZE_U16, (uint16_t)value);
+ }
+ } else if (value >= MPACK_INT8_MIN) {
+ MPACK_WRITE_ENCODED(mpack_encode_i8, MPACK_TAG_SIZE_I8, (int8_t)value);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_i16, MPACK_TAG_SIZE_I16, (int16_t)value);
+ }
+ #endif
+}
+
+void mpack_write_i32(mpack_writer_t* writer, int32_t value) {
+ #if MPACK_OPTIMIZE_FOR_SIZE
+ mpack_write_i64(writer, value);
+ #else
+ mpack_writer_track_element(writer);
+ if (value >= -32) {
+ if (value <= 127) {
+ // we encode positive and negative fixints together
+ MPACK_WRITE_ENCODED(mpack_encode_fixint, MPACK_TAG_SIZE_FIXINT, (int8_t)value);
+ } else if (value <= MPACK_UINT8_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_u8, MPACK_TAG_SIZE_U8, (uint8_t)value);
+ } else if (value <= MPACK_UINT16_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_u16, MPACK_TAG_SIZE_U16, (uint16_t)value);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_u32, MPACK_TAG_SIZE_U32, (uint32_t)value);
+ }
+ } else if (value >= MPACK_INT8_MIN) {
+ MPACK_WRITE_ENCODED(mpack_encode_i8, MPACK_TAG_SIZE_I8, (int8_t)value);
+ } else if (value >= MPACK_INT16_MIN) {
+ MPACK_WRITE_ENCODED(mpack_encode_i16, MPACK_TAG_SIZE_I16, (int16_t)value);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_i32, MPACK_TAG_SIZE_I32, value);
+ }
+ #endif
+}
+
+void mpack_write_i64(mpack_writer_t* writer, int64_t value) {
+ #if MPACK_OPTIMIZE_FOR_SIZE
+ if (value > 127) {
+ // for non-fix positive ints we call the u64 writer to save space
+ mpack_write_u64(writer, (uint64_t)value);
+ return;
+ }
+ #endif
+
+ mpack_writer_track_element(writer);
+ if (value >= -32) {
+ #if MPACK_OPTIMIZE_FOR_SIZE
+ MPACK_WRITE_ENCODED(mpack_encode_fixint, MPACK_TAG_SIZE_FIXINT, (int8_t)value);
+ #else
+ if (value <= 127) {
+ MPACK_WRITE_ENCODED(mpack_encode_fixint, MPACK_TAG_SIZE_FIXINT, (int8_t)value);
+ } else if (value <= MPACK_UINT8_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_u8, MPACK_TAG_SIZE_U8, (uint8_t)value);
+ } else if (value <= MPACK_UINT16_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_u16, MPACK_TAG_SIZE_U16, (uint16_t)value);
+ } else if (value <= MPACK_UINT32_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_u32, MPACK_TAG_SIZE_U32, (uint32_t)value);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_u64, MPACK_TAG_SIZE_U64, (uint64_t)value);
+ }
+ #endif
+ } else if (value >= MPACK_INT8_MIN) {
+ MPACK_WRITE_ENCODED(mpack_encode_i8, MPACK_TAG_SIZE_I8, (int8_t)value);
+ } else if (value >= MPACK_INT16_MIN) {
+ MPACK_WRITE_ENCODED(mpack_encode_i16, MPACK_TAG_SIZE_I16, (int16_t)value);
+ } else if (value >= MPACK_INT32_MIN) {
+ MPACK_WRITE_ENCODED(mpack_encode_i32, MPACK_TAG_SIZE_I32, (int32_t)value);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_i64, MPACK_TAG_SIZE_I64, value);
+ }
+}
+
+#if MPACK_FLOAT
+void mpack_write_float(mpack_writer_t* writer, float value) {
+ mpack_writer_track_element(writer);
+ MPACK_WRITE_ENCODED(mpack_encode_float, MPACK_TAG_SIZE_FLOAT, value);
+}
+#else
+void mpack_write_raw_float(mpack_writer_t* writer, uint32_t value) {
+ mpack_writer_track_element(writer);
+ MPACK_WRITE_ENCODED(mpack_encode_raw_float, MPACK_TAG_SIZE_FLOAT, value);
+}
+#endif
+
+#if MPACK_DOUBLE
+void mpack_write_double(mpack_writer_t* writer, double value) {
+ mpack_writer_track_element(writer);
+ MPACK_WRITE_ENCODED(mpack_encode_double, MPACK_TAG_SIZE_DOUBLE, value);
+}
+#else
+void mpack_write_raw_double(mpack_writer_t* writer, uint64_t value) {
+ mpack_writer_track_element(writer);
+ MPACK_WRITE_ENCODED(mpack_encode_raw_double, MPACK_TAG_SIZE_DOUBLE, value);
+}
+#endif
+
+#if MPACK_EXTENSIONS
+void mpack_write_timestamp(mpack_writer_t* writer, int64_t seconds, uint32_t nanoseconds) {
+ #if MPACK_COMPATIBILITY
+ if (writer->version <= mpack_version_v4) {
+ mpack_break("Timestamps require spec version v5 or later. This writer is in v%i mode.", (int)writer->version);
+ mpack_writer_flag_error(writer, mpack_error_bug);
+ return;
+ }
+ #endif
+
+ if (nanoseconds > MPACK_TIMESTAMP_NANOSECONDS_MAX) {
+ mpack_break("timestamp nanoseconds out of bounds: %u", nanoseconds);
+ mpack_writer_flag_error(writer, mpack_error_bug);
+ return;
+ }
+
+ mpack_writer_track_element(writer);
+
+ if (seconds < 0 || seconds >= (MPACK_INT64_C(1) << 34)) {
+ MPACK_WRITE_ENCODED(mpack_encode_timestamp_12, MPACK_EXT_SIZE_TIMESTAMP12, seconds, nanoseconds);
+ } else if (seconds > MPACK_UINT32_MAX || nanoseconds > 0) {
+ MPACK_WRITE_ENCODED(mpack_encode_timestamp_8, MPACK_EXT_SIZE_TIMESTAMP8, seconds, nanoseconds);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_timestamp_4, MPACK_EXT_SIZE_TIMESTAMP4, (uint32_t)seconds);
+ }
+}
+#endif
+
+static void mpack_write_array_notrack(mpack_writer_t* writer, uint32_t count) {
+ if (count <= 15) {
+ MPACK_WRITE_ENCODED(mpack_encode_fixarray, MPACK_TAG_SIZE_FIXARRAY, (uint8_t)count);
+ } else if (count <= MPACK_UINT16_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_array16, MPACK_TAG_SIZE_ARRAY16, (uint16_t)count);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_array32, MPACK_TAG_SIZE_ARRAY32, (uint32_t)count);
+ }
+}
+
+static void mpack_write_map_notrack(mpack_writer_t* writer, uint32_t count) {
+ if (count <= 15) {
+ MPACK_WRITE_ENCODED(mpack_encode_fixmap, MPACK_TAG_SIZE_FIXMAP, (uint8_t)count);
+ } else if (count <= MPACK_UINT16_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_map16, MPACK_TAG_SIZE_MAP16, (uint16_t)count);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_map32, MPACK_TAG_SIZE_MAP32, (uint32_t)count);
+ }
+}
+
+void mpack_start_array(mpack_writer_t* writer, uint32_t count) {
+ mpack_writer_track_element(writer);
+ mpack_write_array_notrack(writer, count);
+ mpack_writer_track_push(writer, mpack_type_array, count);
+ mpack_builder_compound_push(writer);
+}
+
+void mpack_start_map(mpack_writer_t* writer, uint32_t count) {
+ mpack_writer_track_element(writer);
+ mpack_write_map_notrack(writer, count);
+ mpack_writer_track_push(writer, mpack_type_map, count);
+ mpack_builder_compound_push(writer);
+}
+
+static void mpack_start_str_notrack(mpack_writer_t* writer, uint32_t count) {
+ if (count <= 31) {
+ MPACK_WRITE_ENCODED(mpack_encode_fixstr, MPACK_TAG_SIZE_FIXSTR, (uint8_t)count);
+
+ // str8 is only supported in v5 or later.
+ } else if (count <= MPACK_UINT8_MAX
+ #if MPACK_COMPATIBILITY
+ && writer->version >= mpack_version_v5
+ #endif
+ ) {
+ MPACK_WRITE_ENCODED(mpack_encode_str8, MPACK_TAG_SIZE_STR8, (uint8_t)count);
+
+ } else if (count <= MPACK_UINT16_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_str16, MPACK_TAG_SIZE_STR16, (uint16_t)count);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_str32, MPACK_TAG_SIZE_STR32, (uint32_t)count);
+ }
+}
+
+static void mpack_start_bin_notrack(mpack_writer_t* writer, uint32_t count) {
+ #if MPACK_COMPATIBILITY
+ // In the v4 spec, there was only the raw type for any kind of
+ // variable-length data. In v4 mode, we support the bin functions,
+ // but we produce an old-style raw.
+ if (writer->version <= mpack_version_v4) {
+ mpack_start_str_notrack(writer, count);
+ return;
+ }
+ #endif
+
+ if (count <= MPACK_UINT8_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_bin8, MPACK_TAG_SIZE_BIN8, (uint8_t)count);
+ } else if (count <= MPACK_UINT16_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_bin16, MPACK_TAG_SIZE_BIN16, (uint16_t)count);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_bin32, MPACK_TAG_SIZE_BIN32, (uint32_t)count);
+ }
+}
+
+void mpack_start_str(mpack_writer_t* writer, uint32_t count) {
+ mpack_writer_track_element(writer);
+ mpack_start_str_notrack(writer, count);
+ mpack_writer_track_push(writer, mpack_type_str, count);
+}
+
+void mpack_start_bin(mpack_writer_t* writer, uint32_t count) {
+ mpack_writer_track_element(writer);
+ mpack_start_bin_notrack(writer, count);
+ mpack_writer_track_push(writer, mpack_type_bin, count);
+}
+
+#if MPACK_EXTENSIONS
+void mpack_start_ext(mpack_writer_t* writer, int8_t exttype, uint32_t count) {
+ #if MPACK_COMPATIBILITY
+ if (writer->version <= mpack_version_v4) {
+ mpack_break("Ext types require spec version v5 or later. This writer is in v%i mode.", (int)writer->version);
+ mpack_writer_flag_error(writer, mpack_error_bug);
+ return;
+ }
+ #endif
+
+ mpack_writer_track_element(writer);
+
+ if (count == 1) {
+ MPACK_WRITE_ENCODED(mpack_encode_fixext1, MPACK_TAG_SIZE_FIXEXT1, exttype);
+ } else if (count == 2) {
+ MPACK_WRITE_ENCODED(mpack_encode_fixext2, MPACK_TAG_SIZE_FIXEXT2, exttype);
+ } else if (count == 4) {
+ MPACK_WRITE_ENCODED(mpack_encode_fixext4, MPACK_TAG_SIZE_FIXEXT4, exttype);
+ } else if (count == 8) {
+ MPACK_WRITE_ENCODED(mpack_encode_fixext8, MPACK_TAG_SIZE_FIXEXT8, exttype);
+ } else if (count == 16) {
+ MPACK_WRITE_ENCODED(mpack_encode_fixext16, MPACK_TAG_SIZE_FIXEXT16, exttype);
+ } else if (count <= MPACK_UINT8_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_ext8, MPACK_TAG_SIZE_EXT8, exttype, (uint8_t)count);
+ } else if (count <= MPACK_UINT16_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_ext16, MPACK_TAG_SIZE_EXT16, exttype, (uint16_t)count);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_ext32, MPACK_TAG_SIZE_EXT32, exttype, (uint32_t)count);
+ }
+
+ mpack_writer_track_push(writer, mpack_type_ext, count);
+}
+#endif
+
+
+
+/*
+ * Compound helpers and other functions
+ */
+
+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);
+
+ #if MPACK_OPTIMIZE_FOR_SIZE
+ mpack_writer_track_element(writer);
+ mpack_start_str_notrack(writer, count);
+ mpack_write_native(writer, data, count);
+ #else
+
+ mpack_writer_track_element(writer);
+
+ if (count <= 31) {
+ // The minimum buffer size when using a flush function is guaranteed to
+ // fit the largest possible fixstr.
+ size_t size = count + MPACK_TAG_SIZE_FIXSTR;
+ if (MPACK_LIKELY(mpack_writer_buffer_left(writer) >= size) || mpack_writer_ensure(writer, size)) {
+ char* MPACK_RESTRICT p = writer->position;
+ mpack_encode_fixstr(p, (uint8_t)count);
+ mpack_memcpy(p + MPACK_TAG_SIZE_FIXSTR, data, count);
+ writer->position += count + MPACK_TAG_SIZE_FIXSTR;
+ }
+ return;
+ }
+
+ if (count <= MPACK_UINT8_MAX
+ #if MPACK_COMPATIBILITY
+ && writer->version >= mpack_version_v5
+ #endif
+ ) {
+ if (count + MPACK_TAG_SIZE_STR8 <= mpack_writer_buffer_left(writer)) {
+ char* MPACK_RESTRICT p = writer->position;
+ mpack_encode_str8(p, (uint8_t)count);
+ mpack_memcpy(p + MPACK_TAG_SIZE_STR8, data, count);
+ writer->position += count + MPACK_TAG_SIZE_STR8;
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_str8, MPACK_TAG_SIZE_STR8, (uint8_t)count);
+ mpack_write_native(writer, data, count);
+ }
+ return;
+ }
+
+ // str16 and str32 are likely to be a significant fraction of the buffer
+ // size, so we don't bother with a combined space check in order to
+ // minimize code size.
+ if (count <= MPACK_UINT16_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_str16, MPACK_TAG_SIZE_STR16, (uint16_t)count);
+ mpack_write_native(writer, data, count);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_str32, MPACK_TAG_SIZE_STR32, (uint32_t)count);
+ mpack_write_native(writer, data, count);
+ }
+
+ #endif
+}
+
+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_start_bin(writer, count);
+ mpack_write_bytes(writer, data, count);
+ mpack_finish_bin(writer);
+}
+
+#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_start_ext(writer, exttype, count);
+ mpack_write_bytes(writer, data, count);
+ mpack_finish_ext(writer);
+}
+#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_writer_track_bytes(writer, count);
+ mpack_write_native(writer, data, count);
+}
+
+void mpack_write_cstr(mpack_writer_t* writer, const char* cstr) {
+ mpack_assert(cstr != NULL, "cstr pointer is NULL");
+ size_t length = mpack_strlen(cstr);
+ if (length > MPACK_UINT32_MAX)
+ mpack_writer_flag_error(writer, mpack_error_invalid);
+ mpack_write_str(writer, cstr, (uint32_t)length);
+}
+
+void mpack_write_cstr_or_nil(mpack_writer_t* writer, const char* cstr) {
+ if (cstr)
+ mpack_write_cstr(writer, cstr);
+ else
+ mpack_write_nil(writer);
+}
+
+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);
+ if (!mpack_utf8_check(str, length)) {
+ mpack_writer_flag_error(writer, mpack_error_invalid);
+ return;
+ }
+ mpack_write_str(writer, str, length);
+}
+
+void mpack_write_utf8_cstr(mpack_writer_t* writer, const char* cstr) {
+ mpack_assert(cstr != NULL, "cstr pointer is NULL");
+ size_t length = mpack_strlen(cstr);
+ if (length > MPACK_UINT32_MAX) {
+ mpack_writer_flag_error(writer, mpack_error_invalid);
+ return;
+ }
+ mpack_write_utf8(writer, cstr, (uint32_t)length);
+}
+
+void mpack_write_utf8_cstr_or_nil(mpack_writer_t* writer, const char* cstr) {
+ if (cstr)
+ mpack_write_utf8_cstr(writer, cstr);
+ else
+ mpack_write_nil(writer);
+}
+
+/*
+ * Builder implementation
+ *
+ * When a writer is in build mode, it diverts writes to an internal growable
+ * buffer. All elements other than builder start tags are encoded as normal
+ * into the builder buffer (even nested maps and arrays of known size, e.g.
+ * `mpack_start_array()`.) But for compound elements of unknown size, an
+ * mpack_build_t is written to the buffer instead.
+ *
+ * The mpack_build_t tracks everything needed to re-constitute the final
+ * message once all sizes are known. When the last build element is completed,
+ * the builder resolves the build by walking through the builds, outputting the
+ * final encoded tag, and copying everything in between to the writer's true
+ * buffer.
+ *
+ * To make things extra complicated, the builder buffer is not contiguous. It's
+ * allocated in pages, where the first page may be an internal page in the
+ * writer. But, each mpack_build_t must itself be contiguous and aligned
+ * properly within the buffer. This means bytes can be skipped (and wasted)
+ * before the builds or at the end of pages.
+ *
+ * To keep track of this, builds store both their element count and the number
+ * of encoded bytes that follow, and pages store the number of bytes used. As
+ * elements are written, each element adds to the count in the current open
+ * build, and the number of bytes written adds to the current page and the byte
+ * count in the last started build (whether or not it is completed.)
+ */
+
+#if MPACK_BUILDER
+
+#ifdef MPACK_ALIGNOF
+ #define MPACK_BUILD_ALIGNMENT MPACK_ALIGNOF(mpack_build_t)
+#else
+ // without alignof, we just align to the greater of size_t, void* and uint64_t.
+ // (we do this even though we don't have uint64_t in it in case we add it later.)
+ #define MPACK_BUILD_ALIGNMENT_MAX(x, y) ((x) > (y) ? (x) : (y))
+ #define MPACK_BUILD_ALIGNMENT (MPACK_BUILD_ALIGNMENT_MAX(sizeof(void*), \
+ MPACK_BUILD_ALIGNMENT_MAX(sizeof(size_t), sizeof(uint64_t))))
+#endif
+
+static inline void mpack_builder_check_sizes(mpack_writer_t* writer) {
+
+ // We check internal and page sizes here so that we don't have to check
+ // them again. A new page with a build in it will have a page header,
+ // build, and minimum space for a tag. This will perform horribly and waste
+ // tons of memory if the page size is small, so you're best off just
+ // sticking with the defaults.
+ //
+ // These are all known at compile time, so if they are large
+ // enough this function should trivially optimize to a no-op.
+
+ #if MPACK_BUILDER_INTERNAL_STORAGE
+ // make sure the internal storage is big enough to be useful
+ MPACK_STATIC_ASSERT(MPACK_BUILDER_INTERNAL_STORAGE_SIZE >= (sizeof(mpack_builder_page_t) +
+ sizeof(mpack_build_t) + MPACK_WRITER_MINIMUM_BUFFER_SIZE),
+ "MPACK_BUILDER_INTERNAL_STORAGE_SIZE is too small to be useful!");
+ if (MPACK_BUILDER_INTERNAL_STORAGE_SIZE < (sizeof(mpack_builder_page_t) +
+ sizeof(mpack_build_t) + MPACK_WRITER_MINIMUM_BUFFER_SIZE))
+ {
+ mpack_break("MPACK_BUILDER_INTERNAL_STORAGE_SIZE is too small to be useful!");
+ mpack_writer_flag_error(writer, mpack_error_bug);
+ }
+ #endif
+
+ // make sure the builder page size is big enough to be useful
+ MPACK_STATIC_ASSERT(MPACK_BUILDER_PAGE_SIZE >= (sizeof(mpack_builder_page_t) +
+ sizeof(mpack_build_t) + MPACK_WRITER_MINIMUM_BUFFER_SIZE),
+ "MPACK_BUILDER_PAGE_SIZE is too small to be useful!");
+ if (MPACK_BUILDER_PAGE_SIZE < (sizeof(mpack_builder_page_t) +
+ sizeof(mpack_build_t) + MPACK_WRITER_MINIMUM_BUFFER_SIZE))
+ {
+ mpack_break("MPACK_BUILDER_PAGE_SIZE is too small to be useful!");
+ mpack_writer_flag_error(writer, mpack_error_bug);
+ }
+}
+
+static inline size_t mpack_builder_page_size(mpack_writer_t* writer, mpack_builder_page_t* page) {
+ #if MPACK_BUILDER_INTERNAL_STORAGE
+ if ((char*)page == writer->builder.internal)
+ return sizeof(writer->builder.internal);
+ #else
+ (void)writer;
+ (void)page;
+ #endif
+ return MPACK_BUILDER_PAGE_SIZE;
+}
+
+static inline size_t mpack_builder_align_build(size_t bytes_used) {
+ size_t offset = bytes_used;
+ offset += MPACK_BUILD_ALIGNMENT - 1;
+ offset -= offset % MPACK_BUILD_ALIGNMENT;
+ mpack_log("aligned %zi to %zi\n", bytes_used, offset);
+ return offset;
+}
+
+static inline void mpack_builder_free_page(mpack_writer_t* writer, mpack_builder_page_t* page) {
+ mpack_log("freeing page %p\n", (void*)page);
+ #if MPACK_BUILDER_INTERNAL_STORAGE
+ if ((char*)page == writer->builder.internal)
+ return;
+ #else
+ (void)writer;
+ #endif
+ MPACK_FREE(page);
+}
+
+static inline size_t mpack_builder_page_remaining(mpack_writer_t* writer, mpack_builder_page_t* page) {
+ return mpack_builder_page_size(writer, page) - page->bytes_used;
+}
+
+static void mpack_builder_configure_buffer(mpack_writer_t* writer) {
+ if (mpack_writer_error(writer) != mpack_ok)
+ return;
+ mpack_builder_t* builder = &writer->builder;
+
+ mpack_builder_page_t* page = builder->current_page;
+ mpack_assert(page != NULL, "page is null??");
+
+ // This diverts the writer into the remainder of the current page of our
+ // build buffer.
+ writer->buffer = (char*)page + page->bytes_used;
+ writer->position = (char*)page + page->bytes_used;
+ writer->end = (char*)page + mpack_builder_page_size(writer, page);
+ mpack_log("configuring buffer from %p to %p\n", (void*)writer->position, (void*)writer->end);
+}
+
+static void mpack_builder_add_page(mpack_writer_t* writer) {
+ mpack_builder_t* builder = &writer->builder;
+ mpack_assert(writer->error == mpack_ok);
+
+ mpack_log("adding a page.\n");
+ mpack_builder_page_t* page = (mpack_builder_page_t*)MPACK_MALLOC(MPACK_BUILDER_PAGE_SIZE);
+ if (page == NULL) {
+ mpack_writer_flag_error(writer, mpack_error_memory);
+ return;
+ }
+
+ page->next = NULL;
+ page->bytes_used = sizeof(mpack_builder_page_t);
+ builder->current_page->next = page;
+ builder->current_page = page;
+}
+
+// Checks how many bytes the writer wrote to the page, adding it to the page's
+// bytes_used. This must be followed up with mpack_builder_configure_buffer()
+// (after adding a new page, build, etc) to reset the writer's buffer pointers.
+static void mpack_builder_apply_writes(mpack_writer_t* writer) {
+ mpack_assert(writer->error == mpack_ok);
+ mpack_builder_t* builder = &writer->builder;
+ mpack_log("latest build is %p\n", (void*)builder->latest_build);
+
+ // The difference between buffer and current is the number of bytes that
+ // were written to the page.
+ size_t bytes_written = (size_t)(writer->position - writer->buffer);
+ mpack_log("applying write of %zi bytes to build %p\n", bytes_written, (void*)builder->latest_build);
+
+ mpack_assert(builder->current_page != NULL);
+ mpack_assert(builder->latest_build != NULL);
+ builder->current_page->bytes_used += bytes_written;
+ builder->latest_build->bytes += bytes_written;
+ mpack_log("latest build %p now has %zi bytes\n", (void*)builder->latest_build, builder->latest_build->bytes);
+}
+
+static void mpack_builder_flush(mpack_writer_t* writer) {
+ mpack_assert(writer->error == mpack_ok);
+ mpack_builder_apply_writes(writer);
+ mpack_builder_add_page(writer);
+ mpack_builder_configure_buffer(writer);
+}
+
+MPACK_NOINLINE static void mpack_builder_begin(mpack_writer_t* writer) {
+ mpack_builder_t* builder = &writer->builder;
+ mpack_assert(writer->error == mpack_ok);
+ mpack_assert(builder->current_build == NULL);
+ mpack_assert(builder->latest_build == NULL);
+ mpack_assert(builder->pages == NULL);
+
+ // If this is the first build, we need to stash the real buffer backing our
+ // writer. We'll be diverting the writer to our build buffer.
+ builder->stash_buffer = writer->buffer;
+ builder->stash_position = writer->position;
+ builder->stash_end = writer->end;
+
+ mpack_builder_page_t* page;
+
+ // we've checked that both these sizes are large enough above.
+ #if MPACK_BUILDER_INTERNAL_STORAGE
+ page = (mpack_builder_page_t*)builder->internal;
+ mpack_log("beginning builder with internal storage %p\n", (void*)page);
+ #else
+ page = (mpack_builder_page_t*)MPACK_MALLOC(MPACK_BUILDER_PAGE_SIZE);
+ if (page == NULL) {
+ mpack_writer_flag_error(writer, mpack_error_memory);
+ return;
+ }
+ mpack_log("beginning builder with allocated page %p\n", (void*)page);
+ #endif
+
+ page->next = NULL;
+ page->bytes_used = sizeof(mpack_builder_page_t);
+ builder->pages = page;
+ builder->current_page = page;
+}
+
+static void mpack_builder_build(mpack_writer_t* writer, mpack_type_t type) {
+ mpack_builder_check_sizes(writer);
+ if (mpack_writer_error(writer) != mpack_ok)
+ return;
+
+ mpack_writer_track_element(writer);
+ mpack_writer_track_push_builder(writer, type);
+
+ mpack_builder_t* builder = &writer->builder;
+
+ if (builder->current_build == NULL) {
+ mpack_builder_begin(writer);
+ } else {
+ mpack_builder_apply_writes(writer);
+ }
+ if (mpack_writer_error(writer) != mpack_ok)
+ return;
+
+ // find aligned space for a new build. if there isn't enough space in the
+ // current page, we discard the remaining space in it and allocate a new
+ // page.
+ size_t offset = mpack_builder_align_build(builder->current_page->bytes_used);
+ if (offset + sizeof(mpack_build_t) > mpack_builder_page_size(writer, builder->current_page)) {
+ mpack_log("not enough space for a build. %zi bytes used of %zi in this page\n",
+ builder->current_page->bytes_used, mpack_builder_page_size(writer, builder->current_page));
+ mpack_builder_add_page(writer);
+ // there is always enough space in a fresh page.
+ offset = mpack_builder_align_build(builder->current_page->bytes_used);
+ }
+
+ // allocate the build within the page. note that we don't keep track of the
+ // space wasted due to the offset. instead the previous build has stored
+ // how many bytes follow it, and we'll redo this offset calculation to find
+ // this build after it.
+ mpack_builder_page_t* page = builder->current_page;
+ page->bytes_used = offset + sizeof(mpack_build_t);
+ mpack_assert(page->bytes_used <= mpack_builder_page_size(writer, page));
+ mpack_build_t* build = (mpack_build_t*)((char*)page + offset);
+ mpack_log("created new build %p within page %p, which now has %zi bytes used\n",
+ (void*)build, (void*)page, page->bytes_used);
+
+ // configure the new build
+ build->parent = builder->current_build;
+ build->bytes = 0;
+ build->count = 0;
+ build->type = type;
+ build->key_needs_value = false;
+ build->nested_compound_elements = 0;
+
+ mpack_log("setting current and latest build to new build %p\n", (void*)build);
+ builder->current_build = build;
+ builder->latest_build = build;
+
+ // we always need to provide a buffer that meets the minimum buffer size.
+ // if there isn't enough space, we discard the remaining space in the
+ // current page and allocate a new one.
+ if (mpack_builder_page_remaining(writer, page) < MPACK_WRITER_MINIMUM_BUFFER_SIZE) {
+ mpack_log("less than minimum buffer size in current page. %zi bytes used of %zi in this page\n",
+ builder->current_page->bytes_used, mpack_builder_page_size(writer, builder->current_page));
+ mpack_builder_add_page(writer);
+ if (mpack_writer_error(writer) != mpack_ok)
+ return;
+ }
+ mpack_assert(mpack_builder_page_remaining(writer, builder->current_page) >= MPACK_WRITER_MINIMUM_BUFFER_SIZE);
+ mpack_builder_configure_buffer(writer);
+}
+
+MPACK_NOINLINE
+static void mpack_builder_resolve(mpack_writer_t* writer) {
+ mpack_builder_t* builder = &writer->builder;
+
+ // 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 =
+ #if MPACK_BUILDER_INTERNAL_STORAGE
+ (mpack_builder_page_t*)builder->internal
+ #else
+ builder->pages
+ #endif
+ ;
+
+ // We start by restoring the writer's original buffer so we can write the
+ // data for real.
+ writer->buffer = builder->stash_buffer;
+ writer->position = builder->stash_position;
+ writer->end = builder->stash_end;
+
+ // We can also close out the build now.
+ builder->current_build = NULL;
+ builder->latest_build = NULL;
+ builder->current_page = NULL;
+ builder->pages = NULL;
+
+ // the starting page always starts with the first build
+ size_t offset = mpack_builder_align_build(sizeof(mpack_builder_page_t));
+ mpack_build_t* build = (mpack_build_t*)((char*)page + offset);
+ mpack_log("starting resolve with build %p in page %p\n", (void*)build, (void*)page);
+
+ // encoded data immediately follows the build
+ offset += sizeof(mpack_build_t);
+
+ // 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.
+ while (true) {
+
+ // write out the container tag
+ mpack_log("writing out an %s with count %u followed by %zi bytes\n",
+ mpack_type_to_string(build->type), build->count, build->bytes);
+ switch (build->type) {
+ case mpack_type_map:
+ mpack_write_map_notrack(writer, build->count);
+ break;
+ case mpack_type_array:
+ mpack_write_array_notrack(writer, build->count);
+ break;
+ default:
+ mpack_break("invalid type in builder?");
+ mpack_writer_flag_error(writer, mpack_error_bug);
+ return;
+ }
+
+ // figure out how many bytes follow this container. we're going to be
+ // freeing pages as we write, so we need to be done with this build.
+ size_t left = build->bytes;
+ build = NULL;
+
+ // write out all bytes following this container
+ while (left > 0) {
+ size_t bytes_used = page->bytes_used;
+ if (offset < bytes_used) {
+ size_t step = bytes_used - offset;
+ if (step > left)
+ step = left;
+ mpack_log("writing out %zi bytes starting at %p in page %p\n",
+ step, (void*)((char*)page + offset), (void*)page);
+ mpack_write_native(writer, (char*)page + offset, step);
+ offset += step;
+ left -= step;
+ }
+
+ if (left == 0) {
+ mpack_log("done writing bytes for this build\n");
+ break;
+ }
+
+ // still need to write more bytes. free this page and jump to the
+ // next one.
+ mpack_builder_page_t* next_page = page->next;
+ mpack_builder_free_page(writer, page);
+ page = next_page;
+ // bytes on the next page immediately follow the header.
+ offset = sizeof(mpack_builder_page_t);
+ }
+
+ // 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)) {
+ 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);
+ page = next_page;
+ if (page == NULL) {
+ mpack_log("no more pages\n");
+ // there are no more pages. we're done.
+ break;
+ }
+ offset = mpack_builder_align_build(sizeof(mpack_builder_page_t));
+ }
+ if (offset + sizeof(mpack_build_t) > page->bytes_used) {
+ // there is no more data. we're done.
+ mpack_log("no more data\n");
+ mpack_builder_free_page(writer, page);
+ break;
+ }
+
+ // we've found another build. loop around!
+ build = (mpack_build_t*)((char*)page + offset);
+ offset += sizeof(mpack_build_t);
+ mpack_log("found build %p\n", (void*)build);
+ }
+
+ mpack_log("done resolve.\n");
+}
+
+static void mpack_builder_complete(mpack_writer_t* writer, mpack_type_t 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!");
+ mpack_assert(builder->current_build->type == type, "completing wrong type!");
+ mpack_log("completing build %p\n", (void*)builder->current_build);
+
+ if (builder->current_build->key_needs_value) {
+ mpack_break("an odd number of elements were written in a map!");
+ mpack_writer_flag_error(writer, mpack_error_bug);
+ return;
+ }
+
+ if (builder->current_build->nested_compound_elements != 0) {
+ mpack_break("there is a nested unfinished non-build map or array in this build.");
+ mpack_writer_flag_error(writer, mpack_error_bug);
+ return;
+ }
+
+ // We need to apply whatever writes have been made to the current build
+ // before popping it.
+ mpack_builder_apply_writes(writer);
+
+ // For a nested build, we just switch the current build back to its parent.
+ if (builder->current_build->parent != NULL) {
+ mpack_log("setting current build to parent build %p. latest is still %p.\n",
+ (void*)builder->current_build->parent, (void*)builder->latest_build);
+ builder->current_build = builder->current_build->parent;
+ mpack_builder_configure_buffer(writer);
+ } else {
+ // We're completing the final build.
+ mpack_builder_resolve(writer);
+ }
+}
+
+void mpack_build_map(mpack_writer_t* writer) {
+ mpack_builder_build(writer, mpack_type_map);
+}
+
+void mpack_build_array(mpack_writer_t* writer) {
+ mpack_builder_build(writer, mpack_type_array);
+}
+
+void mpack_complete_map(mpack_writer_t* writer) {
+ mpack_builder_complete(writer, mpack_type_map);
+}
+
+void mpack_complete_array(mpack_writer_t* writer) {
+ mpack_builder_complete(writer, mpack_type_array);
+}
+
+#endif // MPACK_BUILDER
+#endif // MPACK_WRITER
+
+MPACK_SILENCE_WARNINGS_END
+
+/* mpack/mpack-reader.c.c */
+
+#define MPACK_INTERNAL 1
+
+/* #include "mpack-reader.h" */
+
+MPACK_SILENCE_WARNINGS_BEGIN
+
+#if MPACK_READER
+
+static void mpack_reader_skip_using_fill(mpack_reader_t* reader, size_t count);
+
+void mpack_reader_init(mpack_reader_t* reader, char* buffer, size_t size, size_t count) {
+ mpack_assert(buffer != NULL, "buffer is NULL");
+
+ mpack_memset(reader, 0, sizeof(*reader));
+ reader->buffer = buffer;
+ reader->size = size;
+ reader->data = buffer;
+ reader->end = buffer + count;
+
+ #if MPACK_READ_TRACKING
+ mpack_reader_flag_if_error(reader, mpack_track_init(&reader->track));
+ #endif
+
+ mpack_log("===========================\n");
+ mpack_log("initializing reader with buffer size %i\n", (int)size);
+}
+
+void mpack_reader_init_error(mpack_reader_t* reader, mpack_error_t error) {
+ mpack_memset(reader, 0, sizeof(*reader));
+ reader->error = error;
+
+ mpack_log("===========================\n");
+ mpack_log("initializing reader error state %i\n", (int)error);
+}
+
+void mpack_reader_init_data(mpack_reader_t* reader, const char* data, size_t count) {
+ mpack_assert(data != NULL, "data is NULL");
+
+ mpack_memset(reader, 0, sizeof(*reader));
+ reader->data = data;
+ reader->end = data + count;
+
+ #if MPACK_READ_TRACKING
+ mpack_reader_flag_if_error(reader, mpack_track_init(&reader->track));
+ #endif
+
+ mpack_log("===========================\n");
+ mpack_log("initializing reader with data size %i\n", (int)count);
+}
+
+void mpack_reader_set_fill(mpack_reader_t* reader, mpack_reader_fill_t fill) {
+ MPACK_STATIC_ASSERT(MPACK_READER_MINIMUM_BUFFER_SIZE >= MPACK_MAXIMUM_TAG_SIZE,
+ "minimum buffer size must fit any tag!");
+
+ if (reader->size == 0) {
+ mpack_break("cannot use fill function without a writeable buffer!");
+ mpack_reader_flag_error(reader, mpack_error_bug);
+ return;
+ }
+
+ if (reader->size < MPACK_READER_MINIMUM_BUFFER_SIZE) {
+ mpack_break("buffer size is %i, but minimum buffer size for fill is %i",
+ (int)reader->size, MPACK_READER_MINIMUM_BUFFER_SIZE);
+ mpack_reader_flag_error(reader, mpack_error_bug);
+ return;
+ }
+
+ reader->fill = fill;
+}
+
+void mpack_reader_set_skip(mpack_reader_t* reader, mpack_reader_skip_t skip) {
+ mpack_assert(reader->size != 0, "cannot use skip function without a writeable buffer!");
+ reader->skip = skip;
+}
+
+#if MPACK_STDIO
+static size_t mpack_file_reader_fill(mpack_reader_t* reader, char* buffer, size_t count) {
+ if (feof((FILE *)reader->context)) {
+ mpack_reader_flag_error(reader, mpack_error_eof);
+ return 0;
+ }
+ return fread((void*)buffer, 1, count, (FILE*)reader->context);
+}
+
+static void mpack_file_reader_skip(mpack_reader_t* reader, size_t count) {
+ if (mpack_reader_error(reader) != mpack_ok)
+ return;
+ FILE* file = (FILE*)reader->context;
+
+ // We call ftell() to test whether the stream is seekable
+ // without causing a file error.
+ if (ftell(file) >= 0) {
+ mpack_log("seeking forward %i bytes\n", (int)count);
+ if (fseek(file, (long int)count, SEEK_CUR) == 0)
+ return;
+ mpack_log("fseek() didn't return zero!\n");
+ if (ferror(file)) {
+ mpack_reader_flag_error(reader, mpack_error_io);
+ return;
+ }
+ }
+
+ // If the stream is not seekable, fall back to the fill function.
+ mpack_reader_skip_using_fill(reader, count);
+}
+
+static void mpack_file_reader_teardown(mpack_reader_t* reader) {
+ MPACK_FREE(reader->buffer);
+ reader->buffer = NULL;
+ reader->context = NULL;
+ reader->size = 0;
+ reader->fill = NULL;
+ reader->skip = NULL;
+ reader->teardown = NULL;
+}
+
+static void mpack_file_reader_teardown_close(mpack_reader_t* reader) {
+ FILE* file = (FILE*)reader->context;
+
+ if (file) {
+ int ret = fclose(file);
+ if (ret != 0)
+ mpack_reader_flag_error(reader, mpack_error_io);
+ }
+
+ mpack_file_reader_teardown(reader);
+}
+
+void mpack_reader_init_stdfile(mpack_reader_t* reader, FILE* file, bool close_when_done) {
+ mpack_assert(file != NULL, "file is NULL");
+
+ size_t capacity = MPACK_BUFFER_SIZE;
+ char* buffer = (char*)MPACK_MALLOC(capacity);
+ if (buffer == NULL) {
+ mpack_reader_init_error(reader, mpack_error_memory);
+ if (close_when_done) {
+ fclose(file);
+ }
+ return;
+ }
+
+ mpack_reader_init(reader, buffer, capacity, 0);
+ mpack_reader_set_context(reader, file);
+ mpack_reader_set_fill(reader, mpack_file_reader_fill);
+ mpack_reader_set_skip(reader, mpack_file_reader_skip);
+ mpack_reader_set_teardown(reader, close_when_done ?
+ mpack_file_reader_teardown_close :
+ mpack_file_reader_teardown);
+}
+
+void mpack_reader_init_filename(mpack_reader_t* reader, const char* filename) {
+ mpack_assert(filename != NULL, "filename is NULL");
+
+ FILE* file = fopen(filename, "rb");
+ if (file == NULL) {
+ mpack_reader_init_error(reader, mpack_error_io);
+ return;
+ }
+
+ mpack_reader_init_stdfile(reader, file, true);
+}
+#endif
+
+mpack_error_t mpack_reader_destroy(mpack_reader_t* reader) {
+
+ // clean up tracking, asserting if we're not already in an error state
+ #if MPACK_READ_TRACKING
+ mpack_reader_flag_if_error(reader, mpack_track_destroy(&reader->track, mpack_reader_error(reader) != mpack_ok));
+ #endif
+
+ if (reader->teardown)
+ reader->teardown(reader);
+ reader->teardown = NULL;
+
+ return reader->error;
+}
+
+size_t mpack_reader_remaining(mpack_reader_t* reader, const char** data) {
+ if (mpack_reader_error(reader) != mpack_ok)
+ return 0;
+
+ #if MPACK_READ_TRACKING
+ if (mpack_reader_flag_if_error(reader, mpack_track_check_empty(&reader->track)) != mpack_ok)
+ return 0;
+ #endif
+
+ if (data)
+ *data = reader->data;
+ return (size_t)(reader->end - reader->data);
+}
+
+void mpack_reader_flag_error(mpack_reader_t* reader, mpack_error_t error) {
+ mpack_log("reader %p setting error %i: %s\n", (void*)reader, (int)error, mpack_error_to_string(error));
+
+ if (reader->error == mpack_ok) {
+ reader->error = error;
+ reader->end = reader->data;
+ if (reader->error_fn)
+ reader->error_fn(reader, error);
+ }
+}
+
+// Loops on the fill function, reading between the minimum and
+// maximum number of bytes and flagging an error if it fails.
+MPACK_NOINLINE static size_t mpack_fill_range(mpack_reader_t* reader, char* p, size_t min_bytes, size_t max_bytes) {
+ mpack_assert(reader->fill != NULL, "mpack_fill_range() called with no fill function?");
+ mpack_assert(min_bytes > 0, "cannot fill zero bytes!");
+ mpack_assert(max_bytes >= min_bytes, "min_bytes %i cannot be larger than max_bytes %i!",
+ (int)min_bytes, (int)max_bytes);
+
+ size_t count = 0;
+ while (count < min_bytes) {
+ size_t read = reader->fill(reader, p + count, max_bytes - count);
+
+ // Reader fill functions can flag an error or return 0 on failure. We
+ // also guard against functions that return -1 just in case.
+ if (mpack_reader_error(reader) != mpack_ok)
+ return 0;
+ if (read == 0 || read == ((size_t)(-1))) {
+ mpack_reader_flag_error(reader, mpack_error_io);
+ return 0;
+ }
+
+ count += read;
+ }
+ return count;
+}
+
+MPACK_NOINLINE bool mpack_reader_ensure_straddle(mpack_reader_t* reader, size_t count) {
+ mpack_assert(count != 0, "cannot ensure zero bytes!");
+ mpack_assert(reader->error == mpack_ok, "reader cannot be in an error state!");
+
+ mpack_assert(count > (size_t)(reader->end - reader->data),
+ "straddling ensure requested for %i bytes, but there are %i bytes "
+ "left in buffer. call mpack_reader_ensure() instead",
+ (int)count, (int)(reader->end - reader->data));
+
+ // we'll need a fill function to get more data. if there's no
+ // fill function, the buffer should contain an entire MessagePack
+ // object, so we raise mpack_error_invalid instead of mpack_error_io
+ // on truncated data.
+ if (reader->fill == NULL) {
+ mpack_reader_flag_error(reader, mpack_error_invalid);
+ return false;
+ }
+
+ // we need enough space in the buffer. if the buffer is not
+ // big enough, we return mpack_error_too_big (since this is
+ // for an in-place read larger than the buffer size.)
+ if (count > reader->size) {
+ mpack_reader_flag_error(reader, mpack_error_too_big);
+ return false;
+ }
+
+ // move the existing data to the start of the buffer
+ size_t left = (size_t)(reader->end - reader->data);
+ mpack_memmove(reader->buffer, reader->data, left);
+ reader->end -= reader->data - reader->buffer;
+ reader->data = reader->buffer;
+
+ // read at least the necessary number of bytes, accepting up to the
+ // buffer size
+ size_t read = mpack_fill_range(reader, reader->buffer + left,
+ count - left, reader->size - left);
+ if (mpack_reader_error(reader) != mpack_ok)
+ return false;
+ reader->end += read;
+ return true;
+}
+
+// Reads count bytes into p. Used when there are not enough bytes
+// left in the buffer to satisfy a read.
+MPACK_NOINLINE void mpack_read_native_straddle(mpack_reader_t* reader, char* p, size_t count) {
+ mpack_assert(count == 0 || p != NULL, "data pointer for %i bytes is NULL", (int)count);
+
+ if (mpack_reader_error(reader) != mpack_ok) {
+ mpack_memset(p, 0, count);
+ return;
+ }
+
+ size_t left = (size_t)(reader->end - reader->data);
+ mpack_log("big read for %i bytes into %p, %i left in buffer, buffer size %i\n",
+ (int)count, p, (int)left, (int)reader->size);
+
+ if (count <= left) {
+ mpack_assert(0,
+ "big read requested for %i bytes, but there are %i bytes "
+ "left in buffer. call mpack_read_native() instead",
+ (int)count, (int)left);
+ mpack_reader_flag_error(reader, mpack_error_bug);
+ mpack_memset(p, 0, count);
+ return;
+ }
+
+ // we'll need a fill function to get more data. if there's no
+ // fill function, the buffer should contain an entire MessagePack
+ // object, so we raise mpack_error_invalid instead of mpack_error_io
+ // on truncated data.
+ if (reader->fill == NULL) {
+ mpack_reader_flag_error(reader, mpack_error_invalid);
+ mpack_memset(p, 0, count);
+ return;
+ }
+
+ if (reader->size == 0) {
+ // somewhat debatable what error should be returned here. when
+ // initializing a reader with an in-memory buffer it's not
+ // necessarily a bug if the data is blank; it might just have
+ // been truncated to zero. for this reason we return the same
+ // error as if the data was truncated.
+ mpack_reader_flag_error(reader, mpack_error_io);
+ mpack_memset(p, 0, count);
+ return;
+ }
+
+ // flush what's left of the buffer
+ if (left > 0) {
+ mpack_log("flushing %i bytes remaining in buffer\n", (int)left);
+ mpack_memcpy(p, reader->data, left);
+ count -= left;
+ p += left;
+ reader->data += left;
+ }
+
+ // if the remaining data needed is some small fraction of the
+ // buffer size, we'll try to fill the buffer as much as possible
+ // and copy the needed data out.
+ if (count <= reader->size / MPACK_READER_SMALL_FRACTION_DENOMINATOR) {
+ size_t read = mpack_fill_range(reader, reader->buffer, count, reader->size);
+ if (mpack_reader_error(reader) != mpack_ok)
+ return;
+ mpack_memcpy(p, reader->buffer, count);
+ reader->data = reader->buffer + count;
+ reader->end = reader->buffer + read;
+
+ // otherwise we read the remaining data directly into the target.
+ } else {
+ mpack_log("reading %i additional bytes\n", (int)count);
+ mpack_fill_range(reader, p, count, count);
+ }
+}
+
+MPACK_NOINLINE static void mpack_skip_bytes_straddle(mpack_reader_t* reader, size_t count) {
+
+ // we'll need at least a fill function to skip more data. if there's
+ // no fill function, the buffer should contain an entire MessagePack
+ // object, so we raise mpack_error_invalid instead of mpack_error_io
+ // on truncated data. (see mpack_read_native_straddle())
+ if (reader->fill == NULL) {
+ mpack_log("reader has no fill function!\n");
+ mpack_reader_flag_error(reader, mpack_error_invalid);
+ return;
+ }
+
+ // discard whatever's left in the buffer
+ size_t left = (size_t)(reader->end - reader->data);
+ mpack_log("discarding %i bytes still in buffer\n", (int)left);
+ count -= left;
+ reader->data = reader->end;
+
+ // use the skip function if we've got one, and if we're trying
+ // to skip a lot of data. if we only need to skip some tiny
+ // fraction of the buffer size, it's probably better to just
+ // fill the buffer and skip from it instead of trying to seek.
+ if (reader->skip && count > reader->size / 16) {
+ mpack_log("calling skip function for %i bytes\n", (int)count);
+ reader->skip(reader, count);
+ return;
+ }
+
+ mpack_reader_skip_using_fill(reader, count);
+}
+
+void mpack_skip_bytes(mpack_reader_t* reader, size_t count) {
+ if (mpack_reader_error(reader) != mpack_ok)
+ return;
+ mpack_log("skip requested for %i bytes\n", (int)count);
+
+ mpack_reader_track_bytes(reader, count);
+
+ // 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);
+ reader->data += count;
+ return;
+ }
+
+ mpack_skip_bytes_straddle(reader, count);
+}
+
+MPACK_NOINLINE static void mpack_reader_skip_using_fill(mpack_reader_t* reader, size_t count) {
+ mpack_assert(reader->fill != NULL, "missing fill function!");
+ mpack_assert(reader->data == reader->end, "there are bytes left in the buffer!");
+ mpack_assert(reader->error == mpack_ok, "should not have called this in an error state (%i)", reader->error);
+ mpack_log("skip using fill for %i bytes\n", (int)count);
+
+ // fill and discard multiples of the buffer size
+ while (count > reader->size) {
+ mpack_log("filling and discarding buffer of %i bytes\n", (int)reader->size);
+ if (mpack_fill_range(reader, reader->buffer, reader->size, reader->size) < reader->size) {
+ mpack_reader_flag_error(reader, mpack_error_io);
+ return;
+ }
+ count -= reader->size;
+ }
+
+ // fill the buffer as much as possible
+ reader->data = reader->buffer;
+ size_t read = mpack_fill_range(reader, reader->buffer, count, reader->size);
+ if (read < count) {
+ mpack_reader_flag_error(reader, mpack_error_io);
+ return;
+ }
+ reader->end = reader->data + read;
+ mpack_log("filled %i bytes into buffer; discarding %i bytes\n", (int)read, (int)count);
+ reader->data += count;
+}
+
+void mpack_read_bytes(mpack_reader_t* reader, char* p, size_t count) {
+ mpack_assert(p != NULL, "destination for read of %i bytes is NULL", (int)count);
+ mpack_reader_track_bytes(reader, count);
+ mpack_read_native(reader, p, count);
+}
+
+void mpack_read_utf8(mpack_reader_t* reader, char* p, size_t byte_count) {
+ mpack_assert(p != NULL, "destination for read of %i bytes is NULL", (int)byte_count);
+ mpack_reader_track_str_bytes_all(reader, byte_count);
+ mpack_read_native(reader, p, byte_count);
+
+ if (mpack_reader_error(reader) == mpack_ok && !mpack_utf8_check(p, byte_count))
+ mpack_reader_flag_error(reader, mpack_error_type);
+}
+
+static void mpack_read_cstr_unchecked(mpack_reader_t* reader, char* buf, size_t buffer_size, size_t byte_count) {
+ mpack_assert(buf != NULL, "destination for read of %i bytes is NULL", (int)byte_count);
+ mpack_assert(buffer_size >= 1, "buffer size is zero; you must have room for at least a null-terminator");
+
+ if (mpack_reader_error(reader)) {
+ buf[0] = 0;
+ return;
+ }
+
+ if (byte_count > buffer_size - 1) {
+ mpack_reader_flag_error(reader, mpack_error_too_big);
+ buf[0] = 0;
+ return;
+ }
+
+ mpack_reader_track_str_bytes_all(reader, byte_count);
+ mpack_read_native(reader, buf, byte_count);
+ buf[byte_count] = 0;
+}
+
+void mpack_read_cstr(mpack_reader_t* reader, char* buf, size_t buffer_size, size_t byte_count) {
+ mpack_read_cstr_unchecked(reader, buf, buffer_size, byte_count);
+
+ // check for null bytes
+ if (mpack_reader_error(reader) == mpack_ok && !mpack_str_check_no_null(buf, byte_count)) {
+ buf[0] = 0;
+ mpack_reader_flag_error(reader, mpack_error_type);
+ }
+}
+
+void mpack_read_utf8_cstr(mpack_reader_t* reader, char* buf, size_t buffer_size, size_t byte_count) {
+ mpack_read_cstr_unchecked(reader, buf, buffer_size, byte_count);
+
+ // check encoding
+ if (mpack_reader_error(reader) == mpack_ok && !mpack_utf8_check_no_null(buf, byte_count)) {
+ buf[0] = 0;
+ mpack_reader_flag_error(reader, mpack_error_type);
+ }
+}
+
+#ifdef MPACK_MALLOC
+// Reads native bytes with error callback disabled. This allows MPack reader functions
+// to hold an allocated buffer and read native data into it without leaking it in
+// case of a non-local jump (longjmp, throw) out of an error handler.
+static void mpack_read_native_noerrorfn(mpack_reader_t* reader, char* p, size_t count) {
+ mpack_assert(reader->error == mpack_ok, "cannot call if an error is already flagged!");
+ mpack_reader_error_t error_fn = reader->error_fn;
+ reader->error_fn = NULL;
+ mpack_read_native(reader, p, count);
+ reader->error_fn = error_fn;
+}
+
+char* mpack_read_bytes_alloc_impl(mpack_reader_t* reader, size_t count, bool null_terminated) {
+
+ // track the bytes first in case it jumps
+ mpack_reader_track_bytes(reader, count);
+ if (mpack_reader_error(reader) != mpack_ok)
+ return NULL;
+
+ // cannot allocate zero bytes. this is not an error.
+ if (count == 0 && null_terminated == false)
+ return NULL;
+
+ // allocate data
+ char* data = (char*)MPACK_MALLOC(count + (null_terminated ? 1 : 0)); // TODO: can this overflow?
+ if (data == NULL) {
+ mpack_reader_flag_error(reader, mpack_error_memory);
+ return NULL;
+ }
+
+ // read with error callback disabled so we don't leak our buffer
+ mpack_read_native_noerrorfn(reader, data, count);
+
+ // report flagged errors
+ if (mpack_reader_error(reader) != mpack_ok) {
+ MPACK_FREE(data);
+ if (reader->error_fn)
+ reader->error_fn(reader, mpack_reader_error(reader));
+ return NULL;
+ }
+
+ if (null_terminated)
+ data[count] = '\0';
+ return data;
+}
+#endif
+
+// read inplace without tracking (since there are different
+// tracking modes for different inplace readers)
+static const char* mpack_read_bytes_inplace_notrack(mpack_reader_t* reader, size_t count) {
+ if (mpack_reader_error(reader) != mpack_ok)
+ return NULL;
+
+ // if we have enough bytes already in the buffer, we can return it directly.
+ if ((size_t)(reader->end - reader->data) >= count) {
+ const char* bytes = reader->data;
+ reader->data += count;
+ return bytes;
+ }
+
+ if (!mpack_reader_ensure(reader, count))
+ return NULL;
+
+ const char* bytes = reader->data;
+ reader->data += count;
+ return bytes;
+}
+
+const char* mpack_read_bytes_inplace(mpack_reader_t* reader, size_t count) {
+ mpack_reader_track_bytes(reader, count);
+ return mpack_read_bytes_inplace_notrack(reader, count);
+}
+
+const char* mpack_read_utf8_inplace(mpack_reader_t* reader, size_t count) {
+ mpack_reader_track_str_bytes_all(reader, count);
+ const char* str = mpack_read_bytes_inplace_notrack(reader, count);
+
+ if (mpack_reader_error(reader) == mpack_ok && !mpack_utf8_check(str, count)) {
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return NULL;
+ }
+
+ return str;
+}
+
+static size_t mpack_parse_tag(mpack_reader_t* reader, mpack_tag_t* tag) {
+ mpack_assert(reader->error == mpack_ok, "reader cannot be in an error state!");
+
+ if (!mpack_reader_ensure(reader, 1))
+ return 0;
+ uint8_t type = mpack_load_u8(reader->data);
+
+ // unfortunately, by far the fastest way to parse a tag is to switch
+ // on the first byte, and to explicitly list every possible byte. so for
+ // infix types, the list of cases is quite large.
+ //
+ // in size-optimized builds, we switch on the top four bits first to
+ // handle most infix types with a smaller jump table to save space.
+
+ #if MPACK_OPTIMIZE_FOR_SIZE
+ switch (type >> 4) {
+
+ // positive fixnum
+ case 0x0: case 0x1: case 0x2: case 0x3:
+ case 0x4: case 0x5: case 0x6: case 0x7:
+ *tag = mpack_tag_make_uint(type);
+ return 1;
+
+ // negative fixnum
+ case 0xe: case 0xf:
+ *tag = mpack_tag_make_int((int8_t)type);
+ return 1;
+
+ // fixmap
+ case 0x8:
+ *tag = mpack_tag_make_map(type & ~0xf0u);
+ return 1;
+
+ // fixarray
+ case 0x9:
+ *tag = mpack_tag_make_array(type & ~0xf0u);
+ return 1;
+
+ // fixstr
+ case 0xa: case 0xb:
+ *tag = mpack_tag_make_str(type & ~0xe0u);
+ return 1;
+
+ // not one of the common infix types
+ default:
+ break;
+
+ }
+ #endif
+
+ // handle individual type tags
+ switch (type) {
+
+ #if !MPACK_OPTIMIZE_FOR_SIZE
+ // positive fixnum
+ 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:
+ *tag = mpack_tag_make_uint(type);
+ return 1;
+
+ // negative fixnum
+ 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:
+ *tag = mpack_tag_make_int((int8_t)type);
+ return 1;
+
+ // 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:
+ *tag = mpack_tag_make_map(type & ~0xf0u);
+ return 1;
+
+ // 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:
+ *tag = mpack_tag_make_array(type & ~0xf0u);
+ return 1;
+
+ // 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:
+ *tag = mpack_tag_make_str(type & ~0xe0u);
+ return 1;
+ #endif
+
+ // nil
+ case 0xc0:
+ *tag = mpack_tag_make_nil();
+ return 1;
+
+ // bool
+ case 0xc2: case 0xc3:
+ *tag = mpack_tag_make_bool((bool)(type & 1));
+ return 1;
+
+ // bin8
+ case 0xc4:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_BIN8))
+ return 0;
+ *tag = mpack_tag_make_bin(mpack_load_u8(reader->data + 1));
+ return MPACK_TAG_SIZE_BIN8;
+
+ // bin16
+ case 0xc5:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_BIN16))
+ return 0;
+ *tag = mpack_tag_make_bin(mpack_load_u16(reader->data + 1));
+ return MPACK_TAG_SIZE_BIN16;
+
+ // bin32
+ case 0xc6:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_BIN32))
+ return 0;
+ *tag = mpack_tag_make_bin(mpack_load_u32(reader->data + 1));
+ return MPACK_TAG_SIZE_BIN32;
+
+ #if MPACK_EXTENSIONS
+ // ext8
+ case 0xc7:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_EXT8))
+ return 0;
+ *tag = mpack_tag_make_ext(mpack_load_i8(reader->data + 2), mpack_load_u8(reader->data + 1));
+ return MPACK_TAG_SIZE_EXT8;
+
+ // ext16
+ case 0xc8:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_EXT16))
+ return 0;
+ *tag = mpack_tag_make_ext(mpack_load_i8(reader->data + 3), mpack_load_u16(reader->data + 1));
+ return MPACK_TAG_SIZE_EXT16;
+
+ // ext32
+ case 0xc9:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_EXT32))
+ return 0;
+ *tag = mpack_tag_make_ext(mpack_load_i8(reader->data + 5), mpack_load_u32(reader->data + 1));
+ return MPACK_TAG_SIZE_EXT32;
+ #endif
+
+ // float
+ case 0xca:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_FLOAT))
+ return 0;
+ #if MPACK_FLOAT
+ *tag = mpack_tag_make_float(mpack_load_float(reader->data + 1));
+ #else
+ *tag = mpack_tag_make_raw_float(mpack_load_u32(reader->data + 1));
+ #endif
+ return MPACK_TAG_SIZE_FLOAT;
+
+ // double
+ case 0xcb:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_DOUBLE))
+ return 0;
+ #if MPACK_DOUBLE
+ *tag = mpack_tag_make_double(mpack_load_double(reader->data + 1));
+ #else
+ *tag = mpack_tag_make_raw_double(mpack_load_u64(reader->data + 1));
+ #endif
+ return MPACK_TAG_SIZE_DOUBLE;
+
+ // uint8
+ case 0xcc:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_U8))
+ return 0;
+ *tag = mpack_tag_make_uint(mpack_load_u8(reader->data + 1));
+ return MPACK_TAG_SIZE_U8;
+
+ // uint16
+ case 0xcd:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_U16))
+ return 0;
+ *tag = mpack_tag_make_uint(mpack_load_u16(reader->data + 1));
+ return MPACK_TAG_SIZE_U16;
+
+ // uint32
+ case 0xce:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_U32))
+ return 0;
+ *tag = mpack_tag_make_uint(mpack_load_u32(reader->data + 1));
+ return MPACK_TAG_SIZE_U32;
+
+ // uint64
+ case 0xcf:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_U64))
+ return 0;
+ *tag = mpack_tag_make_uint(mpack_load_u64(reader->data + 1));
+ return MPACK_TAG_SIZE_U64;
+
+ // int8
+ case 0xd0:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_I8))
+ return 0;
+ *tag = mpack_tag_make_int(mpack_load_i8(reader->data + 1));
+ return MPACK_TAG_SIZE_I8;
+
+ // int16
+ case 0xd1:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_I16))
+ return 0;
+ *tag = mpack_tag_make_int(mpack_load_i16(reader->data + 1));
+ return MPACK_TAG_SIZE_I16;
+
+ // int32
+ case 0xd2:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_I32))
+ return 0;
+ *tag = mpack_tag_make_int(mpack_load_i32(reader->data + 1));
+ return MPACK_TAG_SIZE_I32;
+
+ // int64
+ case 0xd3:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_I64))
+ return 0;
+ *tag = mpack_tag_make_int(mpack_load_i64(reader->data + 1));
+ return MPACK_TAG_SIZE_I64;
+
+ #if MPACK_EXTENSIONS
+ // fixext1
+ case 0xd4:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_FIXEXT1))
+ return 0;
+ *tag = mpack_tag_make_ext(mpack_load_i8(reader->data + 1), 1);
+ return MPACK_TAG_SIZE_FIXEXT1;
+
+ // fixext2
+ case 0xd5:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_FIXEXT2))
+ return 0;
+ *tag = mpack_tag_make_ext(mpack_load_i8(reader->data + 1), 2);
+ return MPACK_TAG_SIZE_FIXEXT2;
+
+ // fixext4
+ case 0xd6:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_FIXEXT4))
+ return 0;
+ *tag = mpack_tag_make_ext(mpack_load_i8(reader->data + 1), 4);
+ return 2;
+
+ // fixext8
+ case 0xd7:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_FIXEXT8))
+ return 0;
+ *tag = mpack_tag_make_ext(mpack_load_i8(reader->data + 1), 8);
+ return MPACK_TAG_SIZE_FIXEXT8;
+
+ // fixext16
+ case 0xd8:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_FIXEXT16))
+ return 0;
+ *tag = mpack_tag_make_ext(mpack_load_i8(reader->data + 1), 16);
+ return MPACK_TAG_SIZE_FIXEXT16;
+ #endif
+
+ // str8
+ case 0xd9:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_STR8))
+ return 0;
+ *tag = mpack_tag_make_str(mpack_load_u8(reader->data + 1));
+ return MPACK_TAG_SIZE_STR8;
+
+ // str16
+ case 0xda:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_STR16))
+ return 0;
+ *tag = mpack_tag_make_str(mpack_load_u16(reader->data + 1));
+ return MPACK_TAG_SIZE_STR16;
+
+ // str32
+ case 0xdb:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_STR32))
+ return 0;
+ *tag = mpack_tag_make_str(mpack_load_u32(reader->data + 1));
+ return MPACK_TAG_SIZE_STR32;
+
+ // array16
+ case 0xdc:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_ARRAY16))
+ return 0;
+ *tag = mpack_tag_make_array(mpack_load_u16(reader->data + 1));
+ return MPACK_TAG_SIZE_ARRAY16;
+
+ // array32
+ case 0xdd:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_ARRAY32))
+ return 0;
+ *tag = mpack_tag_make_array(mpack_load_u32(reader->data + 1));
+ return MPACK_TAG_SIZE_ARRAY32;
+
+ // map16
+ case 0xde:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_MAP16))
+ return 0;
+ *tag = mpack_tag_make_map(mpack_load_u16(reader->data + 1));
+ return MPACK_TAG_SIZE_MAP16;
+
+ // map32
+ case 0xdf:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_MAP32))
+ return 0;
+ *tag = mpack_tag_make_map(mpack_load_u32(reader->data + 1));
+ return MPACK_TAG_SIZE_MAP32;
+
+ // reserved
+ case 0xc1:
+ mpack_reader_flag_error(reader, mpack_error_invalid);
+ return 0;
+
+ #if !MPACK_EXTENSIONS
+ // ext
+ case 0xc7: // fallthrough
+ case 0xc8: // fallthrough
+ case 0xc9: // fallthrough
+ // fixext
+ case 0xd4: // fallthrough
+ case 0xd5: // fallthrough
+ case 0xd6: // fallthrough
+ case 0xd7: // fallthrough
+ case 0xd8:
+ mpack_reader_flag_error(reader, mpack_error_unsupported);
+ return 0;
+ #endif
+
+ #if MPACK_OPTIMIZE_FOR_SIZE
+ // any other bytes should have been handled by the infix switch
+ default:
+ break;
+ #endif
+ }
+
+ mpack_assert(0, "unreachable");
+ return 0;
+}
+
+mpack_tag_t mpack_read_tag(mpack_reader_t* reader) {
+ mpack_log("reading tag\n");
+
+ // make sure we can read a tag
+ if (mpack_reader_error(reader) != mpack_ok)
+ return mpack_tag_nil();
+ if (mpack_reader_track_element(reader) != mpack_ok)
+ return mpack_tag_nil();
+
+ mpack_tag_t tag = MPACK_TAG_ZERO;
+ size_t count = mpack_parse_tag(reader, &tag);
+ if (count == 0)
+ return mpack_tag_nil();
+
+ #if MPACK_READ_TRACKING
+ mpack_error_t track_error = mpack_ok;
+
+ switch (tag.type) {
+ case mpack_type_map:
+ case mpack_type_array:
+ track_error = mpack_track_push(&reader->track, tag.type, tag.v.n);
+ break;
+ #if MPACK_EXTENSIONS
+ case mpack_type_ext:
+ #endif
+ case mpack_type_str:
+ case mpack_type_bin:
+ track_error = mpack_track_push(&reader->track, tag.type, tag.v.l);
+ break;
+ default:
+ break;
+ }
+
+ if (track_error != mpack_ok) {
+ mpack_reader_flag_error(reader, track_error);
+ return mpack_tag_nil();
+ }
+ #endif
+
+ reader->data += count;
+ return tag;
+}
+
+mpack_tag_t mpack_peek_tag(mpack_reader_t* reader) {
+ mpack_log("peeking tag\n");
+
+ // make sure we can peek a tag
+ if (mpack_reader_error(reader) != mpack_ok)
+ return mpack_tag_nil();
+ if (mpack_reader_track_peek_element(reader) != mpack_ok)
+ return mpack_tag_nil();
+
+ mpack_tag_t tag = MPACK_TAG_ZERO;
+ if (mpack_parse_tag(reader, &tag) == 0)
+ return mpack_tag_nil();
+ return tag;
+}
+
+void mpack_discard(mpack_reader_t* reader) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (mpack_reader_error(reader))
+ return;
+ switch (var.type) {
+ case mpack_type_str:
+ mpack_skip_bytes(reader, var.v.l);
+ mpack_done_str(reader);
+ break;
+ case mpack_type_bin:
+ mpack_skip_bytes(reader, var.v.l);
+ mpack_done_bin(reader);
+ break;
+ #if MPACK_EXTENSIONS
+ case mpack_type_ext:
+ mpack_skip_bytes(reader, var.v.l);
+ mpack_done_ext(reader);
+ break;
+ #endif
+ case mpack_type_array: {
+ for (; var.v.n > 0; --var.v.n) {
+ mpack_discard(reader);
+ if (mpack_reader_error(reader))
+ break;
+ }
+ mpack_done_array(reader);
+ break;
+ }
+ case mpack_type_map: {
+ for (; var.v.n > 0; --var.v.n) {
+ mpack_discard(reader);
+ mpack_discard(reader);
+ if (mpack_reader_error(reader))
+ break;
+ }
+ mpack_done_map(reader);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+#if MPACK_EXTENSIONS
+mpack_timestamp_t mpack_read_timestamp(mpack_reader_t* reader, size_t size) {
+ mpack_timestamp_t timestamp = {0, 0};
+
+ if (size != 4 && size != 8 && size != 12) {
+ mpack_reader_flag_error(reader, mpack_error_invalid);
+ return timestamp;
+ }
+
+ char buf[12];
+ mpack_read_bytes(reader, buf, size);
+ mpack_done_ext(reader);
+ if (mpack_reader_error(reader) != mpack_ok)
+ return timestamp;
+
+ switch (size) {
+ case 4:
+ timestamp.seconds = (int64_t)(uint64_t)mpack_load_u32(buf);
+ break;
+
+ case 8: {
+ uint64_t packed = mpack_load_u64(buf);
+ timestamp.seconds = (int64_t)(packed & ((MPACK_UINT64_C(1) << 34) - 1));
+ timestamp.nanoseconds = (uint32_t)(packed >> 34);
+ break;
+ }
+
+ case 12:
+ timestamp.nanoseconds = mpack_load_u32(buf);
+ timestamp.seconds = mpack_load_i64(buf + 4);
+ break;
+
+ default:
+ mpack_assert(false, "unreachable");
+ break;
+ }
+
+ if (timestamp.nanoseconds > MPACK_TIMESTAMP_NANOSECONDS_MAX) {
+ mpack_reader_flag_error(reader, mpack_error_invalid);
+ mpack_timestamp_t zero = {0, 0};
+ return zero;
+ }
+
+ return timestamp;
+}
+#endif
+
+#if MPACK_READ_TRACKING
+void mpack_done_type(mpack_reader_t* reader, mpack_type_t type) {
+ if (mpack_reader_error(reader) == mpack_ok)
+ mpack_reader_flag_if_error(reader, mpack_track_pop(&reader->track, type));
+}
+#endif
+
+#if MPACK_DEBUG && MPACK_STDIO
+static size_t mpack_print_read_prefix(mpack_reader_t* reader, size_t length, char* buffer, size_t buffer_size) {
+ if (length == 0)
+ return 0;
+
+ size_t read = (length < buffer_size) ? length : buffer_size;
+ mpack_read_bytes(reader, buffer, read);
+ if (mpack_reader_error(reader) != mpack_ok)
+ return 0;
+
+ mpack_skip_bytes(reader, length - read);
+ return read;
+}
+
+static void mpack_print_element(mpack_reader_t* reader, mpack_print_t* print, size_t depth) {
+ mpack_tag_t val = mpack_read_tag(reader);
+ if (mpack_reader_error(reader) != mpack_ok)
+ return;
+
+ // We read some bytes from bin and ext so we can print its prefix in hex.
+ char buffer[MPACK_PRINT_BYTE_COUNT];
+ size_t count = 0;
+ size_t i, j;
+
+ switch (val.type) {
+ case mpack_type_str:
+ mpack_print_append_cstr(print, "\"");
+ for (i = 0; i < val.v.l; ++i) {
+ char c;
+ mpack_read_bytes(reader, &c, 1);
+ if (mpack_reader_error(reader) != mpack_ok)
+ return;
+ switch (c) {
+ case '\n': mpack_print_append_cstr(print, "\\n"); break;
+ case '\\': mpack_print_append_cstr(print, "\\\\"); break;
+ case '"': mpack_print_append_cstr(print, "\\\""); break;
+ default: mpack_print_append(print, &c, 1); break;
+ }
+ }
+ mpack_print_append_cstr(print, "\"");
+ mpack_done_str(reader);
+ return;
+
+ case mpack_type_array:
+ mpack_print_append_cstr(print, "[\n");
+ for (i = 0; i < val.v.n; ++i) {
+ for (j = 0; j < depth + 1; ++j)
+ mpack_print_append_cstr(print, " ");
+ mpack_print_element(reader, print, depth + 1);
+ if (mpack_reader_error(reader) != mpack_ok)
+ return;
+ if (i != val.v.n - 1)
+ mpack_print_append_cstr(print, ",");
+ mpack_print_append_cstr(print, "\n");
+ }
+ for (i = 0; i < depth; ++i)
+ mpack_print_append_cstr(print, " ");
+ mpack_print_append_cstr(print, "]");
+ mpack_done_array(reader);
+ return;
+
+ case mpack_type_map:
+ mpack_print_append_cstr(print, "{\n");
+ for (i = 0; i < val.v.n; ++i) {
+ for (j = 0; j < depth + 1; ++j)
+ mpack_print_append_cstr(print, " ");
+ mpack_print_element(reader, print, depth + 1);
+ if (mpack_reader_error(reader) != mpack_ok)
+ return;
+ mpack_print_append_cstr(print, ": ");
+ mpack_print_element(reader, print, depth + 1);
+ if (mpack_reader_error(reader) != mpack_ok)
+ return;
+ if (i != val.v.n - 1)
+ mpack_print_append_cstr(print, ",");
+ mpack_print_append_cstr(print, "\n");
+ }
+ for (i = 0; i < depth; ++i)
+ mpack_print_append_cstr(print, " ");
+ mpack_print_append_cstr(print, "}");
+ mpack_done_map(reader);
+ return;
+
+ // The above cases return so as not to print a pseudo-json value. The
+ // below cases break and print pseudo-json.
+
+ case mpack_type_bin:
+ count = mpack_print_read_prefix(reader, mpack_tag_bin_length(&val), buffer, sizeof(buffer));
+ mpack_done_bin(reader);
+ break;
+
+ #if MPACK_EXTENSIONS
+ case mpack_type_ext:
+ count = mpack_print_read_prefix(reader, mpack_tag_ext_length(&val), buffer, sizeof(buffer));
+ mpack_done_ext(reader);
+ break;
+ #endif
+
+ default:
+ break;
+ }
+
+ char buf[256];
+ mpack_tag_debug_pseudo_json(val, buf, sizeof(buf), buffer, count);
+ mpack_print_append_cstr(print, buf);
+}
+
+static void mpack_print_and_destroy(mpack_reader_t* reader, mpack_print_t* print, size_t depth) {
+ size_t i;
+ for (i = 0; i < depth; ++i)
+ mpack_print_append_cstr(print, " ");
+ mpack_print_element(reader, print, depth);
+
+ size_t remaining = mpack_reader_remaining(reader, NULL);
+
+ char buf[256];
+ if (mpack_reader_destroy(reader) != mpack_ok) {
+ mpack_snprintf(buf, sizeof(buf), "\n<mpack parsing error %s>", mpack_error_to_string(mpack_reader_error(reader)));
+ buf[sizeof(buf) - 1] = '\0';
+ mpack_print_append_cstr(print, buf);
+ } else if (remaining > 0) {
+ mpack_snprintf(buf, sizeof(buf), "\n<%i extra bytes at end of message>", (int)remaining);
+ buf[sizeof(buf) - 1] = '\0';
+ mpack_print_append_cstr(print, buf);
+ }
+}
+
+static void mpack_print_data(const char* data, size_t len, mpack_print_t* print, size_t depth) {
+ mpack_reader_t reader;
+ mpack_reader_init_data(&reader, data, len);
+ mpack_print_and_destroy(&reader, print, depth);
+}
+
+void mpack_print_data_to_buffer(const char* data, size_t data_size, char* buffer, size_t buffer_size) {
+ if (buffer_size == 0) {
+ mpack_assert(false, "buffer size is zero!");
+ return;
+ }
+
+ mpack_print_t print;
+ mpack_memset(&print, 0, sizeof(print));
+ print.buffer = buffer;
+ print.size = buffer_size;
+ mpack_print_data(data, data_size, &print, 0);
+ mpack_print_append(&print, "", 1); // null-terminator
+ mpack_print_flush(&print);
+
+ // we always make sure there's a null-terminator at the end of the buffer
+ // in case we ran out of space.
+ print.buffer[print.size - 1] = '\0';
+}
+
+void mpack_print_data_to_callback(const char* data, size_t size, mpack_print_callback_t callback, void* context) {
+ char buffer[1024];
+ mpack_print_t print;
+ mpack_memset(&print, 0, sizeof(print));
+ print.buffer = buffer;
+ print.size = sizeof(buffer);
+ print.callback = callback;
+ print.context = context;
+ mpack_print_data(data, size, &print, 0);
+ mpack_print_flush(&print);
+}
+
+void mpack_print_data_to_file(const char* data, size_t len, FILE* file) {
+ mpack_assert(data != NULL, "data is NULL");
+ mpack_assert(file != NULL, "file is NULL");
+
+ char buffer[1024];
+ mpack_print_t print;
+ mpack_memset(&print, 0, sizeof(print));
+ print.buffer = buffer;
+ print.size = sizeof(buffer);
+ print.callback = &mpack_print_file_callback;
+ print.context = file;
+
+ mpack_print_data(data, len, &print, 2);
+ mpack_print_append_cstr(&print, "\n");
+ mpack_print_flush(&print);
+}
+
+void mpack_print_stdfile_to_callback(FILE* file, mpack_print_callback_t callback, void* context) {
+ char buffer[1024];
+ mpack_print_t print;
+ mpack_memset(&print, 0, sizeof(print));
+ print.buffer = buffer;
+ print.size = sizeof(buffer);
+ print.callback = callback;
+ print.context = context;
+
+ mpack_reader_t reader;
+ mpack_reader_init_stdfile(&reader, file, false);
+ mpack_print_and_destroy(&reader, &print, 0);
+ mpack_print_flush(&print);
+}
+#endif
+
+#endif
+
+MPACK_SILENCE_WARNINGS_END
+
+/* mpack/mpack-expect.c.c */
+
+#define MPACK_INTERNAL 1
+
+/* #include "mpack-expect.h" */
+
+MPACK_SILENCE_WARNINGS_BEGIN
+
+#if MPACK_EXPECT
+
+
+// Helpers
+
+MPACK_STATIC_INLINE uint8_t mpack_expect_native_u8(mpack_reader_t* reader) {
+ if (mpack_reader_error(reader) != mpack_ok)
+ return 0;
+ uint8_t type;
+ if (!mpack_reader_ensure(reader, sizeof(type)))
+ return 0;
+ type = mpack_load_u8(reader->data);
+ reader->data += sizeof(type);
+ return type;
+}
+
+#if !MPACK_OPTIMIZE_FOR_SIZE
+MPACK_STATIC_INLINE uint16_t mpack_expect_native_u16(mpack_reader_t* reader) {
+ if (mpack_reader_error(reader) != mpack_ok)
+ return 0;
+ uint16_t type;
+ if (!mpack_reader_ensure(reader, sizeof(type)))
+ return 0;
+ type = mpack_load_u16(reader->data);
+ reader->data += sizeof(type);
+ return type;
+}
+
+MPACK_STATIC_INLINE uint32_t mpack_expect_native_u32(mpack_reader_t* reader) {
+ if (mpack_reader_error(reader) != mpack_ok)
+ return 0;
+ uint32_t type;
+ if (!mpack_reader_ensure(reader, sizeof(type)))
+ return 0;
+ type = mpack_load_u32(reader->data);
+ reader->data += sizeof(type);
+ return type;
+}
+#endif
+
+MPACK_STATIC_INLINE uint8_t mpack_expect_type_byte(mpack_reader_t* reader) {
+ mpack_reader_track_element(reader);
+ return mpack_expect_native_u8(reader);
+}
+
+
+// Basic Number Functions
+
+uint8_t mpack_expect_u8(mpack_reader_t* reader) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_uint) {
+ if (var.v.u <= MPACK_UINT8_MAX)
+ return (uint8_t)var.v.u;
+ } else if (var.type == mpack_type_int) {
+ if (var.v.i >= 0 && var.v.i <= MPACK_UINT8_MAX)
+ return (uint8_t)var.v.i;
+ }
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+}
+
+uint16_t mpack_expect_u16(mpack_reader_t* reader) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_uint) {
+ if (var.v.u <= MPACK_UINT16_MAX)
+ return (uint16_t)var.v.u;
+ } else if (var.type == mpack_type_int) {
+ if (var.v.i >= 0 && var.v.i <= MPACK_UINT16_MAX)
+ return (uint16_t)var.v.i;
+ }
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+}
+
+uint32_t mpack_expect_u32(mpack_reader_t* reader) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_uint) {
+ if (var.v.u <= MPACK_UINT32_MAX)
+ return (uint32_t)var.v.u;
+ } else if (var.type == mpack_type_int) {
+ if (var.v.i >= 0 && var.v.i <= MPACK_UINT32_MAX)
+ return (uint32_t)var.v.i;
+ }
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+}
+
+uint64_t mpack_expect_u64(mpack_reader_t* reader) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_uint) {
+ return var.v.u;
+ } else if (var.type == mpack_type_int) {
+ if (var.v.i >= 0)
+ return (uint64_t)var.v.i;
+ }
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+}
+
+int8_t mpack_expect_i8(mpack_reader_t* reader) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_uint) {
+ if (var.v.u <= MPACK_INT8_MAX)
+ return (int8_t)var.v.u;
+ } else if (var.type == mpack_type_int) {
+ if (var.v.i >= MPACK_INT8_MIN && var.v.i <= MPACK_INT8_MAX)
+ return (int8_t)var.v.i;
+ }
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+}
+
+int16_t mpack_expect_i16(mpack_reader_t* reader) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_uint) {
+ if (var.v.u <= MPACK_INT16_MAX)
+ return (int16_t)var.v.u;
+ } else if (var.type == mpack_type_int) {
+ if (var.v.i >= MPACK_INT16_MIN && var.v.i <= MPACK_INT16_MAX)
+ return (int16_t)var.v.i;
+ }
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+}
+
+int32_t mpack_expect_i32(mpack_reader_t* reader) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_uint) {
+ if (var.v.u <= MPACK_INT32_MAX)
+ return (int32_t)var.v.u;
+ } else if (var.type == mpack_type_int) {
+ if (var.v.i >= MPACK_INT32_MIN && var.v.i <= MPACK_INT32_MAX)
+ return (int32_t)var.v.i;
+ }
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+}
+
+int64_t mpack_expect_i64(mpack_reader_t* reader) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_uint) {
+ if (var.v.u <= MPACK_INT64_MAX)
+ return (int64_t)var.v.u;
+ } else if (var.type == mpack_type_int) {
+ return var.v.i;
+ }
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+}
+
+#if MPACK_FLOAT
+float mpack_expect_float(mpack_reader_t* reader) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_uint)
+ return (float)var.v.u;
+ if (var.type == mpack_type_int)
+ return (float)var.v.i;
+ if (var.type == mpack_type_float)
+ return var.v.f;
+
+ if (var.type == mpack_type_double) {
+ #if MPACK_DOUBLE
+ return (float)var.v.d;
+ #else
+ return mpack_shorten_raw_double_to_float(var.v.d);
+ #endif
+ }
+
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0.0f;
+}
+#endif
+
+#if MPACK_DOUBLE
+double mpack_expect_double(mpack_reader_t* reader) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_uint)
+ return (double)var.v.u;
+ else if (var.type == mpack_type_int)
+ return (double)var.v.i;
+ else if (var.type == mpack_type_float)
+ return (double)var.v.f;
+ else if (var.type == mpack_type_double)
+ return var.v.d;
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0.0;
+}
+#endif
+
+#if MPACK_FLOAT
+float mpack_expect_float_strict(mpack_reader_t* reader) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_float)
+ return var.v.f;
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0.0f;
+}
+#endif
+
+#if MPACK_DOUBLE
+double mpack_expect_double_strict(mpack_reader_t* reader) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_float)
+ return (double)var.v.f;
+ else if (var.type == mpack_type_double)
+ return var.v.d;
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0.0;
+}
+#endif
+
+#if !MPACK_FLOAT
+uint32_t mpack_expect_raw_float(mpack_reader_t* reader) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_float)
+ return var.v.f;
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+}
+#endif
+
+#if !MPACK_DOUBLE
+uint64_t mpack_expect_raw_double(mpack_reader_t* reader) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_double)
+ return var.v.d;
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+}
+#endif
+
+
+// Ranged Number Functions
+//
+// All ranged functions are identical other than the type, so we
+// define their content with a macro. The prototypes are still written
+// out in full to support ctags/IDE tools.
+
+#define MPACK_EXPECT_RANGE_IMPL(name, type_t) \
+ \
+ /* make sure the range is sensible */ \
+ mpack_assert(min_value <= max_value, \
+ "min_value %i must be less than or equal to max_value %i", \
+ min_value, max_value); \
+ \
+ /* read the value */ \
+ type_t val = mpack_expect_##name(reader); \
+ if (mpack_reader_error(reader) != mpack_ok) \
+ return min_value; \
+ \
+ /* make sure it fits */ \
+ if (val < min_value || val > max_value) { \
+ mpack_reader_flag_error(reader, mpack_error_type); \
+ return min_value; \
+ } \
+ \
+ return val;
+
+uint8_t mpack_expect_u8_range(mpack_reader_t* reader, uint8_t min_value, uint8_t max_value) {MPACK_EXPECT_RANGE_IMPL(u8, uint8_t)}
+uint16_t mpack_expect_u16_range(mpack_reader_t* reader, uint16_t min_value, uint16_t max_value) {MPACK_EXPECT_RANGE_IMPL(u16, uint16_t)}
+uint32_t mpack_expect_u32_range(mpack_reader_t* reader, uint32_t min_value, uint32_t max_value) {MPACK_EXPECT_RANGE_IMPL(u32, uint32_t)}
+uint64_t mpack_expect_u64_range(mpack_reader_t* reader, uint64_t min_value, uint64_t max_value) {MPACK_EXPECT_RANGE_IMPL(u64, uint64_t)}
+
+int8_t mpack_expect_i8_range(mpack_reader_t* reader, int8_t min_value, int8_t max_value) {MPACK_EXPECT_RANGE_IMPL(i8, int8_t)}
+int16_t mpack_expect_i16_range(mpack_reader_t* reader, int16_t min_value, int16_t max_value) {MPACK_EXPECT_RANGE_IMPL(i16, int16_t)}
+int32_t mpack_expect_i32_range(mpack_reader_t* reader, int32_t min_value, int32_t max_value) {MPACK_EXPECT_RANGE_IMPL(i32, int32_t)}
+int64_t mpack_expect_i64_range(mpack_reader_t* reader, int64_t min_value, int64_t max_value) {MPACK_EXPECT_RANGE_IMPL(i64, int64_t)}
+
+#if MPACK_FLOAT
+float mpack_expect_float_range(mpack_reader_t* reader, float min_value, float max_value) {MPACK_EXPECT_RANGE_IMPL(float, float)}
+#endif
+#if MPACK_DOUBLE
+double mpack_expect_double_range(mpack_reader_t* reader, double min_value, double max_value) {MPACK_EXPECT_RANGE_IMPL(double, double)}
+#endif
+
+uint32_t mpack_expect_map_range(mpack_reader_t* reader, uint32_t min_value, uint32_t max_value) {MPACK_EXPECT_RANGE_IMPL(map, uint32_t)}
+uint32_t mpack_expect_array_range(mpack_reader_t* reader, uint32_t min_value, uint32_t max_value) {MPACK_EXPECT_RANGE_IMPL(array, uint32_t)}
+
+
+// Matching Number Functions
+
+void mpack_expect_uint_match(mpack_reader_t* reader, uint64_t value) {
+ if (mpack_expect_u64(reader) != value)
+ mpack_reader_flag_error(reader, mpack_error_type);
+}
+
+void mpack_expect_int_match(mpack_reader_t* reader, int64_t value) {
+ if (mpack_expect_i64(reader) != value)
+ mpack_reader_flag_error(reader, mpack_error_type);
+}
+
+
+// Other Basic Types
+
+void mpack_expect_nil(mpack_reader_t* reader) {
+ if (mpack_expect_type_byte(reader) != 0xc0)
+ mpack_reader_flag_error(reader, mpack_error_type);
+}
+
+bool mpack_expect_bool(mpack_reader_t* reader) {
+ uint8_t type = mpack_expect_type_byte(reader);
+ if ((type & ~1) != 0xc2)
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return (bool)(type & 1);
+}
+
+void mpack_expect_true(mpack_reader_t* reader) {
+ if (mpack_expect_bool(reader) != true)
+ mpack_reader_flag_error(reader, mpack_error_type);
+}
+
+void mpack_expect_false(mpack_reader_t* reader) {
+ if (mpack_expect_bool(reader) != false)
+ mpack_reader_flag_error(reader, mpack_error_type);
+}
+
+#if MPACK_EXTENSIONS
+mpack_timestamp_t mpack_expect_timestamp(mpack_reader_t* reader) {
+ mpack_timestamp_t zero = {0, 0};
+
+ mpack_tag_t tag = mpack_read_tag(reader);
+ if (tag.type != mpack_type_ext) {
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return zero;
+ }
+ if (mpack_tag_ext_exttype(&tag) != MPACK_EXTTYPE_TIMESTAMP) {
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return zero;
+ }
+
+ return mpack_read_timestamp(reader, mpack_tag_ext_length(&tag));
+}
+
+int64_t mpack_expect_timestamp_truncate(mpack_reader_t* reader) {
+ return mpack_expect_timestamp(reader).seconds;
+}
+#endif
+
+
+// Compound Types
+
+uint32_t mpack_expect_map(mpack_reader_t* reader) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_map)
+ return var.v.n;
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+}
+
+void mpack_expect_map_match(mpack_reader_t* reader, uint32_t count) {
+ if (mpack_expect_map(reader) != count)
+ mpack_reader_flag_error(reader, mpack_error_type);
+}
+
+bool mpack_expect_map_or_nil(mpack_reader_t* reader, uint32_t* count) {
+ mpack_assert(count != NULL, "count cannot be NULL");
+
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_nil) {
+ *count = 0;
+ return false;
+ }
+ if (var.type == mpack_type_map) {
+ *count = var.v.n;
+ return true;
+ }
+ mpack_reader_flag_error(reader, mpack_error_type);
+ *count = 0;
+ return false;
+}
+
+bool mpack_expect_map_max_or_nil(mpack_reader_t* reader, uint32_t max_count, uint32_t* count) {
+ mpack_assert(count != NULL, "count cannot be NULL");
+
+ bool has_map = mpack_expect_map_or_nil(reader, count);
+ if (has_map && *count > max_count) {
+ *count = 0;
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return false;
+ }
+ return has_map;
+}
+
+uint32_t mpack_expect_array(mpack_reader_t* reader) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_array)
+ return var.v.n;
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+}
+
+void mpack_expect_array_match(mpack_reader_t* reader, uint32_t count) {
+ if (mpack_expect_array(reader) != count)
+ mpack_reader_flag_error(reader, mpack_error_type);
+}
+
+bool mpack_expect_array_or_nil(mpack_reader_t* reader, uint32_t* count) {
+ mpack_assert(count != NULL, "count cannot be NULL");
+
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_nil) {
+ *count = 0;
+ return false;
+ }
+ if (var.type == mpack_type_array) {
+ *count = var.v.n;
+ return true;
+ }
+ mpack_reader_flag_error(reader, mpack_error_type);
+ *count = 0;
+ return false;
+}
+
+bool mpack_expect_array_max_or_nil(mpack_reader_t* reader, uint32_t max_count, uint32_t* count) {
+ mpack_assert(count != NULL, "count cannot be NULL");
+
+ bool has_array = mpack_expect_array_or_nil(reader, count);
+ if (has_array && *count > max_count) {
+ *count = 0;
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return false;
+ }
+ return has_array;
+}
+
+#ifdef MPACK_MALLOC
+void* mpack_expect_array_alloc_impl(mpack_reader_t* reader, size_t element_size, uint32_t max_count, uint32_t* out_count, bool allow_nil) {
+ mpack_assert(out_count != NULL, "out_count cannot be NULL");
+ *out_count = 0;
+
+ uint32_t count;
+ bool has_array = true;
+ if (allow_nil)
+ has_array = mpack_expect_array_max_or_nil(reader, max_count, &count);
+ else
+ count = mpack_expect_array_max(reader, max_count);
+ if (mpack_reader_error(reader))
+ return NULL;
+
+ // size 0 is not an error; we return NULL for no elements.
+ if (count == 0) {
+ // we call mpack_done_array() automatically ONLY if we are using
+ // the _or_nil variant. this is the only way to allow nil and empty
+ // to work the same way.
+ if (allow_nil && has_array)
+ mpack_done_array(reader);
+ return NULL;
+ }
+
+ void* p = MPACK_MALLOC(element_size * count);
+ if (p == NULL) {
+ mpack_reader_flag_error(reader, mpack_error_memory);
+ return NULL;
+ }
+
+ *out_count = count;
+ return p;
+}
+#endif
+
+
+// Str, Bin and Ext Functions
+
+uint32_t mpack_expect_str(mpack_reader_t* reader) {
+ #if MPACK_OPTIMIZE_FOR_SIZE
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_str)
+ return var.v.l;
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+ #else
+ uint8_t type = mpack_expect_type_byte(reader);
+ uint32_t count;
+
+ if ((type >> 5) == 5) {
+ count = type & (uint8_t)~0xe0;
+ } else if (type == 0xd9) {
+ count = mpack_expect_native_u8(reader);
+ } else if (type == 0xda) {
+ count = mpack_expect_native_u16(reader);
+ } else if (type == 0xdb) {
+ count = mpack_expect_native_u32(reader);
+ } else {
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+ }
+
+ #if MPACK_READ_TRACKING
+ mpack_reader_flag_if_error(reader, mpack_track_push(&reader->track, mpack_type_str, count));
+ #endif
+ return count;
+ #endif
+}
+
+size_t mpack_expect_str_buf(mpack_reader_t* reader, char* buf, size_t bufsize) {
+ mpack_assert(buf != NULL, "buf cannot be NULL");
+
+ size_t length = mpack_expect_str(reader);
+ if (mpack_reader_error(reader))
+ return 0;
+
+ if (length > bufsize) {
+ mpack_reader_flag_error(reader, mpack_error_too_big);
+ return 0;
+ }
+
+ mpack_read_bytes(reader, buf, length);
+ if (mpack_reader_error(reader))
+ return 0;
+
+ mpack_done_str(reader);
+ return length;
+}
+
+size_t mpack_expect_utf8(mpack_reader_t* reader, char* buf, size_t size) {
+ mpack_assert(buf != NULL, "buf cannot be NULL");
+
+ size_t length = mpack_expect_str_buf(reader, buf, size);
+
+ if (!mpack_utf8_check(buf, length)) {
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+ }
+
+ return length;
+}
+
+uint32_t mpack_expect_bin(mpack_reader_t* reader) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_bin)
+ return var.v.l;
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+}
+
+size_t mpack_expect_bin_buf(mpack_reader_t* reader, char* buf, size_t bufsize) {
+ mpack_assert(buf != NULL, "buf cannot be NULL");
+
+ size_t binsize = mpack_expect_bin(reader);
+ if (mpack_reader_error(reader))
+ return 0;
+ if (binsize > bufsize) {
+ mpack_reader_flag_error(reader, mpack_error_too_big);
+ return 0;
+ }
+ mpack_read_bytes(reader, buf, binsize);
+ if (mpack_reader_error(reader))
+ return 0;
+ mpack_done_bin(reader);
+ return binsize;
+}
+
+void mpack_expect_bin_size_buf(mpack_reader_t* reader, char* buf, uint32_t size) {
+ mpack_assert(buf != NULL, "buf cannot be NULL");
+ mpack_expect_bin_size(reader, size);
+ mpack_read_bytes(reader, buf, size);
+ mpack_done_bin(reader);
+}
+
+#if MPACK_EXTENSIONS
+uint32_t mpack_expect_ext(mpack_reader_t* reader, int8_t* type) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_ext) {
+ *type = mpack_tag_ext_exttype(&var);
+ return mpack_tag_ext_length(&var);
+ }
+ *type = 0;
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+}
+
+size_t mpack_expect_ext_buf(mpack_reader_t* reader, int8_t* type, char* buf, size_t bufsize) {
+ mpack_assert(buf != NULL, "buf cannot be NULL");
+
+ size_t extsize = mpack_expect_ext(reader, type);
+ if (mpack_reader_error(reader))
+ return 0;
+ if (extsize > bufsize) {
+ *type = 0;
+ mpack_reader_flag_error(reader, mpack_error_too_big);
+ return 0;
+ }
+ mpack_read_bytes(reader, buf, extsize);
+ if (mpack_reader_error(reader)) {
+ *type = 0;
+ return 0;
+ }
+ mpack_done_ext(reader);
+ return extsize;
+}
+#endif
+
+void mpack_expect_cstr(mpack_reader_t* reader, char* buf, size_t bufsize) {
+ uint32_t length = mpack_expect_str(reader);
+ mpack_read_cstr(reader, buf, bufsize, length);
+ mpack_done_str(reader);
+}
+
+void mpack_expect_utf8_cstr(mpack_reader_t* reader, char* buf, size_t bufsize) {
+ uint32_t length = mpack_expect_str(reader);
+ mpack_read_utf8_cstr(reader, buf, bufsize, length);
+ mpack_done_str(reader);
+}
+
+#ifdef MPACK_MALLOC
+static char* mpack_expect_cstr_alloc_unchecked(mpack_reader_t* reader, size_t maxsize, size_t* out_length) {
+ mpack_assert(out_length != NULL, "out_length cannot be NULL");
+ *out_length = 0;
+
+ // make sure argument makes sense
+ if (maxsize < 1) {
+ mpack_break("maxsize is zero; you must have room for at least a null-terminator");
+ mpack_reader_flag_error(reader, mpack_error_bug);
+ return NULL;
+ }
+
+ if (SIZE_MAX < MPACK_UINT32_MAX) {
+ if (maxsize > SIZE_MAX)
+ maxsize = SIZE_MAX;
+ } else {
+ if (maxsize > (size_t)MPACK_UINT32_MAX)
+ maxsize = (size_t)MPACK_UINT32_MAX;
+ }
+
+ size_t length = mpack_expect_str_max(reader, (uint32_t)maxsize - 1);
+ char* str = mpack_read_bytes_alloc_impl(reader, length, true);
+ mpack_done_str(reader);
+
+ if (str)
+ *out_length = length;
+ return str;
+}
+
+char* mpack_expect_cstr_alloc(mpack_reader_t* reader, size_t maxsize) {
+ size_t length;
+ char* str = mpack_expect_cstr_alloc_unchecked(reader, maxsize, &length);
+
+ if (str && !mpack_str_check_no_null(str, length)) {
+ MPACK_FREE(str);
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return NULL;
+ }
+
+ return str;
+}
+
+char* mpack_expect_utf8_cstr_alloc(mpack_reader_t* reader, size_t maxsize) {
+ size_t length;
+ char* str = mpack_expect_cstr_alloc_unchecked(reader, maxsize, &length);
+
+ if (str && !mpack_utf8_check_no_null(str, length)) {
+ MPACK_FREE(str);
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return NULL;
+ }
+
+ return str;
+}
+#endif
+
+void mpack_expect_str_match(mpack_reader_t* reader, const char* str, size_t len) {
+ mpack_assert(str != NULL, "str cannot be NULL");
+
+ // expect a str the correct length
+ if (len > MPACK_UINT32_MAX)
+ mpack_reader_flag_error(reader, mpack_error_type);
+ mpack_expect_str_length(reader, (uint32_t)len);
+ if (mpack_reader_error(reader))
+ return;
+ mpack_reader_track_bytes(reader, (uint32_t)len);
+
+ // check each byte one by one (matched strings are likely to be very small)
+ for (; len > 0; --len) {
+ if (mpack_expect_native_u8(reader) != *str++) {
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return;
+ }
+ }
+
+ mpack_done_str(reader);
+}
+
+void mpack_expect_tag(mpack_reader_t* reader, mpack_tag_t expected) {
+ mpack_tag_t actual = mpack_read_tag(reader);
+ if (!mpack_tag_equal(actual, expected))
+ mpack_reader_flag_error(reader, mpack_error_type);
+}
+
+#ifdef MPACK_MALLOC
+char* mpack_expect_bin_alloc(mpack_reader_t* reader, size_t maxsize, size_t* size) {
+ mpack_assert(size != NULL, "size cannot be NULL");
+ *size = 0;
+
+ if (SIZE_MAX < MPACK_UINT32_MAX) {
+ if (maxsize > SIZE_MAX)
+ maxsize = SIZE_MAX;
+ } else {
+ if (maxsize > (size_t)MPACK_UINT32_MAX)
+ maxsize = (size_t)MPACK_UINT32_MAX;
+ }
+
+ size_t length = mpack_expect_bin_max(reader, (uint32_t)maxsize);
+ if (mpack_reader_error(reader))
+ return NULL;
+
+ char* data = mpack_read_bytes_alloc(reader, length);
+ mpack_done_bin(reader);
+
+ if (data)
+ *size = length;
+ return data;
+}
+#endif
+
+#if MPACK_EXTENSIONS && defined(MPACK_MALLOC)
+char* mpack_expect_ext_alloc(mpack_reader_t* reader, int8_t* type, size_t maxsize, size_t* size) {
+ mpack_assert(size != NULL, "size cannot be NULL");
+ *size = 0;
+
+ if (SIZE_MAX < MPACK_UINT32_MAX) {
+ if (maxsize > SIZE_MAX)
+ maxsize = SIZE_MAX;
+ } else {
+ if (maxsize > (size_t)MPACK_UINT32_MAX)
+ maxsize = (size_t)MPACK_UINT32_MAX;
+ }
+
+ size_t length = mpack_expect_ext_max(reader, type, (uint32_t)maxsize);
+ if (mpack_reader_error(reader))
+ return NULL;
+
+ char* data = mpack_read_bytes_alloc(reader, length);
+ mpack_done_ext(reader);
+
+ if (data) {
+ *size = length;
+ } else {
+ *type = 0;
+ }
+ return data;
+}
+#endif
+
+size_t mpack_expect_enum(mpack_reader_t* reader, const char* strings[], size_t count) {
+
+ // read the string in-place
+ size_t keylen = mpack_expect_str(reader);
+ const char* key = mpack_read_bytes_inplace(reader, keylen);
+ mpack_done_str(reader);
+ if (mpack_reader_error(reader) != mpack_ok)
+ return count;
+
+ // find what key it matches
+ size_t i;
+ for (i = 0; i < count; ++i) {
+ const char* other = strings[i];
+ size_t otherlen = mpack_strlen(other);
+ if (keylen == otherlen && mpack_memcmp(key, other, keylen) == 0)
+ return i;
+ }
+
+ // no matches
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return count;
+}
+
+size_t mpack_expect_enum_optional(mpack_reader_t* reader, const char* strings[], size_t count) {
+ if (mpack_reader_error(reader) != mpack_ok)
+ return count;
+
+ mpack_assert(count != 0, "count cannot be zero; no strings are valid!");
+ mpack_assert(strings != NULL, "strings cannot be NULL");
+
+ // the key is only recognized if it is a string
+ if (mpack_peek_tag(reader).type != mpack_type_str) {
+ mpack_discard(reader);
+ return count;
+ }
+
+ // read the string in-place
+ size_t keylen = mpack_expect_str(reader);
+ const char* key = mpack_read_bytes_inplace(reader, keylen);
+ mpack_done_str(reader);
+ if (mpack_reader_error(reader) != mpack_ok)
+ return count;
+
+ // find what key it matches
+ size_t i;
+ for (i = 0; i < count; ++i) {
+ const char* other = strings[i];
+ size_t otherlen = mpack_strlen(other);
+ if (keylen == otherlen && mpack_memcmp(key, other, keylen) == 0)
+ return i;
+ }
+
+ // no matches
+ return count;
+}
+
+size_t mpack_expect_key_uint(mpack_reader_t* reader, bool found[], size_t count) {
+ if (mpack_reader_error(reader) != mpack_ok)
+ return count;
+
+ if (count == 0) {
+ mpack_break("count cannot be zero; no keys are valid!");
+ mpack_reader_flag_error(reader, mpack_error_bug);
+ return count;
+ }
+ mpack_assert(found != NULL, "found cannot be NULL");
+
+ // the key is only recognized if it is an unsigned int
+ if (mpack_peek_tag(reader).type != mpack_type_uint) {
+ mpack_discard(reader);
+ return count;
+ }
+
+ // read the key
+ uint64_t value = mpack_expect_u64(reader);
+ if (mpack_reader_error(reader) != mpack_ok)
+ return count;
+
+ // unrecognized keys are fine, we just return count
+ if (value >= count)
+ return count;
+
+ // check if this key is a duplicate
+ if (found[value]) {
+ mpack_reader_flag_error(reader, mpack_error_invalid);
+ return count;
+ }
+
+ found[value] = true;
+ return (size_t)value;
+}
+
+size_t mpack_expect_key_cstr(mpack_reader_t* reader, const char* keys[], bool found[], size_t count) {
+ size_t i = mpack_expect_enum_optional(reader, keys, count);
+
+ // unrecognized keys are fine, we just return count
+ if (i == count)
+ return count;
+
+ // check if this key is a duplicate
+ mpack_assert(found != NULL, "found cannot be NULL");
+ if (found[i]) {
+ mpack_reader_flag_error(reader, mpack_error_invalid);
+ return count;
+ }
+
+ found[i] = true;
+ return i;
+}
+
+#endif
+
+MPACK_SILENCE_WARNINGS_END
+
+/* mpack/mpack-node.c.c */
+
+#define MPACK_INTERNAL 1
+
+/* #include "mpack-node.h" */
+
+MPACK_SILENCE_WARNINGS_BEGIN
+
+#if MPACK_NODE
+
+MPACK_STATIC_INLINE const char* mpack_node_data_unchecked(mpack_node_t node) {
+ mpack_assert(mpack_node_error(node) == mpack_ok, "tree is in an error state!");
+
+ mpack_type_t type = node.data->type;
+ MPACK_UNUSED(type);
+ #if MPACK_EXTENSIONS
+ mpack_assert(type == mpack_type_str || type == mpack_type_bin || type == mpack_type_ext,
+ "node of type %i (%s) is not a data type!", type, mpack_type_to_string(type));
+ #else
+ mpack_assert(type == mpack_type_str || type == mpack_type_bin,
+ "node of type %i (%s) is not a data type!", type, mpack_type_to_string(type));
+ #endif
+
+ return node.tree->data + node.data->value.offset;
+}
+
+#if MPACK_EXTENSIONS
+MPACK_STATIC_INLINE int8_t mpack_node_exttype_unchecked(mpack_node_t node) {
+ mpack_assert(mpack_node_error(node) == mpack_ok, "tree is in an error state!");
+
+ mpack_type_t type = node.data->type;
+ MPACK_UNUSED(type);
+ mpack_assert(type == mpack_type_ext, "node of type %i (%s) is not an ext type!",
+ type, mpack_type_to_string(type));
+
+ // the exttype of an ext node is stored in the byte preceding the data
+ return mpack_load_i8(mpack_node_data_unchecked(node) - 1);
+}
+#endif
+
+
+
+/*
+ * Tree Parsing
+ */
+
+#ifdef MPACK_MALLOC
+
+// fix up the alloc size to make sure it exactly fits the
+// maximum number of nodes it can contain (the allocator will
+// waste it back anyway, but we round it down just in case)
+
+#define MPACK_NODES_PER_PAGE \
+ ((MPACK_NODE_PAGE_SIZE - sizeof(mpack_tree_page_t)) / sizeof(mpack_node_data_t) + 1)
+
+#define MPACK_PAGE_ALLOC_SIZE \
+ (sizeof(mpack_tree_page_t) + sizeof(mpack_node_data_t) * (MPACK_NODES_PER_PAGE - 1))
+
+#endif
+
+#ifdef MPACK_MALLOC
+/*
+ * Fills the tree until we have at least enough bytes for the current node.
+ */
+static bool mpack_tree_reserve_fill(mpack_tree_t* tree) {
+ mpack_assert(tree->parser.state == mpack_tree_parse_state_in_progress);
+
+ size_t bytes = tree->parser.current_node_reserved;
+ mpack_assert(bytes > tree->parser.possible_nodes_left,
+ "there are already enough bytes! call mpack_tree_ensure() instead.");
+ mpack_log("filling to reserve %i bytes\n", (int)bytes);
+
+ // if the necessary bytes would put us over the maximum tree
+ // size, fail right away.
+ // TODO: check for overflow?
+ if (tree->data_length + bytes > tree->max_size) {
+ mpack_tree_flag_error(tree, mpack_error_too_big);
+ return false;
+ }
+
+ // we'll need a read function to fetch more data. if there's
+ // no read function, the data should contain an entire message
+ // (or messages), so we flag it as invalid.
+ if (tree->read_fn == NULL) {
+ mpack_log("tree has no read function!\n");
+ mpack_tree_flag_error(tree, mpack_error_invalid);
+ return false;
+ }
+
+ // expand the buffer if needed
+ if (tree->data_length + bytes > tree->buffer_capacity) {
+
+ // TODO: check for overflow?
+ size_t new_capacity = (tree->buffer_capacity == 0) ? MPACK_BUFFER_SIZE : tree->buffer_capacity;
+ while (new_capacity < tree->data_length + bytes)
+ new_capacity *= 2;
+ if (new_capacity > tree->max_size)
+ new_capacity = tree->max_size;
+
+ mpack_log("expanding buffer from %i to %i\n", (int)tree->buffer_capacity, (int)new_capacity);
+
+ char* new_buffer;
+ if (tree->buffer == NULL)
+ new_buffer = (char*)MPACK_MALLOC(new_capacity);
+ else
+ new_buffer = (char*)mpack_realloc(tree->buffer, tree->data_length, new_capacity);
+
+ if (new_buffer == NULL) {
+ mpack_tree_flag_error(tree, mpack_error_memory);
+ return false;
+ }
+
+ tree->data = new_buffer;
+ tree->buffer = new_buffer;
+ tree->buffer_capacity = new_capacity;
+ }
+
+ // request as much data as possible, looping until we have
+ // all the data we need
+ do {
+ size_t read = tree->read_fn(tree, tree->buffer + tree->data_length, tree->buffer_capacity - tree->data_length);
+
+ // If the fill function encounters an error, it should flag an error on
+ // the tree.
+ if (mpack_tree_error(tree) != mpack_ok)
+ return false;
+
+ // We guard against fill functions that return -1 just in case.
+ if (read == (size_t)(-1)) {
+ mpack_tree_flag_error(tree, mpack_error_io);
+ return false;
+ }
+
+ // If the fill function returns 0, the data is not available yet. We
+ // return false to stop parsing the current node.
+ if (read == 0) {
+ mpack_log("not enough data.\n");
+ return false;
+ }
+
+ mpack_log("read %u more bytes\n", (uint32_t)read);
+ tree->data_length += read;
+ tree->parser.possible_nodes_left += read;
+ } while (tree->parser.possible_nodes_left < bytes);
+
+ return true;
+}
+#endif
+
+/*
+ * Ensures there are enough additional bytes in the tree for the current node
+ * (including reserved bytes for the children of this node, and in addition to
+ * the reserved bytes for children of previous compound nodes), reading more
+ * data if needed.
+ *
+ * extra_bytes is the number of additional bytes to reserve for the current
+ * node beyond the type byte (since one byte is already reserved for each node
+ * by its parent array or map.)
+ *
+ * This may reallocate the tree, which means the tree->data pointer may change!
+ *
+ * Returns false if not enough bytes could be read.
+ */
+MPACK_STATIC_INLINE bool mpack_tree_reserve_bytes(mpack_tree_t* tree, size_t extra_bytes) {
+ mpack_assert(tree->parser.state == mpack_tree_parse_state_in_progress);
+
+ // We guard against overflow here. A compound type could declare more than
+ // MPACK_UINT32_MAX contents which overflows SIZE_MAX on 32-bit platforms. We
+ // flag mpack_error_invalid instead of mpack_error_too_big since it's far
+ // more likely that the message is corrupt than that the data is valid but
+ // not parseable on this architecture (see test_read_node_possible() in
+ // test-node.c .)
+ if ((uint64_t)tree->parser.current_node_reserved + (uint64_t)extra_bytes > SIZE_MAX) {
+ mpack_tree_flag_error(tree, mpack_error_invalid);
+ return false;
+ }
+
+ tree->parser.current_node_reserved += extra_bytes;
+
+ // Note that possible_nodes_left already accounts for reserved bytes for
+ // children of previous compound nodes. So even if there are hundreds of
+ // bytes left in the buffer, we might need to read anyway.
+ if (tree->parser.current_node_reserved <= tree->parser.possible_nodes_left)
+ return true;
+
+ #ifdef MPACK_MALLOC
+ return mpack_tree_reserve_fill(tree);
+ #else
+ return false;
+ #endif
+}
+
+MPACK_STATIC_INLINE size_t mpack_tree_parser_stack_capacity(mpack_tree_t* tree) {
+ #ifdef MPACK_MALLOC
+ return tree->parser.stack_capacity;
+ #else
+ return sizeof(tree->parser.stack) / sizeof(tree->parser.stack[0]);
+ #endif
+}
+
+static bool mpack_tree_push_stack(mpack_tree_t* tree, mpack_node_data_t* first_child, size_t total) {
+ mpack_tree_parser_t* parser = &tree->parser;
+ mpack_assert(parser->state == mpack_tree_parse_state_in_progress);
+
+ // No need to push empty containers
+ if (total == 0)
+ return true;
+
+ // Make sure we have enough room in the stack
+ if (parser->level + 1 == mpack_tree_parser_stack_capacity(tree)) {
+ #ifdef MPACK_MALLOC
+ size_t new_capacity = parser->stack_capacity * 2;
+ mpack_log("growing parse stack to capacity %i\n", (int)new_capacity);
+
+ // Replace the stack-allocated parsing stack
+ if (!parser->stack_owned) {
+ mpack_level_t* new_stack = (mpack_level_t*)MPACK_MALLOC(sizeof(mpack_level_t) * new_capacity);
+ if (!new_stack) {
+ mpack_tree_flag_error(tree, mpack_error_memory);
+ return false;
+ }
+ mpack_memcpy(new_stack, parser->stack, sizeof(mpack_level_t) * parser->stack_capacity);
+ parser->stack = new_stack;
+ parser->stack_owned = true;
+
+ // Realloc the allocated parsing stack
+ } else {
+ mpack_level_t* new_stack = (mpack_level_t*)mpack_realloc(parser->stack,
+ sizeof(mpack_level_t) * parser->stack_capacity, sizeof(mpack_level_t) * new_capacity);
+ if (!new_stack) {
+ mpack_tree_flag_error(tree, mpack_error_memory);
+ return false;
+ }
+ parser->stack = new_stack;
+ }
+ parser->stack_capacity = new_capacity;
+ #else
+ mpack_tree_flag_error(tree, mpack_error_too_big);
+ return false;
+ #endif
+ }
+
+ // Push the contents of this node onto the parsing stack
+ ++parser->level;
+ parser->stack[parser->level].child = first_child;
+ parser->stack[parser->level].left = total;
+ return true;
+}
+
+static bool mpack_tree_parse_children(mpack_tree_t* tree, mpack_node_data_t* node) {
+ mpack_tree_parser_t* parser = &tree->parser;
+ mpack_assert(parser->state == mpack_tree_parse_state_in_progress);
+
+ mpack_type_t type = node->type;
+ size_t total = node->len;
+
+ // Calculate total elements to read
+ if (type == mpack_type_map) {
+ if ((uint64_t)total * 2 > SIZE_MAX) {
+ mpack_tree_flag_error(tree, mpack_error_too_big);
+ return false;
+ }
+ total *= 2;
+ }
+
+ // Make sure we are under our total node limit (TODO can this overflow?)
+ tree->node_count += total;
+ if (tree->node_count > tree->max_nodes) {
+ mpack_tree_flag_error(tree, mpack_error_too_big);
+ return false;
+ }
+
+ // Each node is at least one byte. Count these bytes now to make
+ // sure there is enough data left.
+ if (!mpack_tree_reserve_bytes(tree, total))
+ return false;
+
+ // If there are enough nodes left in the current page, no need to grow
+ if (total <= parser->nodes_left) {
+ node->value.children = parser->nodes;
+ parser->nodes += total;
+ parser->nodes_left -= total;
+
+ } else {
+
+ #ifdef MPACK_MALLOC
+
+ // We can't grow if we're using a fixed pool (i.e. we didn't start with a page)
+ if (!tree->next) {
+ mpack_tree_flag_error(tree, mpack_error_too_big);
+ return false;
+ }
+
+ // Otherwise we need to grow, and the node's children need to be contiguous.
+ // This is a heuristic to decide whether we should waste the remaining space
+ // in the current page and start a new one, or give the children their
+ // own page. With a fraction of 1/8, this causes at most 12% additional
+ // waste. Note that reducing this too much causes less cache coherence and
+ // more malloc() overhead due to smaller allocations, so there's a tradeoff
+ // here. This heuristic could use some improvement, especially with custom
+ // page sizes.
+
+ mpack_tree_page_t* page;
+
+ if (total > MPACK_NODES_PER_PAGE || parser->nodes_left > MPACK_NODES_PER_PAGE / 8) {
+ // TODO: this should check for overflow
+ page = (mpack_tree_page_t*)MPACK_MALLOC(
+ sizeof(mpack_tree_page_t) + sizeof(mpack_node_data_t) * (total - 1));
+ if (page == NULL) {
+ mpack_tree_flag_error(tree, mpack_error_memory);
+ return false;
+ }
+ mpack_log("allocated seperate page %p for %i children, %i left in page of %i total\n",
+ (void*)page, (int)total, (int)parser->nodes_left, (int)MPACK_NODES_PER_PAGE);
+
+ node->value.children = page->nodes;
+
+ } else {
+ page = (mpack_tree_page_t*)MPACK_MALLOC(MPACK_PAGE_ALLOC_SIZE);
+ if (page == NULL) {
+ mpack_tree_flag_error(tree, mpack_error_memory);
+ return false;
+ }
+ mpack_log("allocated new page %p for %i children, wasting %i in page of %i total\n",
+ (void*)page, (int)total, (int)parser->nodes_left, (int)MPACK_NODES_PER_PAGE);
+
+ node->value.children = page->nodes;
+ parser->nodes = page->nodes + total;
+ parser->nodes_left = MPACK_NODES_PER_PAGE - total;
+ }
+
+ page->next = tree->next;
+ tree->next = page;
+
+ #else
+ // We can't grow if we don't have an allocator
+ mpack_tree_flag_error(tree, mpack_error_too_big);
+ return false;
+ #endif
+ }
+
+ return mpack_tree_push_stack(tree, node->value.children, total);
+}
+
+static bool mpack_tree_parse_bytes(mpack_tree_t* tree, mpack_node_data_t* node) {
+ node->value.offset = tree->size + tree->parser.current_node_reserved + 1;
+ return mpack_tree_reserve_bytes(tree, node->len);
+}
+
+#if MPACK_EXTENSIONS
+static bool mpack_tree_parse_ext(mpack_tree_t* tree, mpack_node_data_t* node) {
+ // reserve space for exttype
+ tree->parser.current_node_reserved += sizeof(int8_t);
+ node->type = mpack_type_ext;
+ return mpack_tree_parse_bytes(tree, node);
+}
+#endif
+
+static bool mpack_tree_parse_node_contents(mpack_tree_t* tree, mpack_node_data_t* node) {
+ mpack_assert(tree->parser.state == mpack_tree_parse_state_in_progress);
+ mpack_assert(node != NULL, "null node?");
+
+ // read the type. we've already accounted for this byte in
+ // possible_nodes_left, so we already know it is in bounds, and we don't
+ // need to reserve it for this node.
+ mpack_assert(tree->data_length > tree->size);
+ uint8_t type = mpack_load_u8(tree->data + tree->size);
+ mpack_log("node type %x\n", type);
+ tree->parser.current_node_reserved = 0;
+
+ // as with mpack_read_tag(), the fastest way to parse a node is to switch
+ // on the first byte, and to explicitly list every possible byte. we switch
+ // on the first four bits in size-optimized builds.
+
+ #if MPACK_OPTIMIZE_FOR_SIZE
+ switch (type >> 4) {
+
+ // positive fixnum
+ case 0x0: case 0x1: case 0x2: case 0x3:
+ case 0x4: case 0x5: case 0x6: case 0x7:
+ node->type = mpack_type_uint;
+ node->value.u = type;
+ return true;
+
+ // negative fixnum
+ case 0xe: case 0xf:
+ node->type = mpack_type_int;
+ node->value.i = (int8_t)type;
+ return true;
+
+ // fixmap
+ case 0x8:
+ node->type = mpack_type_map;
+ node->len = (uint32_t)(type & ~0xf0);
+ return mpack_tree_parse_children(tree, node);
+
+ // fixarray
+ case 0x9:
+ node->type = mpack_type_array;
+ node->len = (uint32_t)(type & ~0xf0);
+ return mpack_tree_parse_children(tree, node);
+
+ // fixstr
+ case 0xa: case 0xb:
+ node->type = mpack_type_str;
+ node->len = (uint32_t)(type & ~0xe0);
+ return mpack_tree_parse_bytes(tree, node);
+
+ // not one of the common infix types
+ default:
+ break;
+ }
+ #endif
+
+ switch (type) {
+
+ #if !MPACK_OPTIMIZE_FOR_SIZE
+ // positive fixnum
+ 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:
+ node->type = mpack_type_uint;
+ node->value.u = type;
+ return true;
+
+ // negative fixnum
+ 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:
+ node->type = mpack_type_int;
+ node->value.i = (int8_t)type;
+ return true;
+
+ // 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:
+ node->type = mpack_type_map;
+ node->len = (uint32_t)(type & ~0xf0);
+ return mpack_tree_parse_children(tree, node);
+
+ // 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:
+ node->type = mpack_type_array;
+ node->len = (uint32_t)(type & ~0xf0);
+ return mpack_tree_parse_children(tree, node);
+
+ // 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:
+ node->type = mpack_type_str;
+ node->len = (uint32_t)(type & ~0xe0);
+ return mpack_tree_parse_bytes(tree, node);
+ #endif
+
+ // nil
+ case 0xc0:
+ node->type = mpack_type_nil;
+ return true;
+
+ // bool
+ case 0xc2: case 0xc3:
+ node->type = mpack_type_bool;
+ node->value.b = type & 1;
+ return true;
+
+ // bin8
+ case 0xc4:
+ node->type = mpack_type_bin;
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint8_t)))
+ return false;
+ node->len = mpack_load_u8(tree->data + tree->size + 1);
+ return mpack_tree_parse_bytes(tree, node);
+
+ // bin16
+ case 0xc5:
+ node->type = mpack_type_bin;
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint16_t)))
+ return false;
+ node->len = mpack_load_u16(tree->data + tree->size + 1);
+ return mpack_tree_parse_bytes(tree, node);
+
+ // bin32
+ case 0xc6:
+ node->type = mpack_type_bin;
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint32_t)))
+ return false;
+ node->len = mpack_load_u32(tree->data + tree->size + 1);
+ return mpack_tree_parse_bytes(tree, node);
+
+ #if MPACK_EXTENSIONS
+ // ext8
+ case 0xc7:
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint8_t)))
+ return false;
+ node->len = mpack_load_u8(tree->data + tree->size + 1);
+ return mpack_tree_parse_ext(tree, node);
+
+ // ext16
+ case 0xc8:
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint16_t)))
+ return false;
+ node->len = mpack_load_u16(tree->data + tree->size + 1);
+ return mpack_tree_parse_ext(tree, node);
+
+ // ext32
+ case 0xc9:
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint32_t)))
+ return false;
+ node->len = mpack_load_u32(tree->data + tree->size + 1);
+ return mpack_tree_parse_ext(tree, node);
+ #endif
+
+ // float
+ case 0xca:
+ #if MPACK_FLOAT
+ if (!mpack_tree_reserve_bytes(tree, sizeof(float)))
+ return false;
+ node->value.f = mpack_load_float(tree->data + tree->size + 1);
+ #else
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint32_t)))
+ return false;
+ node->value.f = mpack_load_u32(tree->data + tree->size + 1);
+ #endif
+ node->type = mpack_type_float;
+ return true;
+
+ // double
+ case 0xcb:
+ #if MPACK_DOUBLE
+ if (!mpack_tree_reserve_bytes(tree, sizeof(double)))
+ return false;
+ node->value.d = mpack_load_double(tree->data + tree->size + 1);
+ #else
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint64_t)))
+ return false;
+ node->value.d = mpack_load_u64(tree->data + tree->size + 1);
+ #endif
+ node->type = mpack_type_double;
+ return true;
+
+ // uint8
+ case 0xcc:
+ node->type = mpack_type_uint;
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint8_t)))
+ return false;
+ node->value.u = mpack_load_u8(tree->data + tree->size + 1);
+ return true;
+
+ // uint16
+ case 0xcd:
+ node->type = mpack_type_uint;
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint16_t)))
+ return false;
+ node->value.u = mpack_load_u16(tree->data + tree->size + 1);
+ return true;
+
+ // uint32
+ case 0xce:
+ node->type = mpack_type_uint;
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint32_t)))
+ return false;
+ node->value.u = mpack_load_u32(tree->data + tree->size + 1);
+ return true;
+
+ // uint64
+ case 0xcf:
+ node->type = mpack_type_uint;
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint64_t)))
+ return false;
+ node->value.u = mpack_load_u64(tree->data + tree->size + 1);
+ return true;
+
+ // int8
+ case 0xd0:
+ node->type = mpack_type_int;
+ if (!mpack_tree_reserve_bytes(tree, sizeof(int8_t)))
+ return false;
+ node->value.i = mpack_load_i8(tree->data + tree->size + 1);
+ return true;
+
+ // int16
+ case 0xd1:
+ node->type = mpack_type_int;
+ if (!mpack_tree_reserve_bytes(tree, sizeof(int16_t)))
+ return false;
+ node->value.i = mpack_load_i16(tree->data + tree->size + 1);
+ return true;
+
+ // int32
+ case 0xd2:
+ node->type = mpack_type_int;
+ if (!mpack_tree_reserve_bytes(tree, sizeof(int32_t)))
+ return false;
+ node->value.i = mpack_load_i32(tree->data + tree->size + 1);
+ return true;
+
+ // int64
+ case 0xd3:
+ node->type = mpack_type_int;
+ if (!mpack_tree_reserve_bytes(tree, sizeof(int64_t)))
+ return false;
+ node->value.i = mpack_load_i64(tree->data + tree->size + 1);
+ return true;
+
+ #if MPACK_EXTENSIONS
+ // fixext1
+ case 0xd4:
+ node->len = 1;
+ return mpack_tree_parse_ext(tree, node);
+
+ // fixext2
+ case 0xd5:
+ node->len = 2;
+ return mpack_tree_parse_ext(tree, node);
+
+ // fixext4
+ case 0xd6:
+ node->len = 4;
+ return mpack_tree_parse_ext(tree, node);
+
+ // fixext8
+ case 0xd7:
+ node->len = 8;
+ return mpack_tree_parse_ext(tree, node);
+
+ // fixext16
+ case 0xd8:
+ node->len = 16;
+ return mpack_tree_parse_ext(tree, node);
+ #endif
+
+ // str8
+ case 0xd9:
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint8_t)))
+ return false;
+ node->len = mpack_load_u8(tree->data + tree->size + 1);
+ node->type = mpack_type_str;
+ return mpack_tree_parse_bytes(tree, node);
+
+ // str16
+ case 0xda:
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint16_t)))
+ return false;
+ node->len = mpack_load_u16(tree->data + tree->size + 1);
+ node->type = mpack_type_str;
+ return mpack_tree_parse_bytes(tree, node);
+
+ // str32
+ case 0xdb:
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint32_t)))
+ return false;
+ node->len = mpack_load_u32(tree->data + tree->size + 1);
+ node->type = mpack_type_str;
+ return mpack_tree_parse_bytes(tree, node);
+
+ // array16
+ case 0xdc:
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint16_t)))
+ return false;
+ node->len = mpack_load_u16(tree->data + tree->size + 1);
+ node->type = mpack_type_array;
+ return mpack_tree_parse_children(tree, node);
+
+ // array32
+ case 0xdd:
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint32_t)))
+ return false;
+ node->len = mpack_load_u32(tree->data + tree->size + 1);
+ node->type = mpack_type_array;
+ return mpack_tree_parse_children(tree, node);
+
+ // map16
+ case 0xde:
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint16_t)))
+ return false;
+ node->len = mpack_load_u16(tree->data + tree->size + 1);
+ node->type = mpack_type_map;
+ return mpack_tree_parse_children(tree, node);
+
+ // map32
+ case 0xdf:
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint32_t)))
+ return false;
+ node->len = mpack_load_u32(tree->data + tree->size + 1);
+ node->type = mpack_type_map;
+ return mpack_tree_parse_children(tree, node);
+
+ // reserved
+ case 0xc1:
+ mpack_tree_flag_error(tree, mpack_error_invalid);
+ return false;
+
+ #if !MPACK_EXTENSIONS
+ // ext
+ case 0xc7: // fallthrough
+ case 0xc8: // fallthrough
+ case 0xc9: // fallthrough
+ // fixext
+ case 0xd4: // fallthrough
+ case 0xd5: // fallthrough
+ case 0xd6: // fallthrough
+ case 0xd7: // fallthrough
+ case 0xd8:
+ mpack_tree_flag_error(tree, mpack_error_unsupported);
+ return false;
+ #endif
+
+ #if MPACK_OPTIMIZE_FOR_SIZE
+ // any other bytes should have been handled by the infix switch
+ default:
+ break;
+ #endif
+ }
+
+ mpack_assert(0, "unreachable");
+ return false;
+}
+
+static bool mpack_tree_parse_node(mpack_tree_t* tree, mpack_node_data_t* node) {
+ mpack_log("parsing a node at position %i in level %i\n",
+ (int)tree->size, (int)tree->parser.level);
+
+ if (!mpack_tree_parse_node_contents(tree, node)) {
+ mpack_log("node parsing returned false\n");
+ return false;
+ }
+
+ tree->parser.possible_nodes_left -= tree->parser.current_node_reserved;
+
+ // The reserve for the current node does not include the initial byte
+ // previously reserved as part of its parent.
+ size_t node_size = tree->parser.current_node_reserved + 1;
+
+ // If the parsed type is a map or array, the reserve includes one byte for
+ // each child. We want to subtract these out of possible_nodes_left, but
+ // not out of the current size of the tree.
+ if (node->type == mpack_type_array)
+ node_size -= node->len;
+ else if (node->type == mpack_type_map)
+ node_size -= node->len * 2;
+ tree->size += node_size;
+
+ mpack_log("parsed a node of type %s of %i bytes and "
+ "%i additional bytes reserved for children.\n",
+ mpack_type_to_string(node->type), (int)node_size,
+ (int)tree->parser.current_node_reserved + 1 - (int)node_size);
+
+ return true;
+}
+
+/*
+ * We read nodes in a loop instead of recursively for maximum performance. The
+ * stack holds the amount of children left to read in each level of the tree.
+ * Parsing can pause and resume when more data becomes available.
+ */
+static bool mpack_tree_continue_parsing(mpack_tree_t* tree) {
+ if (mpack_tree_error(tree) != mpack_ok)
+ return false;
+
+ mpack_tree_parser_t* parser = &tree->parser;
+ mpack_assert(parser->state == mpack_tree_parse_state_in_progress);
+ mpack_log("parsing tree elements, %i bytes in buffer\n", (int)tree->data_length);
+
+ // we loop parsing nodes until the parse stack is empty. we break
+ // by returning out of the function.
+ while (true) {
+ mpack_node_data_t* node = parser->stack[parser->level].child;
+ size_t level = parser->level;
+ if (!mpack_tree_parse_node(tree, node))
+ return false;
+ --parser->stack[level].left;
+ ++parser->stack[level].child;
+
+ mpack_assert(mpack_tree_error(tree) == mpack_ok,
+ "mpack_tree_parse_node() should have returned false due to error!");
+
+ // pop empty stack levels, exiting the outer loop when the stack is empty.
+ // (we could tail-optimize containers by pre-emptively popping empty
+ // stack levels before reading the new element, this way we wouldn't
+ // have to loop. but we eventually want to use the parse stack to give
+ // better error messages that contain the location of the error, so
+ // it needs to be complete.)
+ while (parser->stack[parser->level].left == 0) {
+ if (parser->level == 0)
+ return true;
+ --parser->level;
+ }
+ }
+}
+
+static void mpack_tree_cleanup(mpack_tree_t* tree) {
+ MPACK_UNUSED(tree);
+
+ #ifdef MPACK_MALLOC
+ if (tree->parser.stack_owned) {
+ MPACK_FREE(tree->parser.stack);
+ tree->parser.stack = NULL;
+ tree->parser.stack_owned = false;
+ }
+
+ mpack_tree_page_t* page = tree->next;
+ while (page != NULL) {
+ mpack_tree_page_t* next = page->next;
+ mpack_log("freeing page %p\n", (void*)page);
+ MPACK_FREE(page);
+ page = next;
+ }
+ tree->next = NULL;
+ #endif
+}
+
+static bool mpack_tree_parse_start(mpack_tree_t* tree) {
+ if (mpack_tree_error(tree) != mpack_ok)
+ return false;
+
+ mpack_tree_parser_t* parser = &tree->parser;
+ mpack_assert(parser->state != mpack_tree_parse_state_in_progress,
+ "previous parsing was not finished!");
+
+ if (parser->state == mpack_tree_parse_state_parsed)
+ mpack_tree_cleanup(tree);
+
+ mpack_log("starting parse\n");
+ tree->parser.state = mpack_tree_parse_state_in_progress;
+ tree->parser.current_node_reserved = 0;
+
+ // check if we previously parsed a tree
+ if (tree->size > 0) {
+ #ifdef MPACK_MALLOC
+ // if we're buffered, move the remaining data back to the
+ // start of the buffer
+ // TODO: This is not ideal performance-wise. We should only move data
+ // when we need to call the fill function.
+ // TODO: We could consider shrinking the buffer here, especially if we
+ // determine that the fill function is providing less than a quarter of
+ // the buffer size or if messages take up less than a quarter of the
+ // buffer size. Maybe this should be configurable.
+ if (tree->buffer != NULL) {
+ mpack_memmove(tree->buffer, tree->buffer + tree->size, tree->data_length - tree->size);
+ }
+ else
+ #endif
+ // otherwise advance past the parsed data
+ {
+ tree->data += tree->size;
+ }
+ tree->data_length -= tree->size;
+ tree->size = 0;
+ tree->node_count = 0;
+ }
+
+ // make sure we have at least one byte available before allocating anything
+ parser->possible_nodes_left = tree->data_length;
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint8_t))) {
+ tree->parser.state = mpack_tree_parse_state_not_started;
+ return false;
+ }
+ mpack_log("parsing tree at %p starting with byte %x\n", tree->data, (uint8_t)tree->data[0]);
+ parser->possible_nodes_left -= 1;
+ tree->node_count = 1;
+
+ #ifdef MPACK_MALLOC
+ parser->stack = parser->stack_local;
+ parser->stack_owned = false;
+ parser->stack_capacity = sizeof(parser->stack_local) / sizeof(*parser->stack_local);
+
+ if (tree->pool == NULL) {
+
+ // allocate first page
+ mpack_tree_page_t* page = (mpack_tree_page_t*)MPACK_MALLOC(MPACK_PAGE_ALLOC_SIZE);
+ mpack_log("allocated initial page %p of size %i count %i\n",
+ (void*)page, (int)MPACK_PAGE_ALLOC_SIZE, (int)MPACK_NODES_PER_PAGE);
+ if (page == NULL) {
+ tree->error = mpack_error_memory;
+ return false;
+ }
+ page->next = NULL;
+ tree->next = page;
+
+ parser->nodes = page->nodes;
+ parser->nodes_left = MPACK_NODES_PER_PAGE;
+ }
+ else
+ #endif
+ {
+ // otherwise use the provided pool
+ mpack_assert(tree->pool != NULL, "no pool provided?");
+ parser->nodes = tree->pool;
+ parser->nodes_left = tree->pool_count;
+ }
+
+ tree->root = parser->nodes;
+ ++parser->nodes;
+ --parser->nodes_left;
+
+ parser->level = 0;
+ parser->stack[0].child = tree->root;
+ parser->stack[0].left = 1;
+
+ return true;
+}
+
+void mpack_tree_parse(mpack_tree_t* tree) {
+ if (mpack_tree_error(tree) != mpack_ok)
+ return;
+
+ if (tree->parser.state != mpack_tree_parse_state_in_progress) {
+ if (!mpack_tree_parse_start(tree)) {
+ mpack_tree_flag_error(tree, (tree->read_fn == NULL) ?
+ mpack_error_invalid : mpack_error_io);
+ return;
+ }
+ }
+
+ if (!mpack_tree_continue_parsing(tree)) {
+ if (mpack_tree_error(tree) != mpack_ok)
+ return;
+
+ // We're parsing synchronously on a blocking fill function. If we
+ // didn't completely finish parsing the tree, it's an error.
+ mpack_log("tree parsing incomplete. flagging error.\n");
+ mpack_tree_flag_error(tree, (tree->read_fn == NULL) ?
+ mpack_error_invalid : mpack_error_io);
+ return;
+ }
+
+ mpack_assert(mpack_tree_error(tree) == mpack_ok);
+ mpack_assert(tree->parser.level == 0);
+ tree->parser.state = mpack_tree_parse_state_parsed;
+ mpack_log("parsed tree of %i bytes, %i bytes left\n", (int)tree->size, (int)tree->parser.possible_nodes_left);
+ mpack_log("%i nodes in final page\n", (int)tree->parser.nodes_left);
+}
+
+bool mpack_tree_try_parse(mpack_tree_t* tree) {
+ if (mpack_tree_error(tree) != mpack_ok)
+ return false;
+
+ if (tree->parser.state != mpack_tree_parse_state_in_progress)
+ if (!mpack_tree_parse_start(tree))
+ return false;
+
+ if (!mpack_tree_continue_parsing(tree))
+ return false;
+
+ mpack_assert(mpack_tree_error(tree) == mpack_ok);
+ mpack_assert(tree->parser.level == 0);
+ tree->parser.state = mpack_tree_parse_state_parsed;
+ return true;
+}
+
+
+
+/*
+ * Tree functions
+ */
+
+mpack_node_t mpack_tree_root(mpack_tree_t* tree) {
+ if (mpack_tree_error(tree) != mpack_ok)
+ return mpack_tree_nil_node(tree);
+
+ // We check that a tree was parsed successfully and assert if not. You must
+ // call mpack_tree_parse() (or mpack_tree_try_parse() with a success
+ // result) in order to access the root node.
+ if (tree->parser.state != mpack_tree_parse_state_parsed) {
+ mpack_break("Tree has not been parsed! "
+ "Did you call mpack_tree_parse() or mpack_tree_try_parse()?");
+ mpack_tree_flag_error(tree, mpack_error_bug);
+ return mpack_tree_nil_node(tree);
+ }
+
+ return mpack_node(tree, tree->root);
+}
+
+static void mpack_tree_init_clear(mpack_tree_t* tree) {
+ mpack_memset(tree, 0, sizeof(*tree));
+ tree->nil_node.type = mpack_type_nil;
+ tree->missing_node.type = mpack_type_missing;
+ tree->max_size = SIZE_MAX;
+ tree->max_nodes = SIZE_MAX;
+}
+
+#ifdef MPACK_MALLOC
+void mpack_tree_init_data(mpack_tree_t* tree, const char* data, size_t length) {
+ mpack_tree_init_clear(tree);
+
+ MPACK_STATIC_ASSERT(MPACK_NODE_PAGE_SIZE >= sizeof(mpack_tree_page_t),
+ "MPACK_NODE_PAGE_SIZE is too small");
+
+ MPACK_STATIC_ASSERT(MPACK_PAGE_ALLOC_SIZE <= MPACK_NODE_PAGE_SIZE,
+ "incorrect page rounding?");
+
+ tree->data = data;
+ tree->data_length = length;
+ tree->pool = NULL;
+ tree->pool_count = 0;
+ tree->next = NULL;
+
+ mpack_log("===========================\n");
+ mpack_log("initializing tree with data of size %i\n", (int)length);
+}
+#endif
+
+void mpack_tree_init_pool(mpack_tree_t* tree, const char* data, size_t length,
+ mpack_node_data_t* node_pool, size_t node_pool_count)
+{
+ mpack_tree_init_clear(tree);
+ #ifdef MPACK_MALLOC
+ tree->next = NULL;
+ #endif
+
+ if (node_pool_count == 0) {
+ mpack_break("initial page has no nodes!");
+ mpack_tree_flag_error(tree, mpack_error_bug);
+ return;
+ }
+
+ tree->data = data;
+ tree->data_length = length;
+ tree->pool = node_pool;
+ tree->pool_count = node_pool_count;
+
+ mpack_log("===========================\n");
+ mpack_log("initializing tree with data of size %i and pool of count %i\n",
+ (int)length, (int)node_pool_count);
+}
+
+void mpack_tree_init_error(mpack_tree_t* tree, mpack_error_t error) {
+ mpack_tree_init_clear(tree);
+ tree->error = error;
+
+ mpack_log("===========================\n");
+ mpack_log("initializing tree error state %i\n", (int)error);
+}
+
+#ifdef MPACK_MALLOC
+void mpack_tree_init_stream(mpack_tree_t* tree, mpack_tree_read_t read_fn, void* context,
+ size_t max_message_size, size_t max_message_nodes) {
+ mpack_tree_init_clear(tree);
+
+ tree->read_fn = read_fn;
+ tree->context = context;
+
+ mpack_tree_set_limits(tree, max_message_size, max_message_nodes);
+ tree->max_size = max_message_size;
+ tree->max_nodes = max_message_nodes;
+
+ mpack_log("===========================\n");
+ mpack_log("initializing tree with stream, max size %i max nodes %i\n",
+ (int)max_message_size, (int)max_message_nodes);
+}
+#endif
+
+void mpack_tree_set_limits(mpack_tree_t* tree, size_t max_message_size, size_t max_message_nodes) {
+ mpack_assert(max_message_size > 0);
+ mpack_assert(max_message_nodes > 0);
+ tree->max_size = max_message_size;
+ tree->max_nodes = max_message_nodes;
+}
+
+#if MPACK_STDIO
+typedef struct mpack_file_tree_t {
+ char* data;
+ size_t size;
+ char buffer[MPACK_BUFFER_SIZE];
+} mpack_file_tree_t;
+
+static void mpack_file_tree_teardown(mpack_tree_t* tree) {
+ mpack_file_tree_t* file_tree = (mpack_file_tree_t*)tree->context;
+ MPACK_FREE(file_tree->data);
+ MPACK_FREE(file_tree);
+}
+
+static bool mpack_file_tree_read(mpack_tree_t* tree, mpack_file_tree_t* file_tree, FILE* file, size_t max_bytes) {
+
+ // get the file size
+ errno = 0;
+ int error = 0;
+ fseek(file, 0, SEEK_END);
+ error |= errno;
+ long size = ftell(file);
+ error |= errno;
+ fseek(file, 0, SEEK_SET);
+ error |= errno;
+
+ // check for errors
+ if (error != 0 || size < 0) {
+ mpack_tree_init_error(tree, mpack_error_io);
+ return false;
+ }
+ if (size == 0) {
+ mpack_tree_init_error(tree, mpack_error_invalid);
+ return false;
+ }
+
+ // make sure the size is less than max_bytes
+ // (this mess exists to safely convert between long and size_t regardless of their widths)
+ if (max_bytes != 0 && (((uint64_t)LONG_MAX > (uint64_t)SIZE_MAX && size > (long)SIZE_MAX) || (size_t)size > max_bytes)) {
+ mpack_tree_init_error(tree, mpack_error_too_big);
+ return false;
+ }
+
+ // allocate data
+ file_tree->data = (char*)MPACK_MALLOC((size_t)size);
+ if (file_tree->data == NULL) {
+ mpack_tree_init_error(tree, mpack_error_memory);
+ return false;
+ }
+
+ // read the file
+ long total = 0;
+ while (total < size) {
+ size_t read = fread(file_tree->data + total, 1, (size_t)(size - total), file);
+ if (read <= 0) {
+ mpack_tree_init_error(tree, mpack_error_io);
+ MPACK_FREE(file_tree->data);
+ return false;
+ }
+ total += (long)read;
+ }
+
+ file_tree->size = (size_t)size;
+ return true;
+}
+
+static bool mpack_tree_file_check_max_bytes(mpack_tree_t* tree, size_t max_bytes) {
+
+ // the C STDIO family of file functions use long (e.g. ftell)
+ if (max_bytes > LONG_MAX) {
+ mpack_break("max_bytes of %" PRIu64 " is invalid, maximum is LONG_MAX", (uint64_t)max_bytes);
+ mpack_tree_init_error(tree, mpack_error_bug);
+ return false;
+ }
+
+ return true;
+}
+
+static void mpack_tree_init_stdfile_noclose(mpack_tree_t* tree, FILE* stdfile, size_t max_bytes) {
+
+ // allocate file tree
+ mpack_file_tree_t* file_tree = (mpack_file_tree_t*) MPACK_MALLOC(sizeof(mpack_file_tree_t));
+ if (file_tree == NULL) {
+ mpack_tree_init_error(tree, mpack_error_memory);
+ return;
+ }
+
+ // read all data
+ if (!mpack_file_tree_read(tree, file_tree, stdfile, max_bytes)) {
+ MPACK_FREE(file_tree);
+ return;
+ }
+
+ mpack_tree_init_data(tree, file_tree->data, file_tree->size);
+ mpack_tree_set_context(tree, file_tree);
+ mpack_tree_set_teardown(tree, mpack_file_tree_teardown);
+}
+
+void mpack_tree_init_stdfile(mpack_tree_t* tree, FILE* stdfile, size_t max_bytes, bool close_when_done) {
+ if (!mpack_tree_file_check_max_bytes(tree, max_bytes))
+ return;
+
+ mpack_tree_init_stdfile_noclose(tree, stdfile, max_bytes);
+
+ if (close_when_done)
+ fclose(stdfile);
+}
+
+void mpack_tree_init_filename(mpack_tree_t* tree, const char* filename, size_t max_bytes) {
+ if (!mpack_tree_file_check_max_bytes(tree, max_bytes))
+ return;
+
+ // open the file
+ FILE* file = fopen(filename, "rb");
+ if (!file) {
+ mpack_tree_init_error(tree, mpack_error_io);
+ return;
+ }
+
+ mpack_tree_init_stdfile(tree, file, max_bytes, true);
+}
+#endif
+
+mpack_error_t mpack_tree_destroy(mpack_tree_t* tree) {
+ mpack_tree_cleanup(tree);
+
+ #ifdef MPACK_MALLOC
+ if (tree->buffer)
+ MPACK_FREE(tree->buffer);
+ #endif
+
+ if (tree->teardown)
+ tree->teardown(tree);
+ tree->teardown = NULL;
+
+ return tree->error;
+}
+
+void mpack_tree_flag_error(mpack_tree_t* tree, mpack_error_t error) {
+ if (tree->error == mpack_ok) {
+ mpack_log("tree %p setting error %i: %s\n", (void*)tree, (int)error, mpack_error_to_string(error));
+ tree->error = error;
+ if (tree->error_fn)
+ tree->error_fn(tree, error);
+ }
+
+}
+
+
+
+/*
+ * Node misc functions
+ */
+
+void mpack_node_flag_error(mpack_node_t node, mpack_error_t error) {
+ mpack_tree_flag_error(node.tree, error);
+}
+
+mpack_tag_t mpack_node_tag(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return mpack_tag_nil();
+
+ mpack_tag_t tag = MPACK_TAG_ZERO;
+
+ tag.type = node.data->type;
+ switch (node.data->type) {
+ case mpack_type_missing:
+ // If a node is missing, I don't know if it makes sense to ask for
+ // a tag for it. We'll return a missing tag to match the missing
+ // node I guess, but attempting to use the tag for anything (like
+ // writing it for example) will flag mpack_error_bug.
+ break;
+ case mpack_type_nil: break;
+ case mpack_type_bool: tag.v.b = node.data->value.b; break;
+ case mpack_type_float: tag.v.f = node.data->value.f; break;
+ case mpack_type_double: tag.v.d = node.data->value.d; break;
+ case mpack_type_int: tag.v.i = node.data->value.i; break;
+ case mpack_type_uint: tag.v.u = node.data->value.u; break;
+
+ case mpack_type_str: tag.v.l = node.data->len; break;
+ case mpack_type_bin: tag.v.l = node.data->len; break;
+
+ #if MPACK_EXTENSIONS
+ case mpack_type_ext:
+ tag.v.l = node.data->len;
+ tag.exttype = mpack_node_exttype_unchecked(node);
+ break;
+ #endif
+
+ case mpack_type_array: tag.v.n = node.data->len; break;
+ case mpack_type_map: tag.v.n = node.data->len; break;
+
+ default:
+ mpack_assert(0, "unrecognized type %i", (int)node.data->type);
+ break;
+ }
+ return tag;
+}
+
+#if MPACK_DEBUG && MPACK_STDIO
+static void mpack_node_print_element(mpack_node_t node, mpack_print_t* print, size_t depth) {
+ mpack_node_data_t* data = node.data;
+ size_t i,j;
+ switch (data->type) {
+ case mpack_type_str:
+ {
+ mpack_print_append_cstr(print, "\"");
+ const char* bytes = mpack_node_data_unchecked(node);
+ for (i = 0; i < data->len; ++i) {
+ char c = bytes[i];
+ switch (c) {
+ case '\n': mpack_print_append_cstr(print, "\\n"); break;
+ case '\\': mpack_print_append_cstr(print, "\\\\"); break;
+ case '"': mpack_print_append_cstr(print, "\\\""); break;
+ default: mpack_print_append(print, &c, 1); break;
+ }
+ }
+ mpack_print_append_cstr(print, "\"");
+ }
+ break;
+
+ case mpack_type_array:
+ mpack_print_append_cstr(print, "[\n");
+ for (i = 0; i < data->len; ++i) {
+ for (j = 0; j < depth + 1; ++j)
+ mpack_print_append_cstr(print, " ");
+ mpack_node_print_element(mpack_node_array_at(node, i), print, depth + 1);
+ if (i != data->len - 1)
+ mpack_print_append_cstr(print, ",");
+ mpack_print_append_cstr(print, "\n");
+ }
+ for (i = 0; i < depth; ++i)
+ mpack_print_append_cstr(print, " ");
+ mpack_print_append_cstr(print, "]");
+ break;
+
+ case mpack_type_map:
+ mpack_print_append_cstr(print, "{\n");
+ for (i = 0; i < data->len; ++i) {
+ for (j = 0; j < depth + 1; ++j)
+ mpack_print_append_cstr(print, " ");
+ mpack_node_print_element(mpack_node_map_key_at(node, i), print, depth + 1);
+ mpack_print_append_cstr(print, ": ");
+ mpack_node_print_element(mpack_node_map_value_at(node, i), print, depth + 1);
+ if (i != data->len - 1)
+ mpack_print_append_cstr(print, ",");
+ mpack_print_append_cstr(print, "\n");
+ }
+ for (i = 0; i < depth; ++i)
+ mpack_print_append_cstr(print, " ");
+ mpack_print_append_cstr(print, "}");
+ break;
+
+ default:
+ {
+ const char* prefix = NULL;
+ size_t prefix_length = 0;
+ if (mpack_node_type(node) == mpack_type_bin
+ #if MPACK_EXTENSIONS
+ || mpack_node_type(node) == mpack_type_ext
+ #endif
+ ) {
+ prefix = mpack_node_data(node);
+ prefix_length = mpack_node_data_len(node);
+ }
+
+ char buf[256];
+ mpack_tag_t tag = mpack_node_tag(node);
+ mpack_tag_debug_pseudo_json(tag, buf, sizeof(buf), prefix, prefix_length);
+ mpack_print_append_cstr(print, buf);
+ }
+ break;
+ }
+}
+
+void mpack_node_print_to_buffer(mpack_node_t node, char* buffer, size_t buffer_size) {
+ if (buffer_size == 0) {
+ mpack_assert(false, "buffer size is zero!");
+ return;
+ }
+
+ mpack_print_t print;
+ mpack_memset(&print, 0, sizeof(print));
+ print.buffer = buffer;
+ print.size = buffer_size;
+ mpack_node_print_element(node, &print, 0);
+ mpack_print_append(&print, "", 1); // null-terminator
+ mpack_print_flush(&print);
+
+ // we always make sure there's a null-terminator at the end of the buffer
+ // in case we ran out of space.
+ print.buffer[print.size - 1] = '\0';
+}
+
+void mpack_node_print_to_callback(mpack_node_t node, mpack_print_callback_t callback, void* context) {
+ char buffer[1024];
+ mpack_print_t print;
+ mpack_memset(&print, 0, sizeof(print));
+ print.buffer = buffer;
+ print.size = sizeof(buffer);
+ print.callback = callback;
+ print.context = context;
+ mpack_node_print_element(node, &print, 0);
+ mpack_print_flush(&print);
+}
+
+void mpack_node_print_to_file(mpack_node_t node, FILE* file) {
+ mpack_assert(file != NULL, "file is NULL");
+
+ char buffer[1024];
+ mpack_print_t print;
+ mpack_memset(&print, 0, sizeof(print));
+ print.buffer = buffer;
+ print.size = sizeof(buffer);
+ print.callback = &mpack_print_file_callback;
+ print.context = file;
+
+ size_t depth = 2;
+ size_t i;
+ for (i = 0; i < depth; ++i)
+ mpack_print_append_cstr(&print, " ");
+ mpack_node_print_element(node, &print, depth);
+ mpack_print_append_cstr(&print, "\n");
+ mpack_print_flush(&print);
+}
+#endif
+
+
+
+/*
+ * Node Value Functions
+ */
+
+#if MPACK_EXTENSIONS
+mpack_timestamp_t mpack_node_timestamp(mpack_node_t node) {
+ mpack_timestamp_t timestamp = {0, 0};
+
+ // we'll let mpack_node_exttype() do most checks
+ if (mpack_node_exttype(node) != MPACK_EXTTYPE_TIMESTAMP) {
+ mpack_log("exttype %i\n", mpack_node_exttype(node));
+ mpack_node_flag_error(node, mpack_error_type);
+ return timestamp;
+ }
+
+ const char* p = mpack_node_data_unchecked(node);
+
+ switch (node.data->len) {
+ case 4:
+ timestamp.nanoseconds = 0;
+ timestamp.seconds = mpack_load_u32(p);
+ break;
+
+ case 8: {
+ uint64_t value = mpack_load_u64(p);
+ timestamp.nanoseconds = (uint32_t)(value >> 34);
+ timestamp.seconds = value & ((MPACK_UINT64_C(1) << 34) - 1);
+ break;
+ }
+
+ case 12:
+ timestamp.nanoseconds = mpack_load_u32(p);
+ timestamp.seconds = mpack_load_i64(p + 4);
+ break;
+
+ default:
+ mpack_tree_flag_error(node.tree, mpack_error_invalid);
+ return timestamp;
+ }
+
+ if (timestamp.nanoseconds > MPACK_TIMESTAMP_NANOSECONDS_MAX) {
+ mpack_tree_flag_error(node.tree, mpack_error_invalid);
+ mpack_timestamp_t zero = {0, 0};
+ return zero;
+ }
+
+ return timestamp;
+}
+
+int64_t mpack_node_timestamp_seconds(mpack_node_t node) {
+ return mpack_node_timestamp(node).seconds;
+}
+
+uint32_t mpack_node_timestamp_nanoseconds(mpack_node_t node) {
+ return mpack_node_timestamp(node).nanoseconds;
+}
+#endif
+
+
+
+/*
+ * Node Data Functions
+ */
+
+void mpack_node_check_utf8(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return;
+ mpack_node_data_t* data = node.data;
+ if (data->type != mpack_type_str || !mpack_utf8_check(mpack_node_data_unchecked(node), data->len))
+ mpack_node_flag_error(node, mpack_error_type);
+}
+
+void mpack_node_check_utf8_cstr(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return;
+ mpack_node_data_t* data = node.data;
+ if (data->type != mpack_type_str || !mpack_utf8_check_no_null(mpack_node_data_unchecked(node), data->len))
+ mpack_node_flag_error(node, mpack_error_type);
+}
+
+size_t mpack_node_copy_data(mpack_node_t node, char* buffer, size_t bufsize) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0;
+
+ mpack_assert(bufsize == 0 || buffer != NULL, "buffer is NULL for maximum of %i bytes", (int)bufsize);
+
+ mpack_type_t type = node.data->type;
+ if (type != mpack_type_str && type != mpack_type_bin
+ #if MPACK_EXTENSIONS
+ && type != mpack_type_ext
+ #endif
+ ) {
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+ }
+
+ if (node.data->len > bufsize) {
+ mpack_node_flag_error(node, mpack_error_too_big);
+ return 0;
+ }
+
+ mpack_memcpy(buffer, mpack_node_data_unchecked(node), node.data->len);
+ return (size_t)node.data->len;
+}
+
+size_t mpack_node_copy_utf8(mpack_node_t node, char* buffer, size_t bufsize) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0;
+
+ mpack_assert(bufsize == 0 || buffer != NULL, "buffer is NULL for maximum of %i bytes", (int)bufsize);
+
+ mpack_type_t type = node.data->type;
+ if (type != mpack_type_str) {
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+ }
+
+ if (node.data->len > bufsize) {
+ mpack_node_flag_error(node, mpack_error_too_big);
+ return 0;
+ }
+
+ if (!mpack_utf8_check(mpack_node_data_unchecked(node), node.data->len)) {
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+ }
+
+ mpack_memcpy(buffer, mpack_node_data_unchecked(node), node.data->len);
+ return (size_t)node.data->len;
+}
+
+void mpack_node_copy_cstr(mpack_node_t node, char* buffer, size_t bufsize) {
+
+ // we can't break here because the error isn't recoverable; we
+ // have to add a null-terminator.
+ mpack_assert(buffer != NULL, "buffer is NULL");
+ mpack_assert(bufsize >= 1, "buffer size is zero; you must have room for at least a null-terminator");
+
+ if (mpack_node_error(node) != mpack_ok) {
+ buffer[0] = '\0';
+ return;
+ }
+
+ if (node.data->type != mpack_type_str) {
+ buffer[0] = '\0';
+ mpack_node_flag_error(node, mpack_error_type);
+ return;
+ }
+
+ if (node.data->len > bufsize - 1) {
+ buffer[0] = '\0';
+ mpack_node_flag_error(node, mpack_error_too_big);
+ return;
+ }
+
+ if (!mpack_str_check_no_null(mpack_node_data_unchecked(node), node.data->len)) {
+ buffer[0] = '\0';
+ mpack_node_flag_error(node, mpack_error_type);
+ return;
+ }
+
+ mpack_memcpy(buffer, mpack_node_data_unchecked(node), node.data->len);
+ buffer[node.data->len] = '\0';
+}
+
+void mpack_node_copy_utf8_cstr(mpack_node_t node, char* buffer, size_t bufsize) {
+
+ // we can't break here because the error isn't recoverable; we
+ // have to add a null-terminator.
+ mpack_assert(buffer != NULL, "buffer is NULL");
+ mpack_assert(bufsize >= 1, "buffer size is zero; you must have room for at least a null-terminator");
+
+ if (mpack_node_error(node) != mpack_ok) {
+ buffer[0] = '\0';
+ return;
+ }
+
+ if (node.data->type != mpack_type_str) {
+ buffer[0] = '\0';
+ mpack_node_flag_error(node, mpack_error_type);
+ return;
+ }
+
+ if (node.data->len > bufsize - 1) {
+ buffer[0] = '\0';
+ mpack_node_flag_error(node, mpack_error_too_big);
+ return;
+ }
+
+ if (!mpack_utf8_check_no_null(mpack_node_data_unchecked(node), node.data->len)) {
+ buffer[0] = '\0';
+ mpack_node_flag_error(node, mpack_error_type);
+ return;
+ }
+
+ mpack_memcpy(buffer, mpack_node_data_unchecked(node), node.data->len);
+ buffer[node.data->len] = '\0';
+}
+
+#ifdef MPACK_MALLOC
+char* mpack_node_data_alloc(mpack_node_t node, size_t maxlen) {
+ if (mpack_node_error(node) != mpack_ok)
+ return NULL;
+
+ // make sure this is a valid data type
+ mpack_type_t type = node.data->type;
+ if (type != mpack_type_str && type != mpack_type_bin
+ #if MPACK_EXTENSIONS
+ && type != mpack_type_ext
+ #endif
+ ) {
+ mpack_node_flag_error(node, mpack_error_type);
+ return NULL;
+ }
+
+ if (node.data->len > maxlen) {
+ mpack_node_flag_error(node, mpack_error_too_big);
+ return NULL;
+ }
+
+ char* ret = (char*) MPACK_MALLOC((size_t)node.data->len);
+ if (ret == NULL) {
+ mpack_node_flag_error(node, mpack_error_memory);
+ return NULL;
+ }
+
+ mpack_memcpy(ret, mpack_node_data_unchecked(node), node.data->len);
+ return ret;
+}
+
+char* mpack_node_cstr_alloc(mpack_node_t node, size_t maxlen) {
+ if (mpack_node_error(node) != mpack_ok)
+ return NULL;
+
+ // make sure maxlen makes sense
+ if (maxlen < 1) {
+ mpack_break("maxlen is zero; you must have room for at least a null-terminator");
+ mpack_node_flag_error(node, mpack_error_bug);
+ return NULL;
+ }
+
+ if (node.data->type != mpack_type_str) {
+ mpack_node_flag_error(node, mpack_error_type);
+ return NULL;
+ }
+
+ if (node.data->len > maxlen - 1) {
+ mpack_node_flag_error(node, mpack_error_too_big);
+ return NULL;
+ }
+
+ if (!mpack_str_check_no_null(mpack_node_data_unchecked(node), node.data->len)) {
+ mpack_node_flag_error(node, mpack_error_type);
+ return NULL;
+ }
+
+ char* ret = (char*) MPACK_MALLOC((size_t)(node.data->len + 1));
+ if (ret == NULL) {
+ mpack_node_flag_error(node, mpack_error_memory);
+ return NULL;
+ }
+
+ mpack_memcpy(ret, mpack_node_data_unchecked(node), node.data->len);
+ ret[node.data->len] = '\0';
+ return ret;
+}
+
+char* mpack_node_utf8_cstr_alloc(mpack_node_t node, size_t maxlen) {
+ if (mpack_node_error(node) != mpack_ok)
+ return NULL;
+
+ // make sure maxlen makes sense
+ if (maxlen < 1) {
+ mpack_break("maxlen is zero; you must have room for at least a null-terminator");
+ mpack_node_flag_error(node, mpack_error_bug);
+ return NULL;
+ }
+
+ if (node.data->type != mpack_type_str) {
+ mpack_node_flag_error(node, mpack_error_type);
+ return NULL;
+ }
+
+ if (node.data->len > maxlen - 1) {
+ mpack_node_flag_error(node, mpack_error_too_big);
+ return NULL;
+ }
+
+ if (!mpack_utf8_check_no_null(mpack_node_data_unchecked(node), node.data->len)) {
+ mpack_node_flag_error(node, mpack_error_type);
+ return NULL;
+ }
+
+ char* ret = (char*) MPACK_MALLOC((size_t)(node.data->len + 1));
+ if (ret == NULL) {
+ mpack_node_flag_error(node, mpack_error_memory);
+ return NULL;
+ }
+
+ mpack_memcpy(ret, mpack_node_data_unchecked(node), node.data->len);
+ ret[node.data->len] = '\0';
+ return ret;
+}
+#endif
+
+
+/*
+ * Compound Node Functions
+ */
+
+static mpack_node_data_t* mpack_node_map_int_impl(mpack_node_t node, int64_t num) {
+ if (mpack_node_error(node) != mpack_ok)
+ return NULL;
+
+ if (node.data->type != mpack_type_map) {
+ mpack_node_flag_error(node, mpack_error_type);
+ return NULL;
+ }
+
+ mpack_node_data_t* found = NULL;
+
+ size_t i;
+ for (i = 0; i < node.data->len; ++i) {
+ mpack_node_data_t* key = mpack_node_child(node, i * 2);
+
+ if ((key->type == mpack_type_int && key->value.i == num) ||
+ (key->type == mpack_type_uint && num >= 0 && key->value.u == (uint64_t)num))
+ {
+ if (found) {
+ mpack_node_flag_error(node, mpack_error_data);
+ return NULL;
+ }
+ found = mpack_node_child(node, i * 2 + 1);
+ }
+ }
+
+ if (found)
+ return found;
+
+ return NULL;
+}
+
+static mpack_node_data_t* mpack_node_map_uint_impl(mpack_node_t node, uint64_t num) {
+ if (mpack_node_error(node) != mpack_ok)
+ return NULL;
+
+ if (node.data->type != mpack_type_map) {
+ mpack_node_flag_error(node, mpack_error_type);
+ return NULL;
+ }
+
+ mpack_node_data_t* found = NULL;
+
+ size_t i;
+ for (i = 0; i < node.data->len; ++i) {
+ mpack_node_data_t* key = mpack_node_child(node, i * 2);
+
+ if ((key->type == mpack_type_uint && key->value.u == num) ||
+ (key->type == mpack_type_int && key->value.i >= 0 && (uint64_t)key->value.i == num))
+ {
+ if (found) {
+ mpack_node_flag_error(node, mpack_error_data);
+ return NULL;
+ }
+ found = mpack_node_child(node, i * 2 + 1);
+ }
+ }
+
+ if (found)
+ return found;
+
+ return NULL;
+}
+
+static mpack_node_data_t* mpack_node_map_str_impl(mpack_node_t node, const char* str, size_t length) {
+ if (mpack_node_error(node) != mpack_ok)
+ return NULL;
+
+ mpack_assert(length == 0 || str != NULL, "str of length %i is NULL", (int)length);
+
+ if (node.data->type != mpack_type_map) {
+ mpack_node_flag_error(node, mpack_error_type);
+ return NULL;
+ }
+
+ mpack_tree_t* tree = node.tree;
+ mpack_node_data_t* found = NULL;
+
+ size_t i;
+ for (i = 0; i < node.data->len; ++i) {
+ mpack_node_data_t* key = mpack_node_child(node, i * 2);
+
+ if (key->type == mpack_type_str && key->len == length &&
+ mpack_memcmp(str, mpack_node_data_unchecked(mpack_node(tree, key)), length) == 0) {
+ if (found) {
+ mpack_node_flag_error(node, mpack_error_data);
+ return NULL;
+ }
+ found = mpack_node_child(node, i * 2 + 1);
+ }
+ }
+
+ if (found)
+ return found;
+
+ return NULL;
+}
+
+static mpack_node_t mpack_node_wrap_lookup(mpack_tree_t* tree, mpack_node_data_t* data) {
+ if (!data) {
+ if (tree->error == mpack_ok)
+ mpack_tree_flag_error(tree, mpack_error_data);
+ return mpack_tree_nil_node(tree);
+ }
+ return mpack_node(tree, data);
+}
+
+static mpack_node_t mpack_node_wrap_lookup_optional(mpack_tree_t* tree, mpack_node_data_t* data) {
+ if (!data) {
+ if (tree->error == mpack_ok)
+ return mpack_tree_missing_node(tree);
+ return mpack_tree_nil_node(tree);
+ }
+ return mpack_node(tree, data);
+}
+
+mpack_node_t mpack_node_map_int(mpack_node_t node, int64_t num) {
+ return mpack_node_wrap_lookup(node.tree, mpack_node_map_int_impl(node, num));
+}
+
+mpack_node_t mpack_node_map_int_optional(mpack_node_t node, int64_t num) {
+ return mpack_node_wrap_lookup_optional(node.tree, mpack_node_map_int_impl(node, num));
+}
+
+mpack_node_t mpack_node_map_uint(mpack_node_t node, uint64_t num) {
+ return mpack_node_wrap_lookup(node.tree, mpack_node_map_uint_impl(node, num));
+}
+
+mpack_node_t mpack_node_map_uint_optional(mpack_node_t node, uint64_t num) {
+ return mpack_node_wrap_lookup_optional(node.tree, mpack_node_map_uint_impl(node, num));
+}
+
+mpack_node_t mpack_node_map_str(mpack_node_t node, const char* str, size_t length) {
+ return mpack_node_wrap_lookup(node.tree, mpack_node_map_str_impl(node, str, length));
+}
+
+mpack_node_t mpack_node_map_str_optional(mpack_node_t node, const char* str, size_t length) {
+ return mpack_node_wrap_lookup_optional(node.tree, mpack_node_map_str_impl(node, str, length));
+}
+
+mpack_node_t mpack_node_map_cstr(mpack_node_t node, const char* cstr) {
+ mpack_assert(cstr != NULL, "cstr is NULL");
+ return mpack_node_map_str(node, cstr, mpack_strlen(cstr));
+}
+
+mpack_node_t mpack_node_map_cstr_optional(mpack_node_t node, const char* cstr) {
+ mpack_assert(cstr != NULL, "cstr is NULL");
+ return mpack_node_map_str_optional(node, cstr, mpack_strlen(cstr));
+}
+
+bool mpack_node_map_contains_int(mpack_node_t node, int64_t num) {
+ return mpack_node_map_int_impl(node, num) != NULL;
+}
+
+bool mpack_node_map_contains_uint(mpack_node_t node, uint64_t num) {
+ return mpack_node_map_uint_impl(node, num) != NULL;
+}
+
+bool mpack_node_map_contains_str(mpack_node_t node, const char* str, size_t length) {
+ return mpack_node_map_str_impl(node, str, length) != NULL;
+}
+
+bool mpack_node_map_contains_cstr(mpack_node_t node, const char* cstr) {
+ mpack_assert(cstr != NULL, "cstr is NULL");
+ return mpack_node_map_contains_str(node, cstr, mpack_strlen(cstr));
+}
+
+size_t mpack_node_enum_optional(mpack_node_t node, const char* strings[], size_t count) {
+ if (mpack_node_error(node) != mpack_ok)
+ return count;
+
+ // the value is only recognized if it is a string
+ if (mpack_node_type(node) != mpack_type_str)
+ return count;
+
+ // fetch the string
+ const char* key = mpack_node_str(node);
+ size_t keylen = mpack_node_strlen(node);
+ mpack_assert(mpack_node_error(node) == mpack_ok, "these should not fail");
+
+ // find what key it matches
+ size_t i;
+ for (i = 0; i < count; ++i) {
+ const char* other = strings[i];
+ size_t otherlen = mpack_strlen(other);
+ if (keylen == otherlen && mpack_memcmp(key, other, keylen) == 0)
+ return i;
+ }
+
+ // no matches
+ return count;
+}
+
+size_t mpack_node_enum(mpack_node_t node, const char* strings[], size_t count) {
+ size_t value = mpack_node_enum_optional(node, strings, count);
+ if (value == count)
+ mpack_node_flag_error(node, mpack_error_type);
+ return value;
+}
+
+mpack_type_t mpack_node_type(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return mpack_type_nil;
+ return node.data->type;
+}
+
+bool mpack_node_is_nil(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok) {
+ // All nodes are treated as nil nodes when we are in error.
+ return true;
+ }
+ return node.data->type == mpack_type_nil;
+}
+
+bool mpack_node_is_missing(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok) {
+ // errors still return nil nodes, not missing nodes.
+ return false;
+ }
+ return node.data->type == mpack_type_missing;
+}
+
+void mpack_node_nil(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return;
+ if (node.data->type != mpack_type_nil)
+ mpack_node_flag_error(node, mpack_error_type);
+}
+
+void mpack_node_missing(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return;
+ if (node.data->type != mpack_type_missing)
+ mpack_node_flag_error(node, mpack_error_type);
+}
+
+bool mpack_node_bool(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return false;
+
+ if (node.data->type == mpack_type_bool)
+ return node.data->value.b;
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return false;
+}
+
+void mpack_node_true(mpack_node_t node) {
+ if (mpack_node_bool(node) != true)
+ mpack_node_flag_error(node, mpack_error_type);
+}
+
+void mpack_node_false(mpack_node_t node) {
+ if (mpack_node_bool(node) != false)
+ mpack_node_flag_error(node, mpack_error_type);
+}
+
+uint8_t mpack_node_u8(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0;
+
+ if (node.data->type == mpack_type_uint) {
+ if (node.data->value.u <= MPACK_UINT8_MAX)
+ return (uint8_t)node.data->value.u;
+ } else if (node.data->type == mpack_type_int) {
+ if (node.data->value.i >= 0 && node.data->value.i <= MPACK_UINT8_MAX)
+ return (uint8_t)node.data->value.i;
+ }
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+}
+
+int8_t mpack_node_i8(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0;
+
+ if (node.data->type == mpack_type_uint) {
+ if (node.data->value.u <= MPACK_INT8_MAX)
+ return (int8_t)node.data->value.u;
+ } else if (node.data->type == mpack_type_int) {
+ if (node.data->value.i >= MPACK_INT8_MIN && node.data->value.i <= MPACK_INT8_MAX)
+ return (int8_t)node.data->value.i;
+ }
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+}
+
+uint16_t mpack_node_u16(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0;
+
+ if (node.data->type == mpack_type_uint) {
+ if (node.data->value.u <= MPACK_UINT16_MAX)
+ return (uint16_t)node.data->value.u;
+ } else if (node.data->type == mpack_type_int) {
+ if (node.data->value.i >= 0 && node.data->value.i <= MPACK_UINT16_MAX)
+ return (uint16_t)node.data->value.i;
+ }
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+}
+
+int16_t mpack_node_i16(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0;
+
+ if (node.data->type == mpack_type_uint) {
+ if (node.data->value.u <= MPACK_INT16_MAX)
+ return (int16_t)node.data->value.u;
+ } else if (node.data->type == mpack_type_int) {
+ if (node.data->value.i >= MPACK_INT16_MIN && node.data->value.i <= MPACK_INT16_MAX)
+ return (int16_t)node.data->value.i;
+ }
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+}
+
+uint32_t mpack_node_u32(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0;
+
+ if (node.data->type == mpack_type_uint) {
+ if (node.data->value.u <= MPACK_UINT32_MAX)
+ return (uint32_t)node.data->value.u;
+ } else if (node.data->type == mpack_type_int) {
+ if (node.data->value.i >= 0 && node.data->value.i <= MPACK_UINT32_MAX)
+ return (uint32_t)node.data->value.i;
+ }
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+}
+
+int32_t mpack_node_i32(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0;
+
+ if (node.data->type == mpack_type_uint) {
+ if (node.data->value.u <= MPACK_INT32_MAX)
+ return (int32_t)node.data->value.u;
+ } else if (node.data->type == mpack_type_int) {
+ if (node.data->value.i >= MPACK_INT32_MIN && node.data->value.i <= MPACK_INT32_MAX)
+ return (int32_t)node.data->value.i;
+ }
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+}
+
+uint64_t mpack_node_u64(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0;
+
+ if (node.data->type == mpack_type_uint) {
+ return node.data->value.u;
+ } else if (node.data->type == mpack_type_int) {
+ if (node.data->value.i >= 0)
+ return (uint64_t)node.data->value.i;
+ }
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+}
+
+int64_t mpack_node_i64(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0;
+
+ if (node.data->type == mpack_type_uint) {
+ if (node.data->value.u <= (uint64_t)MPACK_INT64_MAX)
+ return (int64_t)node.data->value.u;
+ } else if (node.data->type == mpack_type_int) {
+ return node.data->value.i;
+ }
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+}
+
+unsigned int mpack_node_uint(mpack_node_t node) {
+
+ // This should be true at compile-time, so this just wraps the 32-bit function.
+ if (sizeof(unsigned int) == 4)
+ return (unsigned int)mpack_node_u32(node);
+
+ // Otherwise we use u64 and check the range.
+ uint64_t val = mpack_node_u64(node);
+ if (val <= MPACK_UINT_MAX)
+ return (unsigned int)val;
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+}
+
+int mpack_node_int(mpack_node_t node) {
+
+ // This should be true at compile-time, so this just wraps the 32-bit function.
+ if (sizeof(int) == 4)
+ return (int)mpack_node_i32(node);
+
+ // Otherwise we use i64 and check the range.
+ int64_t val = mpack_node_i64(node);
+ if (val >= MPACK_INT_MIN && val <= MPACK_INT_MAX)
+ return (int)val;
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+}
+
+#if MPACK_FLOAT
+float mpack_node_float(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0.0f;
+
+ if (node.data->type == mpack_type_uint)
+ return (float)node.data->value.u;
+ if (node.data->type == mpack_type_int)
+ return (float)node.data->value.i;
+ if (node.data->type == mpack_type_float)
+ return node.data->value.f;
+
+ if (node.data->type == mpack_type_double) {
+ #if MPACK_DOUBLE
+ return (float)node.data->value.d;
+ #else
+ return mpack_shorten_raw_double_to_float(node.data->value.d);
+ #endif
+ }
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0.0f;
+}
+#endif
+
+#if MPACK_DOUBLE
+double mpack_node_double(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0.0;
+
+ if (node.data->type == mpack_type_uint)
+ return (double)node.data->value.u;
+ else if (node.data->type == mpack_type_int)
+ return (double)node.data->value.i;
+ else if (node.data->type == mpack_type_float)
+ return (double)node.data->value.f;
+ else if (node.data->type == mpack_type_double)
+ return node.data->value.d;
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0.0;
+}
+#endif
+
+#if MPACK_FLOAT
+float mpack_node_float_strict(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0.0f;
+
+ if (node.data->type == mpack_type_float)
+ return node.data->value.f;
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0.0f;
+}
+#endif
+
+#if MPACK_DOUBLE
+double mpack_node_double_strict(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0.0;
+
+ if (node.data->type == mpack_type_float)
+ return (double)node.data->value.f;
+ else if (node.data->type == mpack_type_double)
+ return node.data->value.d;
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0.0;
+}
+#endif
+
+#if !MPACK_FLOAT
+uint32_t mpack_node_raw_float(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0;
+
+ if (node.data->type == mpack_type_float)
+ return node.data->value.f;
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+}
+#endif
+
+#if !MPACK_DOUBLE
+uint64_t mpack_node_raw_double(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0;
+
+ if (node.data->type == mpack_type_double)
+ return node.data->value.d;
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+}
+#endif
+
+#if MPACK_EXTENSIONS
+int8_t mpack_node_exttype(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0;
+
+ if (node.data->type == mpack_type_ext)
+ return mpack_node_exttype_unchecked(node);
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+}
+#endif
+
+uint32_t mpack_node_data_len(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0;
+
+ mpack_type_t type = node.data->type;
+ if (type == mpack_type_str || type == mpack_type_bin
+ #if MPACK_EXTENSIONS
+ || type == mpack_type_ext
+ #endif
+ )
+ return (uint32_t)node.data->len;
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+}
+
+size_t mpack_node_strlen(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0;
+
+ if (node.data->type == mpack_type_str)
+ return (size_t)node.data->len;
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+}
+
+const char* mpack_node_str(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return NULL;
+
+ mpack_type_t type = node.data->type;
+ if (type == mpack_type_str)
+ return mpack_node_data_unchecked(node);
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return NULL;
+}
+
+const char* mpack_node_data(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return NULL;
+
+ mpack_type_t type = node.data->type;
+ if (type == mpack_type_str || type == mpack_type_bin
+ #if MPACK_EXTENSIONS
+ || type == mpack_type_ext
+ #endif
+ )
+ return mpack_node_data_unchecked(node);
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return NULL;
+}
+
+const char* mpack_node_bin_data(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return NULL;
+
+ if (node.data->type == mpack_type_bin)
+ return mpack_node_data_unchecked(node);
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return NULL;
+}
+
+size_t mpack_node_bin_size(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0;
+
+ if (node.data->type == mpack_type_bin)
+ return (size_t)node.data->len;
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+}
+
+size_t mpack_node_array_length(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0;
+
+ if (node.data->type != mpack_type_array) {
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+ }
+
+ return (size_t)node.data->len;
+}
+
+mpack_node_t mpack_node_array_at(mpack_node_t node, size_t index) {
+ if (mpack_node_error(node) != mpack_ok)
+ return mpack_tree_nil_node(node.tree);
+
+ if (node.data->type != mpack_type_array) {
+ mpack_node_flag_error(node, mpack_error_type);
+ return mpack_tree_nil_node(node.tree);
+ }
+
+ if (index >= node.data->len) {
+ mpack_node_flag_error(node, mpack_error_data);
+ return mpack_tree_nil_node(node.tree);
+ }
+
+ return mpack_node(node.tree, mpack_node_child(node, index));
+}
+
+size_t mpack_node_map_count(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0;
+
+ if (node.data->type != mpack_type_map) {
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+ }
+
+ return node.data->len;
+}
+
+// internal node map lookup
+static mpack_node_t mpack_node_map_at(mpack_node_t node, size_t index, size_t offset) {
+ if (mpack_node_error(node) != mpack_ok)
+ return mpack_tree_nil_node(node.tree);
+
+ if (node.data->type != mpack_type_map) {
+ mpack_node_flag_error(node, mpack_error_type);
+ return mpack_tree_nil_node(node.tree);
+ }
+
+ if (index >= node.data->len) {
+ mpack_node_flag_error(node, mpack_error_data);
+ return mpack_tree_nil_node(node.tree);
+ }
+
+ return mpack_node(node.tree, mpack_node_child(node, index * 2 + offset));
+}
+
+mpack_node_t mpack_node_map_key_at(mpack_node_t node, size_t index) {
+ return mpack_node_map_at(node, index, 0);
+}
+
+mpack_node_t mpack_node_map_value_at(mpack_node_t node, size_t index) {
+ return mpack_node_map_at(node, index, 1);
+}
+
+#endif
+
+} // namespace wpi
+MPACK_SILENCE_WARNINGS_END
diff --git a/wpiutil/src/main/native/cpp/raw_istream.cpp b/wpiutil/src/main/native/cpp/raw_istream.cpp
index f63c217..a179b98 100644
--- a/wpiutil/src/main/native/cpp/raw_istream.cpp
+++ b/wpiutil/src/main/native/cpp/raw_istream.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2015-2019 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#define _CRT_NONSTDC_NO_WARNINGS
@@ -17,10 +14,10 @@
#include <cstdlib>
#include <cstring>
+#include <string_view>
-#include "wpi/FileSystem.h"
#include "wpi/SmallVector.h"
-#include "wpi/StringRef.h"
+#include "wpi/fs.h"
#if defined(_MSC_VER)
#ifndef STDIN_FILENO
@@ -36,22 +33,30 @@
using namespace wpi;
-StringRef raw_istream::getline(SmallVectorImpl<char>& buf, int maxLen) {
+std::string_view raw_istream::getline(SmallVectorImpl<char>& buf, int maxLen) {
buf.clear();
for (int i = 0; i < maxLen; ++i) {
char c;
read(c);
- if (has_error()) return StringRef{buf.data(), buf.size()};
- if (c == '\r') continue;
+ if (has_error()) {
+ return {buf.data(), buf.size()};
+ }
+ if (c == '\r') {
+ continue;
+ }
buf.push_back(c);
- if (c == '\n') break;
+ if (c == '\n') {
+ break;
+ }
}
- return StringRef{buf.data(), buf.size()};
+ return {buf.data(), buf.size()};
}
void raw_mem_istream::close() {}
-size_t raw_mem_istream::in_avail() const { return m_left; }
+size_t raw_mem_istream::in_avail() const {
+ return m_left;
+}
void raw_mem_istream::read_impl(void* data, size_t len) {
if (len > m_left) {
@@ -64,25 +69,27 @@
set_read_count(len);
}
-static int getFD(const Twine& Filename, std::error_code& EC) {
+static int getFD(std::string_view Filename, std::error_code& EC) {
// Handle "-" as stdin. Note that when we do this, we consider ourself
// the owner of stdin. This means that we can do things like close the
// file descriptor when we're done and set the "binary" flag globally.
- if (Filename.isSingleStringRef() && Filename.getSingleStringRef() == "-") {
+ if (Filename == "-") {
EC = std::error_code();
return STDIN_FILENO;
}
- int FD;
-
- EC = sys::fs::openFileForRead(Filename, FD);
- if (EC) return -1;
-
- EC = std::error_code();
+ fs::file_t F = fs::OpenFileForRead(Filename, EC);
+ if (EC) {
+ return -1;
+ }
+ int FD = fs::FileToFd(F, EC, fs::OF_None);
+ if (EC) {
+ return -1;
+ }
return FD;
}
-raw_fd_istream::raw_fd_istream(const Twine& filename, std::error_code& ec,
+raw_fd_istream::raw_fd_istream(std::string_view filename, std::error_code& ec,
size_t bufSize)
: raw_fd_istream(getFD(filename, ec), true, bufSize) {}
@@ -92,7 +99,9 @@
}
raw_fd_istream::~raw_fd_istream() {
- if (m_shouldClose) close();
+ if (m_shouldClose) {
+ close();
+ }
std::free(m_buf);
}
@@ -103,7 +112,9 @@
}
}
-size_t raw_fd_istream::in_avail() const { return m_end - m_cur; }
+size_t raw_fd_istream::in_avail() const {
+ return m_end - m_cur;
+}
void raw_fd_istream::read_impl(void* data, size_t len) {
char* cdata = static_cast<char*>(data);
diff --git a/wpiutil/src/main/native/cpp/raw_socket_istream.cpp b/wpiutil/src/main/native/cpp/raw_socket_istream.cpp
index 68c4dd6..c6b759e 100644
--- a/wpiutil/src/main/native/cpp/raw_socket_istream.cpp
+++ b/wpiutil/src/main/native/cpp/raw_socket_istream.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2015-2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/raw_socket_istream.h"
@@ -27,6 +24,10 @@
set_read_count(pos);
}
-void raw_socket_istream::close() { m_stream.close(); }
+void raw_socket_istream::close() {
+ m_stream.close();
+}
-size_t raw_socket_istream::in_avail() const { return 0; }
+size_t raw_socket_istream::in_avail() const {
+ return 0;
+}
diff --git a/wpiutil/src/main/native/cpp/raw_socket_ostream.cpp b/wpiutil/src/main/native/cpp/raw_socket_ostream.cpp
index fb8d94d..af01c84 100644
--- a/wpiutil/src/main/native/cpp/raw_socket_ostream.cpp
+++ b/wpiutil/src/main/native/cpp/raw_socket_ostream.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2015-2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/raw_socket_ostream.h"
@@ -13,7 +10,9 @@
raw_socket_ostream::~raw_socket_ostream() {
flush();
- if (m_shouldClose) close();
+ if (m_shouldClose) {
+ close();
+ }
}
void raw_socket_ostream::write_impl(const char* data, size_t len) {
@@ -30,10 +29,14 @@
}
}
-uint64_t raw_socket_ostream::current_pos() const { return 0; }
+uint64_t raw_socket_ostream::current_pos() const {
+ return 0;
+}
void raw_socket_ostream::close() {
- if (!m_shouldClose) return;
+ if (!m_shouldClose) {
+ return;
+ }
flush();
m_stream.close();
}
diff --git a/wpiutil/src/main/native/cpp/raw_uv_ostream.cpp b/wpiutil/src/main/native/cpp/raw_uv_ostream.cpp
index 8fc103d..f055a2a 100644
--- a/wpiutil/src/main/native/cpp/raw_uv_ostream.cpp
+++ b/wpiutil/src/main/native/cpp/raw_uv_ostream.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/raw_uv_ostream.h"
@@ -35,6 +32,8 @@
uint64_t raw_uv_ostream::current_pos() const {
uint64_t size = 0;
- for (auto&& buf : m_bufs) size += buf.len;
+ for (auto&& buf : m_bufs) {
+ size += buf.len;
+ }
return size;
}
diff --git a/wpiutil/src/main/native/cpp/sendable/SendableRegistry.cpp b/wpiutil/src/main/native/cpp/sendable/SendableRegistry.cpp
new file mode 100644
index 0000000..3ad39e5
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/sendable/SendableRegistry.cpp
@@ -0,0 +1,434 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/sendable/SendableRegistry.h"
+
+#include <memory>
+
+#include "fmt/format.h"
+#include "wpi/DenseMap.h"
+#include "wpi/SmallVector.h"
+#include "wpi/UidVector.h"
+#include "wpi/mutex.h"
+#include "wpi/sendable/Sendable.h"
+#include "wpi/sendable/SendableBuilder.h"
+
+using namespace wpi;
+
+namespace {
+struct Component {
+ Sendable* sendable = nullptr;
+ std::unique_ptr<SendableBuilder> builder;
+ std::string name;
+ std::string subsystem = "Ungrouped";
+ Sendable* parent = nullptr;
+ bool liveWindow = false;
+ wpi::SmallVector<std::shared_ptr<void>, 2> data;
+
+ void SetName(std::string_view moduleType, int channel) {
+ name = fmt::format("{}[{}]", moduleType, channel);
+ }
+
+ void SetName(std::string_view moduleType, int moduleNumber, int channel) {
+ name = fmt::format("{}[{},{}]", moduleType, moduleNumber, channel);
+ }
+};
+
+struct SendableRegistryInst {
+ wpi::recursive_mutex mutex;
+
+ std::function<std::unique_ptr<SendableBuilder>()> liveWindowFactory;
+ wpi::UidVector<std::unique_ptr<Component>, 32> components;
+ wpi::DenseMap<void*, SendableRegistry::UID> componentMap;
+ int nextDataHandle = 0;
+
+ Component& GetOrAdd(void* sendable, SendableRegistry::UID* uid = nullptr);
+};
+} // namespace
+
+Component& SendableRegistryInst::GetOrAdd(void* sendable,
+ SendableRegistry::UID* uid) {
+ SendableRegistry::UID& compUid = componentMap[sendable];
+ if (compUid == 0) {
+ compUid = components.emplace_back(std::make_unique<Component>()) + 1;
+ }
+ if (uid) {
+ *uid = compUid;
+ }
+
+ return *components[compUid - 1];
+}
+
+static SendableRegistryInst& GetInstance() {
+ static SendableRegistryInst instance;
+ return instance;
+}
+
+void SendableRegistry::SetLiveWindowBuilderFactory(
+ std::function<std::unique_ptr<SendableBuilder>()> factory) {
+ GetInstance().liveWindowFactory = std::move(factory);
+}
+
+void SendableRegistry::Add(Sendable* sendable, std::string_view name) {
+ auto& inst = GetInstance();
+ std::scoped_lock lock(inst.mutex);
+ auto& comp = inst.GetOrAdd(sendable);
+ comp.sendable = sendable;
+ comp.name = name;
+}
+
+void SendableRegistry::Add(Sendable* sendable, std::string_view moduleType,
+ int channel) {
+ auto& inst = GetInstance();
+ std::scoped_lock lock(inst.mutex);
+ auto& comp = inst.GetOrAdd(sendable);
+ comp.sendable = sendable;
+ comp.SetName(moduleType, channel);
+}
+
+void SendableRegistry::Add(Sendable* sendable, std::string_view moduleType,
+ int moduleNumber, int channel) {
+ auto& inst = GetInstance();
+ std::scoped_lock lock(inst.mutex);
+ auto& comp = inst.GetOrAdd(sendable);
+ comp.sendable = sendable;
+ comp.SetName(moduleType, moduleNumber, channel);
+}
+
+void SendableRegistry::Add(Sendable* sendable, std::string_view subsystem,
+ std::string_view name) {
+ auto& inst = GetInstance();
+ std::scoped_lock lock(inst.mutex);
+ auto& comp = inst.GetOrAdd(sendable);
+ comp.sendable = sendable;
+ comp.name = name;
+ comp.subsystem = subsystem;
+}
+
+void SendableRegistry::AddLW(Sendable* sendable, std::string_view name) {
+ auto& inst = GetInstance();
+ std::scoped_lock lock(inst.mutex);
+ auto& comp = inst.GetOrAdd(sendable);
+ comp.sendable = sendable;
+ if (inst.liveWindowFactory) {
+ comp.builder = inst.liveWindowFactory();
+ }
+ comp.liveWindow = true;
+ comp.name = name;
+}
+
+void SendableRegistry::AddLW(Sendable* sendable, std::string_view moduleType,
+ int channel) {
+ auto& inst = GetInstance();
+ std::scoped_lock lock(inst.mutex);
+ auto& comp = inst.GetOrAdd(sendable);
+ comp.sendable = sendable;
+ if (inst.liveWindowFactory) {
+ comp.builder = inst.liveWindowFactory();
+ }
+ comp.liveWindow = true;
+ comp.SetName(moduleType, channel);
+}
+
+void SendableRegistry::AddLW(Sendable* sendable, std::string_view moduleType,
+ int moduleNumber, int channel) {
+ auto& inst = GetInstance();
+ std::scoped_lock lock(inst.mutex);
+ auto& comp = inst.GetOrAdd(sendable);
+ comp.sendable = sendable;
+ if (inst.liveWindowFactory) {
+ comp.builder = inst.liveWindowFactory();
+ }
+ comp.liveWindow = true;
+ comp.SetName(moduleType, moduleNumber, channel);
+}
+
+void SendableRegistry::AddLW(Sendable* sendable, std::string_view subsystem,
+ std::string_view name) {
+ auto& inst = GetInstance();
+ std::scoped_lock lock(inst.mutex);
+ auto& comp = inst.GetOrAdd(sendable);
+ comp.sendable = sendable;
+ if (inst.liveWindowFactory) {
+ comp.builder = inst.liveWindowFactory();
+ }
+ comp.liveWindow = true;
+ comp.name = name;
+ comp.subsystem = subsystem;
+}
+
+void SendableRegistry::AddChild(Sendable* parent, Sendable* child) {
+ auto& inst = GetInstance();
+ std::scoped_lock lock(inst.mutex);
+ auto& comp = inst.GetOrAdd(child);
+ comp.parent = parent;
+}
+
+void SendableRegistry::AddChild(Sendable* parent, void* child) {
+ auto& inst = GetInstance();
+ std::scoped_lock lock(inst.mutex);
+ auto& comp = inst.GetOrAdd(child);
+ comp.parent = parent;
+}
+
+bool SendableRegistry::Remove(Sendable* sendable) {
+ auto& inst = GetInstance();
+ std::scoped_lock lock(inst.mutex);
+ auto it = inst.componentMap.find(sendable);
+ if (it == inst.componentMap.end()) {
+ return false;
+ }
+ UID compUid = it->getSecond();
+ inst.components.erase(compUid - 1);
+ inst.componentMap.erase(it);
+ // update any parent pointers
+ for (auto&& comp : inst.components) {
+ if (comp->parent == sendable) {
+ comp->parent = nullptr;
+ }
+ }
+ return true;
+}
+
+void SendableRegistry::Move(Sendable* to, Sendable* from) {
+ auto& inst = GetInstance();
+ std::scoped_lock lock(inst.mutex);
+ auto it = inst.componentMap.find(from);
+ if (it == inst.componentMap.end() || !inst.components[it->getSecond() - 1]) {
+ return;
+ }
+ UID compUid = it->getSecond();
+ inst.componentMap.erase(it);
+ inst.componentMap[to] = compUid;
+ auto& comp = *inst.components[compUid - 1];
+ comp.sendable = to;
+ if (comp.builder && comp.builder->IsPublished()) {
+ // rebuild builder, as lambda captures can point to "from"
+ comp.builder->ClearProperties();
+ to->InitSendable(*comp.builder);
+ }
+ // update any parent pointers
+ for (auto&& comp : inst.components) {
+ if (comp->parent == from) {
+ comp->parent = to;
+ }
+ }
+}
+
+bool SendableRegistry::Contains(const Sendable* sendable) {
+ auto& inst = GetInstance();
+ std::scoped_lock lock(inst.mutex);
+ return inst.componentMap.count(sendable) != 0;
+}
+
+std::string SendableRegistry::GetName(const Sendable* sendable) {
+ auto& inst = GetInstance();
+ std::scoped_lock lock(inst.mutex);
+ auto it = inst.componentMap.find(sendable);
+ if (it == inst.componentMap.end() || !inst.components[it->getSecond() - 1]) {
+ return {};
+ }
+ return inst.components[it->getSecond() - 1]->name;
+}
+
+void SendableRegistry::SetName(Sendable* sendable, std::string_view name) {
+ auto& inst = GetInstance();
+ std::scoped_lock lock(inst.mutex);
+ auto it = inst.componentMap.find(sendable);
+ if (it == inst.componentMap.end() || !inst.components[it->getSecond() - 1]) {
+ return;
+ }
+ inst.components[it->getSecond() - 1]->name = name;
+}
+
+void SendableRegistry::SetName(Sendable* sendable, std::string_view moduleType,
+ int channel) {
+ auto& inst = GetInstance();
+ std::scoped_lock lock(inst.mutex);
+ auto it = inst.componentMap.find(sendable);
+ if (it == inst.componentMap.end() || !inst.components[it->getSecond() - 1]) {
+ return;
+ }
+ inst.components[it->getSecond() - 1]->SetName(moduleType, channel);
+}
+
+void SendableRegistry::SetName(Sendable* sendable, std::string_view moduleType,
+ int moduleNumber, int channel) {
+ auto& inst = GetInstance();
+ std::scoped_lock lock(inst.mutex);
+ auto it = inst.componentMap.find(sendable);
+ if (it == inst.componentMap.end() || !inst.components[it->getSecond() - 1]) {
+ return;
+ }
+ inst.components[it->getSecond() - 1]->SetName(moduleType, moduleNumber,
+ channel);
+}
+
+void SendableRegistry::SetName(Sendable* sendable, std::string_view subsystem,
+ std::string_view name) {
+ auto& inst = GetInstance();
+ std::scoped_lock lock(inst.mutex);
+ auto it = inst.componentMap.find(sendable);
+ if (it == inst.componentMap.end() || !inst.components[it->getSecond() - 1]) {
+ return;
+ }
+ auto& comp = *inst.components[it->getSecond() - 1];
+ comp.name = name;
+ comp.subsystem = subsystem;
+}
+
+std::string SendableRegistry::GetSubsystem(const Sendable* sendable) {
+ auto& inst = GetInstance();
+ std::scoped_lock lock(inst.mutex);
+ auto it = inst.componentMap.find(sendable);
+ if (it == inst.componentMap.end() || !inst.components[it->getSecond() - 1]) {
+ return {};
+ }
+ return inst.components[it->getSecond() - 1]->subsystem;
+}
+
+void SendableRegistry::SetSubsystem(Sendable* sendable,
+ std::string_view subsystem) {
+ auto& inst = GetInstance();
+ std::scoped_lock lock(inst.mutex);
+ auto it = inst.componentMap.find(sendable);
+ if (it == inst.componentMap.end() || !inst.components[it->getSecond() - 1]) {
+ return;
+ }
+ inst.components[it->getSecond() - 1]->subsystem = subsystem;
+}
+
+int SendableRegistry::GetDataHandle() {
+ auto& inst = GetInstance();
+ std::scoped_lock lock(inst.mutex);
+ return inst.nextDataHandle++;
+}
+
+std::shared_ptr<void> SendableRegistry::SetData(Sendable* sendable, int handle,
+ std::shared_ptr<void> data) {
+ auto& inst = GetInstance();
+ assert(handle >= 0);
+ std::scoped_lock lock(inst.mutex);
+ auto it = inst.componentMap.find(sendable);
+ if (it == inst.componentMap.end() || !inst.components[it->getSecond() - 1]) {
+ return nullptr;
+ }
+ auto& comp = *inst.components[it->getSecond() - 1];
+ std::shared_ptr<void> rv;
+ if (static_cast<size_t>(handle) < comp.data.size()) {
+ rv = std::move(comp.data[handle]);
+ } else {
+ comp.data.resize(handle + 1);
+ }
+ comp.data[handle] = std::move(data);
+ return rv;
+}
+
+std::shared_ptr<void> SendableRegistry::GetData(Sendable* sendable,
+ int handle) {
+ auto& inst = GetInstance();
+ assert(handle >= 0);
+ std::scoped_lock lock(inst.mutex);
+ auto it = inst.componentMap.find(sendable);
+ if (it == inst.componentMap.end() || !inst.components[it->getSecond() - 1]) {
+ return nullptr;
+ }
+ auto& comp = *inst.components[it->getSecond() - 1];
+ if (static_cast<size_t>(handle) >= comp.data.size()) {
+ return nullptr;
+ }
+ return comp.data[handle];
+}
+
+void SendableRegistry::EnableLiveWindow(Sendable* sendable) {
+ auto& inst = GetInstance();
+ std::scoped_lock lock(inst.mutex);
+ auto it = inst.componentMap.find(sendable);
+ if (it == inst.componentMap.end() || !inst.components[it->getSecond() - 1]) {
+ return;
+ }
+ inst.components[it->getSecond() - 1]->liveWindow = true;
+}
+
+void SendableRegistry::DisableLiveWindow(Sendable* sendable) {
+ auto& inst = GetInstance();
+ std::scoped_lock lock(inst.mutex);
+ auto it = inst.componentMap.find(sendable);
+ if (it == inst.componentMap.end() || !inst.components[it->getSecond() - 1]) {
+ return;
+ }
+ inst.components[it->getSecond() - 1]->liveWindow = false;
+}
+
+SendableRegistry::UID SendableRegistry::GetUniqueId(Sendable* sendable) {
+ auto& inst = GetInstance();
+ std::scoped_lock lock(inst.mutex);
+ UID uid;
+ auto& comp = inst.GetOrAdd(sendable, &uid);
+ comp.sendable = sendable;
+ return uid;
+}
+
+Sendable* SendableRegistry::GetSendable(UID uid) {
+ auto& inst = GetInstance();
+ if (uid == 0) {
+ return nullptr;
+ }
+ std::scoped_lock lock(inst.mutex);
+ if ((uid - 1) >= inst.components.size() || !inst.components[uid - 1]) {
+ return nullptr;
+ }
+ return inst.components[uid - 1]->sendable;
+}
+
+void SendableRegistry::Publish(UID sendableUid,
+ std::unique_ptr<SendableBuilder> builder) {
+ auto& inst = GetInstance();
+ std::scoped_lock lock(inst.mutex);
+ if (sendableUid == 0 || (sendableUid - 1) >= inst.components.size() ||
+ !inst.components[sendableUid - 1]) {
+ return;
+ }
+ auto& comp = *inst.components[sendableUid - 1];
+ comp.builder = std::move(builder); // clear any current builder
+ comp.sendable->InitSendable(*comp.builder);
+ comp.builder->Update();
+}
+
+void SendableRegistry::Update(UID sendableUid) {
+ auto& inst = GetInstance();
+ if (sendableUid == 0) {
+ return;
+ }
+ std::scoped_lock lock(inst.mutex);
+ if ((sendableUid - 1) >= inst.components.size() ||
+ !inst.components[sendableUid - 1]) {
+ return;
+ }
+ if (inst.components[sendableUid - 1]->builder) {
+ inst.components[sendableUid - 1]->builder->Update();
+ }
+}
+
+void SendableRegistry::ForeachLiveWindow(
+ int dataHandle, wpi::function_ref<void(CallbackData& data)> callback) {
+ auto& inst = GetInstance();
+ assert(dataHandle >= 0);
+ std::scoped_lock lock(inst.mutex);
+ wpi::SmallVector<Component*, 128> components;
+ for (auto&& comp : inst.components) {
+ components.emplace_back(comp.get());
+ }
+ for (auto comp : components) {
+ if (comp && comp->builder && comp->sendable && comp->liveWindow) {
+ if (static_cast<size_t>(dataHandle) >= comp->data.size()) {
+ comp->data.resize(dataHandle + 1);
+ }
+ CallbackData cbdata{comp->sendable, comp->name,
+ comp->subsystem, comp->parent,
+ comp->data[dataHandle], *comp->builder};
+ callback(cbdata);
+ }
+ }
+}
diff --git a/wpiutil/src/main/native/cpp/sha1.cpp b/wpiutil/src/main/native/cpp/sha1.cpp
index e1346f4..8ec7cb1 100644
--- a/wpiutil/src/main/native/cpp/sha1.cpp
+++ b/wpiutil/src/main/native/cpp/sha1.cpp
@@ -212,10 +212,12 @@
}
}
-SHA1::SHA1() { reset(digest, buf_size, transforms); }
+SHA1::SHA1() {
+ reset(digest, buf_size, transforms);
+}
-void SHA1::Update(StringRef s) {
- raw_mem_istream is(makeArrayRef(s.data(), s.size()));
+void SHA1::Update(std::string_view s) {
+ raw_mem_istream is(span<const char>(s.data(), s.size()));
Update(is);
}
@@ -291,7 +293,7 @@
return os.str();
}
-StringRef SHA1::Final(SmallVectorImpl<char>& buf) {
+std::string_view SHA1::Final(SmallVectorImpl<char>& buf) {
raw_svector_ostream os(buf);
finalize(digest, buffer, buf_size, transforms, os, true);
@@ -299,7 +301,7 @@
return os.str();
}
-StringRef SHA1::RawFinal(SmallVectorImpl<char>& buf) {
+std::string_view SHA1::RawFinal(SmallVectorImpl<char>& buf) {
raw_svector_ostream os(buf);
finalize(digest, buffer, buf_size, transforms, os, false);
@@ -307,7 +309,7 @@
return os.str();
}
-std::string SHA1::FromFile(StringRef filename) {
+std::string SHA1::FromFile(std::string_view filename) {
std::error_code ec;
raw_fd_istream stream(filename, ec);
SHA1 checksum;
diff --git a/wpiutil/src/main/native/cpp/timestamp.cpp b/wpiutil/src/main/native/cpp/timestamp.cpp
index 7f3b0cf..9c32bf3 100644
--- a/wpiutil/src/main/native/cpp/timestamp.cpp
+++ b/wpiutil/src/main/native/cpp/timestamp.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2015-2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/timestamp.h"
@@ -19,7 +16,7 @@
#endif
// offset in microseconds
-static uint64_t zerotime() {
+static uint64_t zerotime() noexcept {
#ifdef _WIN32
FILETIME ft;
uint64_t tmpres = 0;
@@ -43,7 +40,7 @@
#endif
}
-static uint64_t timestamp() {
+static uint64_t timestamp() noexcept {
#ifdef _WIN32
LARGE_INTEGER li;
QueryPerformanceCounter(&li);
@@ -95,14 +92,22 @@
now_impl = func ? func : NowDefault;
}
-uint64_t wpi::Now() { return (now_impl.load())(); }
+uint64_t wpi::Now() {
+ return (now_impl.load())();
+}
extern "C" {
-uint64_t WPI_NowDefault(void) { return wpi::NowDefault(); }
+uint64_t WPI_NowDefault(void) {
+ return wpi::NowDefault();
+}
-void WPI_SetNowImpl(uint64_t (*func)(void)) { wpi::SetNowImpl(func); }
+void WPI_SetNowImpl(uint64_t (*func)(void)) {
+ wpi::SetNowImpl(func);
+}
-uint64_t WPI_Now(void) { return wpi::Now(); }
+uint64_t WPI_Now(void) {
+ return wpi::Now();
+}
} // extern "C"
diff --git a/wpiutil/src/main/native/cpp/uv/Async.cpp b/wpiutil/src/main/native/cpp/uv/Async.cpp
index 5479f49..c9d698e 100644
--- a/wpiutil/src/main/native/cpp/uv/Async.cpp
+++ b/wpiutil/src/main/native/cpp/uv/Async.cpp
@@ -1,22 +1,19 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/uv/Async.h"
#include "wpi/uv/Loop.h"
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
Async<>::~Async() noexcept {
- if (auto loop = m_loop.lock())
+ if (auto loop = m_loop.lock()) {
Close();
- else
+ } else {
ForceClosed();
+ }
}
std::shared_ptr<Async<>> Async<>::Create(const std::shared_ptr<Loop>& loop) {
@@ -33,5 +30,4 @@
return h;
}
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv
diff --git a/wpiutil/src/main/native/cpp/uv/Check.cpp b/wpiutil/src/main/native/cpp/uv/Check.cpp
index 0f4cccf..97265d4 100644
--- a/wpiutil/src/main/native/cpp/uv/Check.cpp
+++ b/wpiutil/src/main/native/cpp/uv/Check.cpp
@@ -1,16 +1,12 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/uv/Check.h"
#include "wpi/uv/Loop.h"
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
std::shared_ptr<Check> Check::Create(Loop& loop) {
auto h = std::make_shared<Check>(private_init{});
@@ -30,5 +26,4 @@
});
}
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv
diff --git a/wpiutil/src/main/native/cpp/uv/FsEvent.cpp b/wpiutil/src/main/native/cpp/uv/FsEvent.cpp
index 54ba31f..3c83d1d 100644
--- a/wpiutil/src/main/native/cpp/uv/FsEvent.cpp
+++ b/wpiutil/src/main/native/cpp/uv/FsEvent.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/uv/FsEvent.h"
@@ -12,8 +9,7 @@
#include "wpi/SmallString.h"
#include "wpi/uv/Loop.h"
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
std::shared_ptr<FsEvent> FsEvent::Create(Loop& loop) {
auto h = std::make_shared<FsEvent>(private_init{});
@@ -26,18 +22,19 @@
return h;
}
-void FsEvent::Start(const Twine& path, unsigned int flags) {
- SmallString<128> pathBuf;
+void FsEvent::Start(std::string_view path, unsigned int flags) {
+ SmallString<128> pathBuf{path};
Invoke(
&uv_fs_event_start, GetRaw(),
[](uv_fs_event_t* handle, const char* filename, int events, int status) {
FsEvent& h = *static_cast<FsEvent*>(handle->data);
- if (status < 0)
+ if (status < 0) {
h.ReportError(status);
- else
+ } else {
h.fsEvent(filename, events);
+ }
},
- path.toNullTerminatedStringRef(pathBuf).data(), flags);
+ pathBuf.c_str(), flags);
}
std::string FsEvent::GetPath() {
@@ -63,5 +60,4 @@
return std::string{};
}
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv
diff --git a/wpiutil/src/main/native/cpp/uv/GetAddrInfo.cpp b/wpiutil/src/main/native/cpp/uv/GetAddrInfo.cpp
index 21f6404..2e6e38f 100644
--- a/wpiutil/src/main/native/cpp/uv/GetAddrInfo.cpp
+++ b/wpiutil/src/main/native/cpp/uv/GetAddrInfo.cpp
@@ -1,55 +1,51 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/uv/GetAddrInfo.h"
+#include "wpi/SmallString.h"
#include "wpi/uv/Loop.h"
#include "wpi/uv/util.h"
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
GetAddrInfoReq::GetAddrInfoReq() {
error = [this](Error err) { GetLoop().error(err); };
}
void GetAddrInfo(Loop& loop, const std::shared_ptr<GetAddrInfoReq>& req,
- const Twine& node, const Twine& service,
+ std::string_view node, std::string_view service,
const addrinfo* hints) {
- SmallVector<char, 128> nodeStr;
- SmallVector<char, 128> serviceStr;
+ SmallString<128> nodeStr{node};
+ SmallString<128> serviceStr{service};
int err = uv_getaddrinfo(
loop.GetRaw(), req->GetRaw(),
[](uv_getaddrinfo_t* req, int status, addrinfo* res) {
auto& h = *static_cast<GetAddrInfoReq*>(req->data);
- if (status < 0)
+ if (status < 0) {
h.ReportError(status);
- else
+ } else {
h.resolved(*res);
+ }
uv_freeaddrinfo(res);
h.Release(); // this is always a one-shot
},
- node.isNull() ? nullptr : node.toNullTerminatedStringRef(nodeStr).data(),
- service.isNull() ? nullptr
- : service.toNullTerminatedStringRef(serviceStr).data(),
- hints);
- if (err < 0)
+ node.empty() ? nullptr : nodeStr.c_str(),
+ service.empty() ? nullptr : serviceStr.c_str(), hints);
+ if (err < 0) {
loop.ReportError(err);
- else
+ } else {
req->Keep();
+ }
}
void GetAddrInfo(Loop& loop, std::function<void(const addrinfo&)> callback,
- const Twine& node, const Twine& service,
+ std::string_view node, std::string_view service,
const addrinfo* hints) {
auto req = std::make_shared<GetAddrInfoReq>();
- req->resolved.connect(callback);
+ req->resolved.connect(std::move(callback));
GetAddrInfo(loop, req, node, service, hints);
}
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv
diff --git a/wpiutil/src/main/native/cpp/uv/GetNameInfo.cpp b/wpiutil/src/main/native/cpp/uv/GetNameInfo.cpp
index 6a4446f..4e662f3 100644
--- a/wpiutil/src/main/native/cpp/uv/GetNameInfo.cpp
+++ b/wpiutil/src/main/native/cpp/uv/GetNameInfo.cpp
@@ -1,17 +1,13 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2020 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. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/uv/GetNameInfo.h"
#include "wpi/uv/Loop.h"
#include "wpi/uv/util.h"
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
GetNameInfoReq::GetNameInfoReq() {
error = [this](Error err) { GetLoop().error(err); };
@@ -24,68 +20,75 @@
[](uv_getnameinfo_t* req, int status, const char* hostname,
const char* service) {
auto& h = *static_cast<GetNameInfoReq*>(req->data);
- if (status < 0)
+ if (status < 0) {
h.ReportError(status);
- else
+ } else {
h.resolved(hostname, service);
+ }
h.Release(); // this is always a one-shot
},
&addr, flags);
- if (err < 0)
+ if (err < 0) {
loop.ReportError(err);
- else
+ } else {
req->Keep();
+ }
}
void GetNameInfo(Loop& loop,
std::function<void(const char*, const char*)> callback,
const sockaddr& addr, int flags) {
auto req = std::make_shared<GetNameInfoReq>();
- req->resolved.connect(callback);
+ req->resolved.connect(std::move(callback));
GetNameInfo(loop, req, addr, flags);
}
void GetNameInfo4(Loop& loop, const std::shared_ptr<GetNameInfoReq>& req,
- const Twine& ip, unsigned int port, int flags) {
+ std::string_view ip, unsigned int port, int flags) {
sockaddr_in addr;
int err = NameToAddr(ip, port, &addr);
- if (err < 0)
+ if (err < 0) {
loop.ReportError(err);
- else
+ } else {
GetNameInfo(loop, req, reinterpret_cast<const sockaddr&>(addr), flags);
+ }
}
void GetNameInfo4(Loop& loop,
std::function<void(const char*, const char*)> callback,
- const Twine& ip, unsigned int port, int flags) {
+ std::string_view ip, unsigned int port, int flags) {
sockaddr_in addr;
int err = NameToAddr(ip, port, &addr);
- if (err < 0)
+ if (err < 0) {
loop.ReportError(err);
- else
- GetNameInfo(loop, callback, reinterpret_cast<const sockaddr&>(addr), flags);
+ } else {
+ GetNameInfo(loop, std::move(callback),
+ reinterpret_cast<const sockaddr&>(addr), flags);
+ }
}
void GetNameInfo6(Loop& loop, const std::shared_ptr<GetNameInfoReq>& req,
- const Twine& ip, unsigned int port, int flags) {
+ std::string_view ip, unsigned int port, int flags) {
sockaddr_in6 addr;
int err = NameToAddr(ip, port, &addr);
- if (err < 0)
+ if (err < 0) {
loop.ReportError(err);
- else
+ } else {
GetNameInfo(loop, req, reinterpret_cast<const sockaddr&>(addr), flags);
+ }
}
void GetNameInfo6(Loop& loop,
std::function<void(const char*, const char*)> callback,
- const Twine& ip, unsigned int port, int flags) {
+ std::string_view ip, unsigned int port, int flags) {
sockaddr_in6 addr;
int err = NameToAddr(ip, port, &addr);
- if (err < 0)
+ if (err < 0) {
loop.ReportError(err);
- else
- GetNameInfo(loop, callback, reinterpret_cast<const sockaddr&>(addr), flags);
+ } else {
+ GetNameInfo(loop, std::move(callback),
+ reinterpret_cast<const sockaddr&>(addr), flags);
+ }
}
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv
diff --git a/wpiutil/src/main/native/cpp/uv/Handle.cpp b/wpiutil/src/main/native/cpp/uv/Handle.cpp
index 93ef423..74c4c60 100644
--- a/wpiutil/src/main/native/cpp/uv/Handle.cpp
+++ b/wpiutil/src/main/native/cpp/uv/Handle.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/uv/Handle.h"
@@ -11,9 +8,9 @@
Handle::~Handle() noexcept {
if (!m_closed && m_uv_handle->type != UV_UNKNOWN_HANDLE) {
- uv_close(m_uv_handle, [](uv_handle_t* uv_handle) { delete uv_handle; });
+ uv_close(m_uv_handle, [](uv_handle_t* uv_handle) { std::free(uv_handle); });
} else {
- delete m_uv_handle;
+ std::free(m_uv_handle);
}
}
@@ -33,4 +30,6 @@
*buf = h.m_allocBuf(size);
}
-void Handle::DefaultFreeBuf(Buffer& buf) { buf.Deallocate(); }
+void Handle::DefaultFreeBuf(Buffer& buf) {
+ buf.Deallocate();
+}
diff --git a/wpiutil/src/main/native/cpp/uv/Idle.cpp b/wpiutil/src/main/native/cpp/uv/Idle.cpp
index 9eae218..6bf8602 100644
--- a/wpiutil/src/main/native/cpp/uv/Idle.cpp
+++ b/wpiutil/src/main/native/cpp/uv/Idle.cpp
@@ -1,16 +1,12 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/uv/Idle.h"
#include "wpi/uv/Loop.h"
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
std::shared_ptr<Idle> Idle::Create(Loop& loop) {
auto h = std::make_shared<Idle>(private_init{});
@@ -30,5 +26,4 @@
});
}
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv
diff --git a/wpiutil/src/main/native/cpp/uv/Loop.cpp b/wpiutil/src/main/native/cpp/uv/Loop.cpp
index a31ad09..c5b7163 100644
--- a/wpiutil/src/main/native/cpp/uv/Loop.cpp
+++ b/wpiutil/src/main/native/cpp/uv/Loop.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2020 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. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/uv/Loop.h"
@@ -29,7 +26,9 @@
std::shared_ptr<Loop> Loop::Create() {
auto loop = std::make_shared<Loop>(private_init{});
- if (uv_loop_init(&loop->m_loopStruct) < 0) return nullptr;
+ if (uv_loop_init(&loop->m_loopStruct) < 0) {
+ return nullptr;
+ }
loop->m_loop = &loop->m_loopStruct;
loop->m_loop->data = loop.get();
return loop;
@@ -38,22 +37,26 @@
std::shared_ptr<Loop> Loop::GetDefault() {
static std::shared_ptr<Loop> loop = std::make_shared<Loop>(private_init{});
loop->m_loop = uv_default_loop();
- if (!loop->m_loop) return nullptr;
+ if (!loop->m_loop) {
+ return nullptr;
+ }
loop->m_loop->data = loop.get();
return loop;
}
void Loop::Close() {
int err = uv_loop_close(m_loop);
- if (err < 0) ReportError(err);
+ if (err < 0) {
+ ReportError(err);
+ }
}
-void Loop::Walk(std::function<void(Handle&)> callback) {
+void Loop::Walk(function_ref<void(Handle&)> callback) {
uv_walk(
m_loop,
[](uv_handle_t* handle, void* func) {
auto& h = *static_cast<Handle*>(handle->data);
- auto& f = *static_cast<std::function<void(Handle&)>*>(func);
+ auto& f = *static_cast<function_ref<void(Handle&)>*>(func);
f(h);
},
&callback);
@@ -61,5 +64,7 @@
void Loop::Fork() {
int err = uv_loop_fork(m_loop);
- if (err < 0) ReportError(err);
+ if (err < 0) {
+ ReportError(err);
+ }
}
diff --git a/wpiutil/src/main/native/cpp/uv/NameToAddr.cpp b/wpiutil/src/main/native/cpp/uv/NameToAddr.cpp
index b407c2b..23ec6da 100644
--- a/wpiutil/src/main/native/cpp/uv/NameToAddr.cpp
+++ b/wpiutil/src/main/native/cpp/uv/NameToAddr.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/uv/util.h" // NOLINT(build/include_order)
@@ -11,58 +8,52 @@
#include "wpi/SmallString.h"
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
-int NameToAddr(const Twine& ip, unsigned int port, sockaddr_in* addr) {
- SmallString<128> tmp;
- StringRef ipStr = ip.toNullTerminatedStringRef(tmp);
- if (ipStr.empty()) {
+int NameToAddr(std::string_view ip, unsigned int port, sockaddr_in* addr) {
+ if (ip.empty()) {
std::memset(addr, 0, sizeof(sockaddr_in));
addr->sin_family = PF_INET;
addr->sin_addr.s_addr = INADDR_ANY;
addr->sin_port = htons(port);
return 0;
} else {
- return uv_ip4_addr(ipStr.data(), port, addr);
+ SmallString<128> ipBuf{ip};
+ return uv_ip4_addr(ipBuf.c_str(), port, addr);
}
}
-int NameToAddr(const Twine& ip, unsigned int port, sockaddr_in6* addr) {
- SmallString<128> tmp;
- StringRef ipStr = ip.toNullTerminatedStringRef(tmp);
- if (ipStr.empty()) {
+int NameToAddr(std::string_view ip, unsigned int port, sockaddr_in6* addr) {
+ if (ip.empty()) {
std::memset(addr, 0, sizeof(sockaddr_in6));
addr->sin6_family = PF_INET6;
addr->sin6_addr = in6addr_any;
addr->sin6_port = htons(port);
return 0;
} else {
- return uv_ip6_addr(ipStr.data(), port, addr);
+ SmallString<128> ipBuf{ip};
+ return uv_ip6_addr(ipBuf.c_str(), port, addr);
}
}
-int NameToAddr(const Twine& ip, in_addr* addr) {
- SmallString<128> tmp;
- StringRef ipStr = ip.toNullTerminatedStringRef(tmp);
- if (ipStr.empty()) {
+int NameToAddr(std::string_view ip, in_addr* addr) {
+ if (ip.empty()) {
addr->s_addr = INADDR_ANY;
return 0;
} else {
- return uv_inet_pton(AF_INET, ipStr.data(), addr);
+ SmallString<128> ipBuf{ip};
+ return uv_inet_pton(AF_INET, ipBuf.c_str(), addr);
}
}
-int NameToAddr(const Twine& ip, in6_addr* addr) {
- SmallString<128> tmp;
- StringRef ipStr = ip.toNullTerminatedStringRef(tmp);
- if (ipStr.empty()) {
+int NameToAddr(std::string_view ip, in6_addr* addr) {
+ if (ip.empty()) {
*addr = in6addr_any;
return 0;
} else {
- return uv_inet_pton(AF_INET6, ipStr.data(), addr);
+ SmallString<128> ipBuf{ip};
+ return uv_inet_pton(AF_INET6, ipBuf.c_str(), addr);
}
}
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv
diff --git a/wpiutil/src/main/native/cpp/uv/NetworkStream.cpp b/wpiutil/src/main/native/cpp/uv/NetworkStream.cpp
index 6e327a7..0bc3337 100644
--- a/wpiutil/src/main/native/cpp/uv/NetworkStream.cpp
+++ b/wpiutil/src/main/native/cpp/uv/NetworkStream.cpp
@@ -1,14 +1,10 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/uv/NetworkStream.h"
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
ConnectReq::ConnectReq() {
error = [this](Error err) { GetStream().error(err); };
@@ -18,17 +14,17 @@
Invoke(&uv_listen, GetRawStream(), backlog,
[](uv_stream_t* handle, int status) {
auto& h = *static_cast<NetworkStream*>(handle->data);
- if (status < 0)
+ if (status < 0) {
h.ReportError(status);
- else
+ } else {
h.connection();
+ }
});
}
void NetworkStream::Listen(std::function<void()> callback, int backlog) {
- connection.connect(callback);
+ connection.connect(std::move(callback));
Listen(backlog);
}
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv
diff --git a/wpiutil/src/main/native/cpp/uv/Pipe.cpp b/wpiutil/src/main/native/cpp/uv/Pipe.cpp
index 9db879a..b5ca673 100644
--- a/wpiutil/src/main/native/cpp/uv/Pipe.cpp
+++ b/wpiutil/src/main/native/cpp/uv/Pipe.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/uv/Pipe.h"
@@ -11,8 +8,7 @@
#include "wpi/SmallString.h"
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
std::shared_ptr<Pipe> Pipe::Create(Loop& loop, bool ipc) {
auto h = std::make_shared<Pipe>(private_init{});
@@ -26,13 +22,19 @@
}
void Pipe::Reuse(std::function<void()> callback, bool ipc) {
- if (IsClosing()) return;
- if (!m_reuseData) m_reuseData = std::make_unique<ReuseData>();
- m_reuseData->callback = callback;
+ if (IsClosing()) {
+ return;
+ }
+ if (!m_reuseData) {
+ m_reuseData = std::make_unique<ReuseData>();
+ }
+ m_reuseData->callback = std::move(callback);
m_reuseData->ipc = ipc;
uv_close(GetRawHandle(), [](uv_handle_t* handle) {
Pipe& h = *static_cast<Pipe*>(handle->data);
- if (!h.m_reuseData) return;
+ if (!h.m_reuseData) {
+ return;
+ }
auto data = std::move(h.m_reuseData);
auto err =
uv_pipe_init(h.GetLoopRef().GetRaw(), h.GetRaw(), data->ipc ? 1 : 0);
@@ -46,7 +48,9 @@
std::shared_ptr<Pipe> Pipe::Accept() {
auto client = Create(GetLoopRef(), GetRaw()->ipc);
- if (!client) return nullptr;
+ if (!client) {
+ return nullptr;
+ }
if (!Accept(client)) {
client->Release();
return nullptr;
@@ -54,33 +58,34 @@
return client;
}
-Pipe* Pipe::DoAccept() { return Accept().get(); }
-
-void Pipe::Bind(const Twine& name) {
- SmallString<128> nameBuf;
- Invoke(&uv_pipe_bind, GetRaw(),
- name.toNullTerminatedStringRef(nameBuf).data());
+Pipe* Pipe::DoAccept() {
+ return Accept().get();
}
-void Pipe::Connect(const Twine& name,
+void Pipe::Bind(std::string_view name) {
+ SmallString<128> nameBuf{name};
+ Invoke(&uv_pipe_bind, GetRaw(), nameBuf.c_str());
+}
+
+void Pipe::Connect(std::string_view name,
const std::shared_ptr<PipeConnectReq>& req) {
- SmallString<128> nameBuf;
- uv_pipe_connect(req->GetRaw(), GetRaw(),
- name.toNullTerminatedStringRef(nameBuf).data(),
+ SmallString<128> nameBuf{name};
+ uv_pipe_connect(req->GetRaw(), GetRaw(), nameBuf.c_str(),
[](uv_connect_t* req, int status) {
auto& h = *static_cast<PipeConnectReq*>(req->data);
- if (status < 0)
+ if (status < 0) {
h.ReportError(status);
- else
+ } else {
h.connected();
+ }
h.Release(); // this is always a one-shot
});
req->Keep();
}
-void Pipe::Connect(const Twine& name, std::function<void()> callback) {
+void Pipe::Connect(std::string_view name, std::function<void()> callback) {
auto req = std::make_shared<PipeConnectReq>();
- req->connected.connect(callback);
+ req->connected.connect(std::move(callback));
Connect(name, req);
}
@@ -130,5 +135,4 @@
return std::string{};
}
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv
diff --git a/wpiutil/src/main/native/cpp/uv/Poll.cpp b/wpiutil/src/main/native/cpp/uv/Poll.cpp
index 8b608cb..090a40b 100644
--- a/wpiutil/src/main/native/cpp/uv/Poll.cpp
+++ b/wpiutil/src/main/native/cpp/uv/Poll.cpp
@@ -1,16 +1,12 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/uv/Poll.h"
#include "wpi/uv/Loop.h"
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
std::shared_ptr<Poll> Poll::Create(Loop& loop, int fd) {
auto h = std::make_shared<Poll>(private_init{});
@@ -35,14 +31,20 @@
}
void Poll::Reuse(int fd, std::function<void()> callback) {
- if (IsClosing()) return;
- if (!m_reuseData) m_reuseData = std::make_unique<ReuseData>();
- m_reuseData->callback = callback;
+ if (IsClosing()) {
+ return;
+ }
+ if (!m_reuseData) {
+ m_reuseData = std::make_unique<ReuseData>();
+ }
+ m_reuseData->callback = std::move(callback);
m_reuseData->isSocket = false;
m_reuseData->fd = fd;
uv_close(GetRawHandle(), [](uv_handle_t* handle) {
Poll& h = *static_cast<Poll*>(handle->data);
- if (!h.m_reuseData || h.m_reuseData->isSocket) return; // just in case
+ if (!h.m_reuseData || h.m_reuseData->isSocket) {
+ return; // just in case
+ }
auto data = std::move(h.m_reuseData);
int err = uv_poll_init(h.GetLoopRef().GetRaw(), h.GetRaw(), data->fd);
if (err < 0) {
@@ -54,14 +56,20 @@
}
void Poll::ReuseSocket(uv_os_sock_t sock, std::function<void()> callback) {
- if (IsClosing()) return;
- if (!m_reuseData) m_reuseData = std::make_unique<ReuseData>();
- m_reuseData->callback = callback;
+ if (IsClosing()) {
+ return;
+ }
+ if (!m_reuseData) {
+ m_reuseData = std::make_unique<ReuseData>();
+ }
+ m_reuseData->callback = std::move(callback);
m_reuseData->isSocket = true;
m_reuseData->sock = sock;
uv_close(GetRawHandle(), [](uv_handle_t* handle) {
Poll& h = *static_cast<Poll*>(handle->data);
- if (!h.m_reuseData || !h.m_reuseData->isSocket) return; // just in case
+ if (!h.m_reuseData || !h.m_reuseData->isSocket) {
+ return; // just in case
+ }
auto data = std::move(h.m_reuseData);
int err = uv_poll_init(h.GetLoopRef().GetRaw(), h.GetRaw(), data->sock);
if (err < 0) {
@@ -76,12 +84,12 @@
Invoke(&uv_poll_start, GetRaw(), events,
[](uv_poll_t* handle, int status, int events) {
Poll& h = *static_cast<Poll*>(handle->data);
- if (status < 0)
+ if (status < 0) {
h.ReportError(status);
- else
+ } else {
h.pollEvent(events);
+ }
});
}
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv
diff --git a/wpiutil/src/main/native/cpp/uv/Prepare.cpp b/wpiutil/src/main/native/cpp/uv/Prepare.cpp
index f27f477..048fd08 100644
--- a/wpiutil/src/main/native/cpp/uv/Prepare.cpp
+++ b/wpiutil/src/main/native/cpp/uv/Prepare.cpp
@@ -1,16 +1,12 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/uv/Prepare.h"
#include "wpi/uv/Loop.h"
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
std::shared_ptr<Prepare> Prepare::Create(Loop& loop) {
auto h = std::make_shared<Prepare>(private_init{});
@@ -30,5 +26,4 @@
});
}
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv
diff --git a/wpiutil/src/main/native/cpp/uv/Process.cpp b/wpiutil/src/main/native/cpp/uv/Process.cpp
index 5778965..c8d5229 100644
--- a/wpiutil/src/main/native/cpp/uv/Process.cpp
+++ b/wpiutil/src/main/native/cpp/uv/Process.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/uv/Process.h"
@@ -11,11 +8,10 @@
#include "wpi/uv/Loop.h"
#include "wpi/uv/Pipe.h"
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
-std::shared_ptr<Process> Process::SpawnArray(Loop& loop, const Twine& file,
- ArrayRef<Option> options) {
+std::shared_ptr<Process> Process::SpawnArray(Loop& loop, std::string_view file,
+ span<const Option> options) {
// convert Option array to libuv structure
uv_process_options_t coptions;
@@ -24,8 +20,8 @@
h.exited(status, signal);
};
- SmallString<128> fileBuf;
- coptions.file = file.toNullTerminatedStringRef(fileBuf).data();
+ SmallString<128> fileBuf{file};
+ coptions.file = fileBuf.c_str();
coptions.cwd = nullptr;
coptions.flags = 0;
coptions.uid = 0;
@@ -68,28 +64,36 @@
break;
case Option::kStdioIgnore: {
size_t index = o.m_data.stdio.index;
- if (index >= stdioBuf.size()) stdioBuf.resize(index + 1);
+ if (index >= stdioBuf.size()) {
+ stdioBuf.resize(index + 1);
+ }
stdioBuf[index].flags = UV_IGNORE;
stdioBuf[index].data.fd = 0;
break;
}
case Option::kStdioInheritFd: {
size_t index = o.m_data.stdio.index;
- if (index >= stdioBuf.size()) stdioBuf.resize(index + 1);
+ if (index >= stdioBuf.size()) {
+ stdioBuf.resize(index + 1);
+ }
stdioBuf[index].flags = UV_INHERIT_FD;
stdioBuf[index].data.fd = o.m_data.stdio.fd;
break;
}
case Option::kStdioInheritPipe: {
size_t index = o.m_data.stdio.index;
- if (index >= stdioBuf.size()) stdioBuf.resize(index + 1);
+ if (index >= stdioBuf.size()) {
+ stdioBuf.resize(index + 1);
+ }
stdioBuf[index].flags = UV_INHERIT_STREAM;
stdioBuf[index].data.stream = o.m_data.stdio.pipe->GetRawStream();
break;
}
case Option::kStdioCreatePipe: {
size_t index = o.m_data.stdio.index;
- if (index >= stdioBuf.size()) stdioBuf.resize(index + 1);
+ if (index >= stdioBuf.size()) {
+ stdioBuf.resize(index + 1);
+ }
stdioBuf[index].flags =
static_cast<uv_stdio_flags>(UV_CREATE_PIPE | o.m_data.stdio.flags);
stdioBuf[index].data.stream = o.m_data.stdio.pipe->GetRawStream();
@@ -100,7 +104,9 @@
}
}
- if (argsBuf.empty()) argsBuf.push_back(const_cast<char*>(coptions.file));
+ if (argsBuf.empty()) {
+ argsBuf.push_back(const_cast<char*>(coptions.file));
+ }
argsBuf.push_back(nullptr);
coptions.args = argsBuf.data();
@@ -124,5 +130,4 @@
return h;
}
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv
diff --git a/wpiutil/src/main/native/cpp/uv/Signal.cpp b/wpiutil/src/main/native/cpp/uv/Signal.cpp
index 4569ac2..81d7c3e 100644
--- a/wpiutil/src/main/native/cpp/uv/Signal.cpp
+++ b/wpiutil/src/main/native/cpp/uv/Signal.cpp
@@ -1,16 +1,12 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2020 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. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/uv/Signal.h"
#include "wpi/uv/Loop.h"
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
std::shared_ptr<Signal> Signal::Create(Loop& loop) {
auto h = std::make_shared<Signal>(private_init{});
@@ -33,5 +29,4 @@
signum);
}
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv
diff --git a/wpiutil/src/main/native/cpp/uv/Stream.cpp b/wpiutil/src/main/native/cpp/uv/Stream.cpp
index b1fd294..a37750b 100644
--- a/wpiutil/src/main/native/cpp/uv/Stream.cpp
+++ b/wpiutil/src/main/native/cpp/uv/Stream.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/uv/Stream.h"
@@ -15,10 +12,11 @@
namespace {
class CallbackWriteReq : public WriteReq {
public:
- CallbackWriteReq(ArrayRef<Buffer> bufs,
- std::function<void(MutableArrayRef<Buffer>, Error)> callback)
+ CallbackWriteReq(span<const Buffer> bufs,
+ std::function<void(span<Buffer>, Error)> callback)
: m_bufs{bufs.begin(), bufs.end()} {
- finish.connect([=](Error err) { callback(m_bufs, err); });
+ finish.connect(
+ [this, f = std::move(callback)](Error err) { f(m_bufs, err); });
}
private:
@@ -26,8 +24,7 @@
};
} // namespace
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
ShutdownReq::ShutdownReq() {
error = [this](Error err) { GetStream().error(err); };
@@ -41,18 +38,22 @@
if (Invoke(&uv_shutdown, req->GetRaw(), GetRawStream(),
[](uv_shutdown_t* req, int status) {
auto& h = *static_cast<ShutdownReq*>(req->data);
- if (status < 0)
+ if (status < 0) {
h.ReportError(status);
- else
+ } else {
h.complete();
+ }
h.Release(); // this is always a one-shot
- }))
+ })) {
req->Keep();
+ }
}
void Stream::Shutdown(std::function<void()> callback) {
auto req = std::make_shared<ShutdownReq>();
- if (callback) req->complete.connect(callback);
+ if (callback) {
+ req->complete.connect(std::move(callback));
+ }
Shutdown(req);
}
@@ -63,37 +64,40 @@
Buffer data = *buf;
// nread=0 is simply ignored
- if (nread == UV_EOF)
+ if (nread == UV_EOF) {
h.end();
- else if (nread > 0)
+ } else if (nread > 0) {
h.data(data, static_cast<size_t>(nread));
- else if (nread < 0)
+ } else if (nread < 0) {
h.ReportError(nread);
+ }
// free the buffer
h.FreeBuf(data);
});
}
-void Stream::Write(ArrayRef<Buffer> bufs,
+void Stream::Write(span<const Buffer> bufs,
const std::shared_ptr<WriteReq>& req) {
if (Invoke(&uv_write, req->GetRaw(), GetRawStream(), bufs.data(), bufs.size(),
[](uv_write_t* r, int status) {
auto& h = *static_cast<WriteReq*>(r->data);
- if (status < 0) h.ReportError(status);
+ if (status < 0) {
+ h.ReportError(status);
+ }
h.finish(Error(status));
h.Release(); // this is always a one-shot
- }))
+ })) {
req->Keep();
+ }
}
-void Stream::Write(
- ArrayRef<Buffer> bufs,
- std::function<void(MutableArrayRef<Buffer>, Error)> callback) {
- Write(bufs, std::make_shared<CallbackWriteReq>(bufs, callback));
+void Stream::Write(span<const Buffer> bufs,
+ std::function<void(span<Buffer>, Error)> callback) {
+ Write(bufs, std::make_shared<CallbackWriteReq>(bufs, std::move(callback)));
}
-int Stream::TryWrite(ArrayRef<Buffer> bufs) {
+int Stream::TryWrite(span<const Buffer> bufs) {
int val = uv_try_write(GetRawStream(), bufs.data(), bufs.size());
if (val < 0) {
this->ReportError(val);
@@ -102,5 +106,4 @@
return val;
}
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv
diff --git a/wpiutil/src/main/native/cpp/uv/Tcp.cpp b/wpiutil/src/main/native/cpp/uv/Tcp.cpp
index f71e055..6f92557 100644
--- a/wpiutil/src/main/native/cpp/uv/Tcp.cpp
+++ b/wpiutil/src/main/native/cpp/uv/Tcp.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/uv/Tcp.h"
@@ -11,8 +8,7 @@
#include "wpi/uv/util.h"
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
std::shared_ptr<Tcp> Tcp::Create(Loop& loop, unsigned int flags) {
auto h = std::make_shared<Tcp>(private_init{});
@@ -26,13 +22,19 @@
}
void Tcp::Reuse(std::function<void()> callback, unsigned int flags) {
- if (IsClosing()) return;
- if (!m_reuseData) m_reuseData = std::make_unique<ReuseData>();
- m_reuseData->callback = callback;
+ if (IsClosing()) {
+ return;
+ }
+ if (!m_reuseData) {
+ m_reuseData = std::make_unique<ReuseData>();
+ }
+ m_reuseData->callback = std::move(callback);
m_reuseData->flags = flags;
uv_close(GetRawHandle(), [](uv_handle_t* handle) {
Tcp& h = *static_cast<Tcp*>(handle->data);
- if (!h.m_reuseData) return; // just in case
+ if (!h.m_reuseData) {
+ return; // just in case
+ }
auto data = std::move(h.m_reuseData);
int err = uv_tcp_init_ex(h.GetLoopRef().GetRaw(), h.GetRaw(), data->flags);
if (err < 0) {
@@ -45,7 +47,9 @@
std::shared_ptr<Tcp> Tcp::Accept() {
auto client = Create(GetLoopRef());
- if (!client) return nullptr;
+ if (!client) {
+ return nullptr;
+ }
if (!Accept(client)) {
client->Release();
return nullptr;
@@ -53,32 +57,37 @@
return client;
}
-Tcp* Tcp::DoAccept() { return Accept().get(); }
-
-void Tcp::Bind(const Twine& ip, unsigned int port, unsigned int flags) {
- sockaddr_in addr;
- int err = NameToAddr(ip, port, &addr);
- if (err < 0)
- ReportError(err);
- else
- Bind(reinterpret_cast<const sockaddr&>(addr), flags);
+Tcp* Tcp::DoAccept() {
+ return Accept().get();
}
-void Tcp::Bind6(const Twine& ip, unsigned int port, unsigned int flags) {
+void Tcp::Bind(std::string_view ip, unsigned int port, unsigned int flags) {
+ sockaddr_in addr;
+ int err = NameToAddr(ip, port, &addr);
+ if (err < 0) {
+ ReportError(err);
+ } else {
+ Bind(reinterpret_cast<const sockaddr&>(addr), flags);
+ }
+}
+
+void Tcp::Bind6(std::string_view ip, unsigned int port, unsigned int flags) {
sockaddr_in6 addr;
int err = NameToAddr(ip, port, &addr);
- if (err < 0)
+ if (err < 0) {
ReportError(err);
- else
+ } else {
Bind(reinterpret_cast<const sockaddr&>(addr), flags);
+ }
}
sockaddr_storage Tcp::GetSock() {
sockaddr_storage name;
int len = sizeof(name);
if (!Invoke(&uv_tcp_getsockname, GetRaw(), reinterpret_cast<sockaddr*>(&name),
- &len))
+ &len)) {
std::memset(&name, 0, sizeof(name));
+ }
return name;
}
@@ -86,8 +95,9 @@
sockaddr_storage name;
int len = sizeof(name);
if (!Invoke(&uv_tcp_getpeername, GetRaw(), reinterpret_cast<sockaddr*>(&name),
- &len))
+ &len)) {
std::memset(&name, 0, sizeof(name));
+ }
return name;
}
@@ -96,60 +106,65 @@
if (Invoke(&uv_tcp_connect, req->GetRaw(), GetRaw(), &addr,
[](uv_connect_t* req, int status) {
auto& h = *static_cast<TcpConnectReq*>(req->data);
- if (status < 0)
+ if (status < 0) {
h.ReportError(status);
- else
+ } else {
h.connected();
+ }
h.Release(); // this is always a one-shot
- }))
+ })) {
req->Keep();
+ }
}
void Tcp::Connect(const sockaddr& addr, std::function<void()> callback) {
auto req = std::make_shared<TcpConnectReq>();
- req->connected.connect(callback);
+ req->connected.connect(std::move(callback));
Connect(addr, req);
}
-void Tcp::Connect(const Twine& ip, unsigned int port,
+void Tcp::Connect(std::string_view ip, unsigned int port,
const std::shared_ptr<TcpConnectReq>& req) {
sockaddr_in addr;
int err = NameToAddr(ip, port, &addr);
- if (err < 0)
+ if (err < 0) {
ReportError(err);
- else
+ } else {
Connect(reinterpret_cast<const sockaddr&>(addr), req);
+ }
}
-void Tcp::Connect(const Twine& ip, unsigned int port,
+void Tcp::Connect(std::string_view ip, unsigned int port,
std::function<void()> callback) {
sockaddr_in addr;
int err = NameToAddr(ip, port, &addr);
- if (err < 0)
+ if (err < 0) {
ReportError(err);
- else
- Connect(reinterpret_cast<const sockaddr&>(addr), callback);
+ } else {
+ Connect(reinterpret_cast<const sockaddr&>(addr), std::move(callback));
+ }
}
-void Tcp::Connect6(const Twine& ip, unsigned int port,
+void Tcp::Connect6(std::string_view ip, unsigned int port,
const std::shared_ptr<TcpConnectReq>& req) {
sockaddr_in6 addr;
int err = NameToAddr(ip, port, &addr);
- if (err < 0)
+ if (err < 0) {
ReportError(err);
- else
+ } else {
Connect(reinterpret_cast<const sockaddr&>(addr), req);
+ }
}
-void Tcp::Connect6(const Twine& ip, unsigned int port,
+void Tcp::Connect6(std::string_view ip, unsigned int port,
std::function<void()> callback) {
sockaddr_in6 addr;
int err = NameToAddr(ip, port, &addr);
- if (err < 0)
+ if (err < 0) {
ReportError(err);
- else
- Connect(reinterpret_cast<const sockaddr&>(addr), callback);
+ } else {
+ Connect(reinterpret_cast<const sockaddr&>(addr), std::move(callback));
+ }
}
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv
diff --git a/wpiutil/src/main/native/cpp/uv/Timer.cpp b/wpiutil/src/main/native/cpp/uv/Timer.cpp
index 0fdd3be..33fd851 100644
--- a/wpiutil/src/main/native/cpp/uv/Timer.cpp
+++ b/wpiutil/src/main/native/cpp/uv/Timer.cpp
@@ -1,16 +1,12 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2020 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. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/uv/Timer.h"
#include "wpi/uv/Loop.h"
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
std::shared_ptr<Timer> Timer::Create(Loop& loop) {
auto h = std::make_shared<Timer>(private_init{});
@@ -25,9 +21,11 @@
void Timer::SingleShot(Loop& loop, Time timeout, std::function<void()> func) {
auto h = Create(loop);
- if (!h) return;
- h->timeout.connect([theTimer = h.get(), func]() {
- func();
+ if (!h) {
+ return;
+ }
+ h->timeout.connect([theTimer = h.get(), f = std::move(func)]() {
+ f();
theTimer->Close();
});
h->Start(timeout);
@@ -43,5 +41,4 @@
timeout.count(), repeat.count());
}
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv
diff --git a/wpiutil/src/main/native/cpp/uv/Tty.cpp b/wpiutil/src/main/native/cpp/uv/Tty.cpp
index cdd6fd5..4531ded 100644
--- a/wpiutil/src/main/native/cpp/uv/Tty.cpp
+++ b/wpiutil/src/main/native/cpp/uv/Tty.cpp
@@ -1,16 +1,12 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/uv/Tty.h"
#include "wpi/uv/Loop.h"
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
std::shared_ptr<Tty> Tty::Create(Loop& loop, uv_file fd, bool readable) {
auto h = std::make_shared<Tty>(private_init{});
@@ -23,5 +19,4 @@
return h;
}
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv
diff --git a/wpiutil/src/main/native/cpp/uv/Udp.cpp b/wpiutil/src/main/native/cpp/uv/Udp.cpp
index 2c0d29f..bea2b57 100644
--- a/wpiutil/src/main/native/cpp/uv/Udp.cpp
+++ b/wpiutil/src/main/native/cpp/uv/Udp.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/uv/Udp.h"
@@ -20,11 +17,11 @@
class CallbackUdpSendReq : public UdpSendReq {
public:
- CallbackUdpSendReq(
- ArrayRef<Buffer> bufs,
- std::function<void(MutableArrayRef<Buffer>, Error)> callback)
+ CallbackUdpSendReq(span<const Buffer> bufs,
+ std::function<void(span<Buffer>, Error)> callback)
: m_bufs{bufs.begin(), bufs.end()} {
- complete.connect([=](Error err) { callback(m_bufs, err); });
+ complete.connect(
+ [this, f = std::move(callback)](Error err) { f(m_bufs, err); });
}
private:
@@ -33,8 +30,7 @@
} // namespace
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
UdpSendReq::UdpSendReq() {
error = [this](Error err) { GetUdp().error(err); };
@@ -51,48 +47,53 @@
return h;
}
-void Udp::Bind(const Twine& ip, unsigned int port, unsigned int flags) {
+void Udp::Bind(std::string_view ip, unsigned int port, unsigned int flags) {
sockaddr_in addr;
int err = NameToAddr(ip, port, &addr);
- if (err < 0)
+ if (err < 0) {
ReportError(err);
- else
+ } else {
Bind(reinterpret_cast<const sockaddr&>(addr), flags);
+ }
}
-void Udp::Bind6(const Twine& ip, unsigned int port, unsigned int flags) {
+void Udp::Bind6(std::string_view ip, unsigned int port, unsigned int flags) {
sockaddr_in6 addr;
int err = NameToAddr(ip, port, &addr);
- if (err < 0)
+ if (err < 0) {
ReportError(err);
- else
+ } else {
Bind(reinterpret_cast<const sockaddr&>(addr), flags);
+ }
}
-void Udp::Connect(const Twine& ip, unsigned int port) {
+void Udp::Connect(std::string_view ip, unsigned int port) {
sockaddr_in addr;
int err = NameToAddr(ip, port, &addr);
- if (err < 0)
+ if (err < 0) {
ReportError(err);
- else
+ } else {
Connect(reinterpret_cast<const sockaddr&>(addr));
+ }
}
-void Udp::Connect6(const Twine& ip, unsigned int port) {
+void Udp::Connect6(std::string_view ip, unsigned int port) {
sockaddr_in6 addr;
int err = NameToAddr(ip, port, &addr);
- if (err < 0)
+ if (err < 0) {
ReportError(err);
- else
+ } else {
Connect(reinterpret_cast<const sockaddr&>(addr));
+ }
}
sockaddr_storage Udp::GetPeer() {
sockaddr_storage name;
int len = sizeof(name);
if (!Invoke(&uv_udp_getpeername, GetRaw(), reinterpret_cast<sockaddr*>(&name),
- &len))
+ &len)) {
std::memset(&name, 0, sizeof(name));
+ }
return name;
}
@@ -100,58 +101,65 @@
sockaddr_storage name;
int len = sizeof(name);
if (!Invoke(&uv_udp_getsockname, GetRaw(), reinterpret_cast<sockaddr*>(&name),
- &len))
+ &len)) {
std::memset(&name, 0, sizeof(name));
+ }
return name;
}
-void Udp::SetMembership(const Twine& multicastAddr, const Twine& interfaceAddr,
+void Udp::SetMembership(std::string_view multicastAddr,
+ std::string_view interfaceAddr,
uv_membership membership) {
- SmallString<128> multicastAddrBuf;
- SmallString<128> interfaceAddrBuf;
- Invoke(&uv_udp_set_membership, GetRaw(),
- multicastAddr.toNullTerminatedStringRef(multicastAddrBuf).data(),
- interfaceAddr.toNullTerminatedStringRef(interfaceAddrBuf).data(),
- membership);
+ SmallString<128> multicastAddrBuf{multicastAddr};
+ SmallString<128> interfaceAddrBuf{interfaceAddr};
+ Invoke(&uv_udp_set_membership, GetRaw(), multicastAddrBuf.c_str(),
+ interfaceAddrBuf.c_str(), membership);
}
-void Udp::SetMulticastInterface(const Twine& interfaceAddr) {
- SmallString<128> interfaceAddrBuf;
- Invoke(&uv_udp_set_multicast_interface, GetRaw(),
- interfaceAddr.toNullTerminatedStringRef(interfaceAddrBuf).data());
+void Udp::SetMulticastInterface(std::string_view interfaceAddr) {
+ SmallString<128> interfaceAddrBuf{interfaceAddr};
+ Invoke(&uv_udp_set_multicast_interface, GetRaw(), interfaceAddrBuf.c_str());
}
-void Udp::Send(const sockaddr& addr, ArrayRef<Buffer> bufs,
+void Udp::Send(const sockaddr& addr, span<const Buffer> bufs,
const std::shared_ptr<UdpSendReq>& req) {
if (Invoke(&uv_udp_send, req->GetRaw(), GetRaw(), bufs.data(), bufs.size(),
&addr, [](uv_udp_send_t* r, int status) {
auto& h = *static_cast<UdpSendReq*>(r->data);
- if (status < 0) h.ReportError(status);
+ if (status < 0) {
+ h.ReportError(status);
+ }
h.complete(Error(status));
h.Release(); // this is always a one-shot
- }))
+ })) {
req->Keep();
+ }
}
-void Udp::Send(const sockaddr& addr, ArrayRef<Buffer> bufs,
- std::function<void(MutableArrayRef<Buffer>, Error)> callback) {
- Send(addr, bufs, std::make_shared<CallbackUdpSendReq>(bufs, callback));
+void Udp::Send(const sockaddr& addr, span<const Buffer> bufs,
+ std::function<void(span<Buffer>, Error)> callback) {
+ Send(addr, bufs,
+ std::make_shared<CallbackUdpSendReq>(bufs, std::move(callback)));
}
-void Udp::Send(ArrayRef<Buffer> bufs, const std::shared_ptr<UdpSendReq>& req) {
+void Udp::Send(span<const Buffer> bufs,
+ const std::shared_ptr<UdpSendReq>& req) {
if (Invoke(&uv_udp_send, req->GetRaw(), GetRaw(), bufs.data(), bufs.size(),
nullptr, [](uv_udp_send_t* r, int status) {
auto& h = *static_cast<UdpSendReq*>(r->data);
- if (status < 0) h.ReportError(status);
+ if (status < 0) {
+ h.ReportError(status);
+ }
h.complete(Error(status));
h.Release(); // this is always a one-shot
- }))
+ })) {
req->Keep();
+ }
}
-void Udp::Send(ArrayRef<Buffer> bufs,
- std::function<void(MutableArrayRef<Buffer>, Error)> callback) {
- Send(bufs, std::make_shared<CallbackUdpSendReq>(bufs, callback));
+void Udp::Send(span<const Buffer> bufs,
+ std::function<void(span<Buffer>, Error)> callback) {
+ Send(bufs, std::make_shared<CallbackUdpSendReq>(bufs, std::move(callback)));
}
void Udp::StartRecv() {
@@ -162,15 +170,15 @@
Buffer data = *buf;
// nread=0 is simply ignored
- if (nread > 0)
+ if (nread > 0) {
h.received(data, static_cast<size_t>(nread), *addr, flags);
- else if (nread < 0)
+ } else if (nread < 0) {
h.ReportError(nread);
+ }
// free the buffer
h.FreeBuf(data);
});
}
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv
diff --git a/wpiutil/src/main/native/cpp/uv/Work.cpp b/wpiutil/src/main/native/cpp/uv/Work.cpp
index e5d7bb6..0fc254e 100644
--- a/wpiutil/src/main/native/cpp/uv/Work.cpp
+++ b/wpiutil/src/main/native/cpp/uv/Work.cpp
@@ -1,16 +1,12 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2020 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. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/uv/Work.h"
#include "wpi/uv/Loop.h"
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
WorkReq::WorkReq() {
error = [this](Error err) { GetLoop().error(err); };
@@ -25,25 +21,30 @@
},
[](uv_work_t* req, int status) {
auto& h = *static_cast<WorkReq*>(req->data);
- if (status < 0)
+ if (status < 0) {
h.ReportError(status);
- else
+ } else {
h.afterWork();
+ }
h.Release(); // this is always a one-shot
});
- if (err < 0)
+ if (err < 0) {
loop.ReportError(err);
- else
+ } else {
req->Keep();
+ }
}
void QueueWork(Loop& loop, std::function<void()> work,
std::function<void()> afterWork) {
auto req = std::make_shared<WorkReq>();
- if (work) req->work.connect(work);
- if (afterWork) req->afterWork.connect(afterWork);
+ if (work) {
+ req->work.connect(std::move(work));
+ }
+ if (afterWork) {
+ req->afterWork.connect(std::move(afterWork));
+ }
QueueWork(loop, req);
}
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv
diff --git a/wpiutil/src/main/native/fmtlib/include/fmt/args.h b/wpiutil/src/main/native/fmtlib/include/fmt/args.h
new file mode 100644
index 0000000..562e8ab
--- /dev/null
+++ b/wpiutil/src/main/native/fmtlib/include/fmt/args.h
@@ -0,0 +1,232 @@
+// Formatting library for C++ - dynamic format arguments
+//
+// Copyright (c) 2012 - present, Victor Zverovich
+// All rights reserved.
+//
+// For the license information refer to format.h.
+
+#ifndef FMT_ARGS_H_
+#define FMT_ARGS_H_
+
+#include <functional> // std::reference_wrapper
+#include <memory> // std::unique_ptr
+#include <vector>
+
+#include "core.h"
+
+FMT_BEGIN_NAMESPACE
+
+namespace detail {
+
+template <typename T> struct is_reference_wrapper : std::false_type {};
+template <typename T>
+struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {};
+
+template <typename T> const T& unwrap(const T& v) { return v; }
+template <typename T> const T& unwrap(const std::reference_wrapper<T>& v) {
+ return static_cast<const T&>(v);
+}
+
+class dynamic_arg_list {
+ // Workaround for clang's -Wweak-vtables. Unlike for regular classes, for
+ // templates it doesn't complain about inability to deduce single translation
+ // unit for placing vtable. So storage_node_base is made a fake template.
+ template <typename = void> struct node {
+ virtual ~node() = default;
+ std::unique_ptr<node<>> next;
+ };
+
+ template <typename T> struct typed_node : node<> {
+ T value;
+
+ template <typename Arg>
+ FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {}
+
+ template <typename Char>
+ FMT_CONSTEXPR typed_node(const basic_string_view<Char>& arg)
+ : value(arg.data(), arg.size()) {}
+ };
+
+ std::unique_ptr<node<>> head_;
+
+ public:
+ template <typename T, typename Arg> const T& push(const Arg& arg) {
+ auto new_node = std::unique_ptr<typed_node<T>>(new typed_node<T>(arg));
+ auto& value = new_node->value;
+ new_node->next = std::move(head_);
+ head_ = std::move(new_node);
+ return value;
+ }
+};
+} // namespace detail
+
+/**
+ \rst
+ A dynamic version of `fmt::format_arg_store`.
+ It's equipped with a storage to potentially temporary objects which lifetimes
+ could be shorter than the format arguments object.
+
+ It can be implicitly converted into `~fmt::basic_format_args` for passing
+ into type-erased formatting functions such as `~fmt::vformat`.
+ \endrst
+ */
+template <typename Context>
+class dynamic_format_arg_store
+#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
+ // Workaround a GCC template argument substitution bug.
+ : public basic_format_args<Context>
+#endif
+{
+ private:
+ using char_type = typename Context::char_type;
+
+ template <typename T> struct need_copy {
+ static constexpr detail::type mapped_type =
+ detail::mapped_type_constant<T, Context>::value;
+
+ enum {
+ value = !(detail::is_reference_wrapper<T>::value ||
+ std::is_same<T, basic_string_view<char_type>>::value ||
+ std::is_same<T, detail::std_string_view<char_type>>::value ||
+ (mapped_type != detail::type::cstring_type &&
+ mapped_type != detail::type::string_type &&
+ mapped_type != detail::type::custom_type))
+ };
+ };
+
+ template <typename T>
+ using stored_type = conditional_t<detail::is_string<T>::value &&
+ !has_formatter<T, Context>::value &&
+ !detail::is_reference_wrapper<T>::value,
+ std::basic_string<char_type>, T>;
+
+ // Storage of basic_format_arg must be contiguous.
+ std::vector<basic_format_arg<Context>> data_;
+ std::vector<detail::named_arg_info<char_type>> named_info_;
+
+ // Storage of arguments not fitting into basic_format_arg must grow
+ // without relocation because items in data_ refer to it.
+ detail::dynamic_arg_list dynamic_args_;
+
+ friend class basic_format_args<Context>;
+
+ unsigned long long get_types() const {
+ return detail::is_unpacked_bit | data_.size() |
+ (named_info_.empty()
+ ? 0ULL
+ : static_cast<unsigned long long>(detail::has_named_args_bit));
+ }
+
+ const basic_format_arg<Context>* data() const {
+ return named_info_.empty() ? data_.data() : data_.data() + 1;
+ }
+
+ template <typename T> void emplace_arg(const T& arg) {
+ data_.emplace_back(detail::make_arg<Context>(arg));
+ }
+
+ template <typename T>
+ void emplace_arg(const detail::named_arg<char_type, T>& arg) {
+ if (named_info_.empty()) {
+ constexpr const detail::named_arg_info<char_type>* zero_ptr{nullptr};
+ data_.insert(data_.begin(), {zero_ptr, 0});
+ }
+ data_.emplace_back(detail::make_arg<Context>(detail::unwrap(arg.value)));
+ auto pop_one = [](std::vector<basic_format_arg<Context>>* data) {
+ data->pop_back();
+ };
+ std::unique_ptr<std::vector<basic_format_arg<Context>>, decltype(pop_one)>
+ guard{&data_, pop_one};
+ named_info_.push_back({arg.name, static_cast<int>(data_.size() - 2u)});
+ data_[0].value_.named_args = {named_info_.data(), named_info_.size()};
+ guard.release();
+ }
+
+ public:
+ /**
+ \rst
+ Adds an argument into the dynamic store for later passing to a formatting
+ function.
+
+ Note that custom types and string types (but not string views) are copied
+ into the store dynamically allocating memory if necessary.
+
+ **Example**::
+
+ fmt::dynamic_format_arg_store<fmt::format_context> store;
+ store.push_back(42);
+ store.push_back("abc");
+ store.push_back(1.5f);
+ std::string result = fmt::vformat("{} and {} and {}", store);
+ \endrst
+ */
+ template <typename T> void push_back(const T& arg) {
+ if (detail::const_check(need_copy<T>::value))
+ emplace_arg(dynamic_args_.push<stored_type<T>>(arg));
+ else
+ emplace_arg(detail::unwrap(arg));
+ }
+
+ /**
+ \rst
+ Adds a reference to the argument into the dynamic store for later passing to
+ a formatting function.
+
+ **Example**::
+
+ fmt::dynamic_format_arg_store<fmt::format_context> store;
+ char band[] = "Rolling Stones";
+ store.push_back(std::cref(band));
+ band[9] = 'c'; // Changing str affects the output.
+ std::string result = fmt::vformat("{}", store);
+ // result == "Rolling Scones"
+ \endrst
+ */
+ template <typename T> void push_back(std::reference_wrapper<T> arg) {
+ static_assert(
+ need_copy<T>::value,
+ "objects of built-in types and string views are always copied");
+ emplace_arg(arg.get());
+ }
+
+ /**
+ Adds named argument into the dynamic store for later passing to a formatting
+ function. ``std::reference_wrapper`` is supported to avoid copying of the
+ argument. The name is always copied into the store.
+ */
+ template <typename T>
+ void push_back(const detail::named_arg<char_type, T>& arg) {
+ const char_type* arg_name =
+ dynamic_args_.push<std::basic_string<char_type>>(arg.name).c_str();
+ if (detail::const_check(need_copy<T>::value)) {
+ emplace_arg(
+ fmt::arg(arg_name, dynamic_args_.push<stored_type<T>>(arg.value)));
+ } else {
+ emplace_arg(fmt::arg(arg_name, arg.value));
+ }
+ }
+
+ /** Erase all elements from the store */
+ void clear() {
+ data_.clear();
+ named_info_.clear();
+ dynamic_args_ = detail::dynamic_arg_list();
+ }
+
+ /**
+ \rst
+ Reserves space to store at least *new_cap* arguments including
+ *new_cap_named* named arguments.
+ \endrst
+ */
+ void reserve(size_t new_cap, size_t new_cap_named) {
+ FMT_ASSERT(new_cap >= new_cap_named,
+ "Set of arguments includes set of named arguments");
+ data_.reserve(new_cap);
+ named_info_.reserve(new_cap_named);
+ }
+};
+
+FMT_END_NAMESPACE
+
+#endif // FMT_ARGS_H_
diff --git a/wpiutil/src/main/native/fmtlib/include/fmt/chrono.h b/wpiutil/src/main/native/fmtlib/include/fmt/chrono.h
new file mode 100644
index 0000000..c024fd7
--- /dev/null
+++ b/wpiutil/src/main/native/fmtlib/include/fmt/chrono.h
@@ -0,0 +1,1308 @@
+// Formatting library for C++ - chrono support
+//
+// Copyright (c) 2012 - present, Victor Zverovich
+// All rights reserved.
+//
+// For the license information refer to format.h.
+
+#ifndef FMT_CHRONO_H_
+#define FMT_CHRONO_H_
+
+#include <algorithm>
+#include <chrono>
+#include <ctime>
+#include <locale>
+#include <sstream>
+
+#include "format.h"
+
+FMT_BEGIN_NAMESPACE
+
+// Enable safe chrono durations, unless explicitly disabled.
+#ifndef FMT_SAFE_DURATION_CAST
+# define FMT_SAFE_DURATION_CAST 1
+#endif
+#if FMT_SAFE_DURATION_CAST
+
+// For conversion between std::chrono::durations without undefined
+// behaviour or erroneous results.
+// This is a stripped down version of duration_cast, for inclusion in fmt.
+// See https://github.com/pauldreik/safe_duration_cast
+//
+// Copyright Paul Dreik 2019
+namespace safe_duration_cast {
+
+template <typename To, typename From,
+ FMT_ENABLE_IF(!std::is_same<From, To>::value &&
+ std::numeric_limits<From>::is_signed ==
+ std::numeric_limits<To>::is_signed)>
+FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
+ ec = 0;
+ using F = std::numeric_limits<From>;
+ using T = std::numeric_limits<To>;
+ static_assert(F::is_integer, "From must be integral");
+ static_assert(T::is_integer, "To must be integral");
+
+ // A and B are both signed, or both unsigned.
+ if (F::digits <= T::digits) {
+ // From fits in To without any problem.
+ } else {
+ // From does not always fit in To, resort to a dynamic check.
+ if (from < (T::min)() || from > (T::max)()) {
+ // outside range.
+ ec = 1;
+ return {};
+ }
+ }
+ return static_cast<To>(from);
+}
+
+/**
+ * converts From to To, without loss. If the dynamic value of from
+ * can't be converted to To without loss, ec is set.
+ */
+template <typename To, typename From,
+ FMT_ENABLE_IF(!std::is_same<From, To>::value &&
+ std::numeric_limits<From>::is_signed !=
+ std::numeric_limits<To>::is_signed)>
+FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
+ ec = 0;
+ using F = std::numeric_limits<From>;
+ using T = std::numeric_limits<To>;
+ static_assert(F::is_integer, "From must be integral");
+ static_assert(T::is_integer, "To must be integral");
+
+ if (detail::const_check(F::is_signed && !T::is_signed)) {
+ // From may be negative, not allowed!
+ if (fmt::detail::is_negative(from)) {
+ ec = 1;
+ return {};
+ }
+ // From is positive. Can it always fit in To?
+ if (F::digits > T::digits &&
+ from > static_cast<From>(detail::max_value<To>())) {
+ ec = 1;
+ return {};
+ }
+ }
+
+ if (!F::is_signed && T::is_signed && F::digits >= T::digits &&
+ from > static_cast<From>(detail::max_value<To>())) {
+ ec = 1;
+ return {};
+ }
+ return static_cast<To>(from); // Lossless conversion.
+}
+
+template <typename To, typename From,
+ FMT_ENABLE_IF(std::is_same<From, To>::value)>
+FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
+ ec = 0;
+ return from;
+} // function
+
+// clang-format off
+/**
+ * converts From to To if possible, otherwise ec is set.
+ *
+ * input | output
+ * ---------------------------------|---------------
+ * NaN | NaN
+ * Inf | Inf
+ * normal, fits in output | converted (possibly lossy)
+ * normal, does not fit in output | ec is set
+ * subnormal | best effort
+ * -Inf | -Inf
+ */
+// clang-format on
+template <typename To, typename From,
+ FMT_ENABLE_IF(!std::is_same<From, To>::value)>
+FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
+ ec = 0;
+ using T = std::numeric_limits<To>;
+ static_assert(std::is_floating_point<From>::value, "From must be floating");
+ static_assert(std::is_floating_point<To>::value, "To must be floating");
+
+ // catch the only happy case
+ if (std::isfinite(from)) {
+ if (from >= T::lowest() && from <= (T::max)()) {
+ return static_cast<To>(from);
+ }
+ // not within range.
+ ec = 1;
+ return {};
+ }
+
+ // nan and inf will be preserved
+ return static_cast<To>(from);
+} // function
+
+template <typename To, typename From,
+ FMT_ENABLE_IF(std::is_same<From, To>::value)>
+FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
+ ec = 0;
+ static_assert(std::is_floating_point<From>::value, "From must be floating");
+ return from;
+}
+
+/**
+ * safe duration cast between integral durations
+ */
+template <typename To, typename FromRep, typename FromPeriod,
+ FMT_ENABLE_IF(std::is_integral<FromRep>::value),
+ FMT_ENABLE_IF(std::is_integral<typename To::rep>::value)>
+To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
+ int& ec) {
+ using From = std::chrono::duration<FromRep, FromPeriod>;
+ ec = 0;
+ // the basic idea is that we need to convert from count() in the from type
+ // to count() in the To type, by multiplying it with this:
+ struct Factor
+ : std::ratio_divide<typename From::period, typename To::period> {};
+
+ static_assert(Factor::num > 0, "num must be positive");
+ static_assert(Factor::den > 0, "den must be positive");
+
+ // the conversion is like this: multiply from.count() with Factor::num
+ // /Factor::den and convert it to To::rep, all this without
+ // overflow/underflow. let's start by finding a suitable type that can hold
+ // both To, From and Factor::num
+ using IntermediateRep =
+ typename std::common_type<typename From::rep, typename To::rep,
+ decltype(Factor::num)>::type;
+
+ // safe conversion to IntermediateRep
+ IntermediateRep count =
+ lossless_integral_conversion<IntermediateRep>(from.count(), ec);
+ if (ec) return {};
+ // multiply with Factor::num without overflow or underflow
+ if (detail::const_check(Factor::num != 1)) {
+ const auto max1 = detail::max_value<IntermediateRep>() / Factor::num;
+ if (count > max1) {
+ ec = 1;
+ return {};
+ }
+ const auto min1 =
+ (std::numeric_limits<IntermediateRep>::min)() / Factor::num;
+ if (count < min1) {
+ ec = 1;
+ return {};
+ }
+ count *= Factor::num;
+ }
+
+ if (detail::const_check(Factor::den != 1)) count /= Factor::den;
+ auto tocount = lossless_integral_conversion<typename To::rep>(count, ec);
+ return ec ? To() : To(tocount);
+}
+
+/**
+ * safe duration_cast between floating point durations
+ */
+template <typename To, typename FromRep, typename FromPeriod,
+ FMT_ENABLE_IF(std::is_floating_point<FromRep>::value),
+ FMT_ENABLE_IF(std::is_floating_point<typename To::rep>::value)>
+To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
+ int& ec) {
+ using From = std::chrono::duration<FromRep, FromPeriod>;
+ ec = 0;
+ if (std::isnan(from.count())) {
+ // nan in, gives nan out. easy.
+ return To{std::numeric_limits<typename To::rep>::quiet_NaN()};
+ }
+ // maybe we should also check if from is denormal, and decide what to do about
+ // it.
+
+ // +-inf should be preserved.
+ if (std::isinf(from.count())) {
+ return To{from.count()};
+ }
+
+ // the basic idea is that we need to convert from count() in the from type
+ // to count() in the To type, by multiplying it with this:
+ struct Factor
+ : std::ratio_divide<typename From::period, typename To::period> {};
+
+ static_assert(Factor::num > 0, "num must be positive");
+ static_assert(Factor::den > 0, "den must be positive");
+
+ // the conversion is like this: multiply from.count() with Factor::num
+ // /Factor::den and convert it to To::rep, all this without
+ // overflow/underflow. let's start by finding a suitable type that can hold
+ // both To, From and Factor::num
+ using IntermediateRep =
+ typename std::common_type<typename From::rep, typename To::rep,
+ decltype(Factor::num)>::type;
+
+ // force conversion of From::rep -> IntermediateRep to be safe,
+ // even if it will never happen be narrowing in this context.
+ IntermediateRep count =
+ safe_float_conversion<IntermediateRep>(from.count(), ec);
+ if (ec) {
+ return {};
+ }
+
+ // multiply with Factor::num without overflow or underflow
+ if (Factor::num != 1) {
+ constexpr auto max1 = detail::max_value<IntermediateRep>() /
+ static_cast<IntermediateRep>(Factor::num);
+ if (count > max1) {
+ ec = 1;
+ return {};
+ }
+ constexpr auto min1 = std::numeric_limits<IntermediateRep>::lowest() /
+ static_cast<IntermediateRep>(Factor::num);
+ if (count < min1) {
+ ec = 1;
+ return {};
+ }
+ count *= static_cast<IntermediateRep>(Factor::num);
+ }
+
+ // this can't go wrong, right? den>0 is checked earlier.
+ if (Factor::den != 1) {
+ using common_t = typename std::common_type<IntermediateRep, intmax_t>::type;
+ count /= static_cast<common_t>(Factor::den);
+ }
+
+ // convert to the to type, safely
+ using ToRep = typename To::rep;
+
+ const ToRep tocount = safe_float_conversion<ToRep>(count, ec);
+ if (ec) {
+ return {};
+ }
+ return To{tocount};
+}
+} // namespace safe_duration_cast
+#endif
+
+// Prevents expansion of a preceding token as a function-style macro.
+// Usage: f FMT_NOMACRO()
+#define FMT_NOMACRO
+
+namespace detail {
+template <typename T = void> struct null {};
+inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); }
+inline null<> localtime_s(...) { return null<>(); }
+inline null<> gmtime_r(...) { return null<>(); }
+inline null<> gmtime_s(...) { return null<>(); }
+
+inline auto do_write(const std::tm& time, const std::locale& loc, char format,
+ char modifier) -> std::string {
+ auto&& os = std::ostringstream();
+ os.imbue(loc);
+ using iterator = std::ostreambuf_iterator<char>;
+ const auto& facet = std::use_facet<std::time_put<char, iterator>>(loc);
+ auto end = facet.put(os, os, ' ', &time, format, modifier);
+ if (end.failed()) FMT_THROW(format_error("failed to format time"));
+ auto str = os.str();
+ if (!detail::is_utf8() || loc == std::locale::classic()) return str;
+ // char16_t and char32_t codecvts are broken in MSVC (linkage errors) and
+ // gcc-4.
+#if FMT_MSC_VER != 0 || \
+ (defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI))
+ // The _GLIBCXX_USE_DUAL_ABI macro is always defined in libstdc++ from gcc-5
+ // and newer.
+ using code_unit = wchar_t;
+#else
+ using code_unit = char32_t;
+#endif
+ auto& f = std::use_facet<std::codecvt<code_unit, char, std::mbstate_t>>(loc);
+ auto mb = std::mbstate_t();
+ const char* from_next = nullptr;
+ code_unit* to_next = nullptr;
+ constexpr size_t buf_size = 32;
+ code_unit buf[buf_size] = {};
+ auto result = f.in(mb, str.data(), str.data() + str.size(), from_next, buf,
+ buf + buf_size, to_next);
+ if (result != std::codecvt_base::ok)
+ FMT_THROW(format_error("failed to format time"));
+ str.clear();
+ for (code_unit* p = buf; p != to_next; ++p) {
+ uint32_t c = static_cast<uint32_t>(*p);
+ if (sizeof(code_unit) == 2 && c >= 0xd800 && c <= 0xdfff) {
+ // surrogate pair
+ ++p;
+ if (p == to_next || (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) {
+ str.push_back(static_cast<char>(c));
+ } else if (c < 0x800) {
+ str.push_back(static_cast<char>(0xc0 | (c >> 6)));
+ str.push_back(static_cast<char>(0x80 | (c & 0x3f)));
+ } else if ((c >= 0x800 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xffff)) {
+ str.push_back(static_cast<char>(0xe0 | (c >> 12)));
+ str.push_back(static_cast<char>(0x80 | ((c & 0xfff) >> 6)));
+ str.push_back(static_cast<char>(0x80 | (c & 0x3f)));
+ } else if (c >= 0x10000 && c <= 0x10ffff) {
+ str.push_back(static_cast<char>(0xf0 | (c >> 18)));
+ str.push_back(static_cast<char>(0x80 | ((c & 0x3ffff) >> 12)));
+ str.push_back(static_cast<char>(0x80 | ((c & 0xfff) >> 6)));
+ str.push_back(static_cast<char>(0x80 | (c & 0x3f)));
+ } else {
+ FMT_THROW(format_error("failed to format time"));
+ }
+ }
+ return str;
+}
+
+template <typename OutputIt>
+auto write(OutputIt out, const std::tm& time, const std::locale& loc,
+ char format, char modifier = 0) -> OutputIt {
+ auto str = do_write(time, loc, format, modifier);
+ return std::copy(str.begin(), str.end(), out);
+}
+} // namespace detail
+
+FMT_MODULE_EXPORT_BEGIN
+
+/**
+ Converts given time since epoch as ``std::time_t`` value into calendar time,
+ expressed in local time. Unlike ``std::localtime``, this function is
+ thread-safe on most platforms.
+ */
+inline std::tm localtime(std::time_t time) {
+ struct dispatcher {
+ std::time_t time_;
+ std::tm tm_;
+
+ dispatcher(std::time_t t) : time_(t) {}
+
+ bool run() {
+ using namespace fmt::detail;
+ return handle(localtime_r(&time_, &tm_));
+ }
+
+ bool handle(std::tm* tm) { return tm != nullptr; }
+
+ bool handle(detail::null<>) {
+ using namespace fmt::detail;
+ return fallback(localtime_s(&tm_, &time_));
+ }
+
+ bool fallback(int res) { return res == 0; }
+
+#if !FMT_MSC_VER
+ bool fallback(detail::null<>) {
+ using namespace fmt::detail;
+ std::tm* tm = std::localtime(&time_);
+ if (tm) tm_ = *tm;
+ return tm != nullptr;
+ }
+#endif
+ };
+ dispatcher lt(time);
+ // Too big time values may be unsupported.
+ if (!lt.run()) FMT_THROW(format_error("time_t value out of range"));
+ 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));
+}
+
+/**
+ Converts given time since epoch as ``std::time_t`` value into calendar time,
+ expressed in Coordinated Universal Time (UTC). Unlike ``std::gmtime``, this
+ function is thread-safe on most platforms.
+ */
+inline std::tm gmtime(std::time_t time) {
+ struct dispatcher {
+ std::time_t time_;
+ std::tm tm_;
+
+ dispatcher(std::time_t t) : time_(t) {}
+
+ bool run() {
+ using namespace fmt::detail;
+ return handle(gmtime_r(&time_, &tm_));
+ }
+
+ bool handle(std::tm* tm) { return tm != nullptr; }
+
+ bool handle(detail::null<>) {
+ using namespace fmt::detail;
+ return fallback(gmtime_s(&tm_, &time_));
+ }
+
+ bool fallback(int res) { return res == 0; }
+
+#if !FMT_MSC_VER
+ bool fallback(detail::null<>) {
+ std::tm* tm = std::gmtime(&time_);
+ if (tm) tm_ = *tm;
+ return tm != nullptr;
+ }
+#endif
+ };
+ dispatcher gt(time);
+ // Too big time values may be unsupported.
+ if (!gt.run()) FMT_THROW(format_error("time_t value out of range"));
+ return gt.tm_;
+}
+
+inline std::tm gmtime(
+ std::chrono::time_point<std::chrono::system_clock> time_point) {
+ return gmtime(std::chrono::system_clock::to_time_t(time_point));
+}
+
+FMT_BEGIN_DETAIL_NAMESPACE
+
+inline size_t strftime(char* str, size_t count, const char* format,
+ const std::tm* time) {
+ // Assign to a pointer to suppress GCCs -Wformat-nonliteral
+ // First assign the nullptr to suppress -Wsuggest-attribute=format
+ std::size_t (*strftime)(char*, std::size_t, const char*, const std::tm*) =
+ nullptr;
+ strftime = std::strftime;
+ return strftime(str, count, format, time);
+}
+
+inline size_t strftime(wchar_t* str, size_t count, const wchar_t* format,
+ const std::tm* time) {
+ // See above
+ std::size_t (*wcsftime)(wchar_t*, std::size_t, const wchar_t*,
+ const std::tm*) = nullptr;
+ wcsftime = std::wcsftime;
+ return wcsftime(str, count, format, time);
+}
+
+FMT_END_DETAIL_NAMESPACE
+
+template <typename Char, typename Duration>
+struct formatter<std::chrono::time_point<std::chrono::system_clock, Duration>,
+ Char> : formatter<std::tm, Char> {
+ FMT_CONSTEXPR formatter() {
+ this->specs = {default_specs, sizeof(default_specs) / sizeof(Char)};
+ }
+
+ template <typename ParseContext>
+ FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
+ auto it = ctx.begin();
+ if (it != ctx.end() && *it == ':') ++it;
+ auto end = it;
+ while (end != ctx.end() && *end != '}') ++end;
+ if (end != it) this->specs = {it, detail::to_unsigned(end - it)};
+ return end;
+ }
+
+ template <typename FormatContext>
+ auto format(std::chrono::time_point<std::chrono::system_clock> val,
+ FormatContext& ctx) -> decltype(ctx.out()) {
+ std::tm time = localtime(val);
+ return formatter<std::tm, Char>::format(time, ctx);
+ }
+
+ static constexpr Char default_specs[] = {'%', 'Y', '-', '%', 'm', '-',
+ '%', 'd', ' ', '%', 'H', ':',
+ '%', 'M', ':', '%', 'S'};
+};
+
+template <typename Char, typename Duration>
+constexpr Char
+ formatter<std::chrono::time_point<std::chrono::system_clock, Duration>,
+ Char>::default_specs[];
+
+template <typename Char> struct formatter<std::tm, Char> {
+ template <typename ParseContext>
+ FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
+ auto it = ctx.begin();
+ if (it != ctx.end() && *it == ':') ++it;
+ auto end = it;
+ while (end != ctx.end() && *end != '}') ++end;
+ specs = {it, detail::to_unsigned(end - it)};
+ return end;
+ }
+
+ template <typename FormatContext>
+ auto format(const std::tm& tm, FormatContext& ctx) const
+ -> decltype(ctx.out()) {
+ basic_memory_buffer<Char> tm_format;
+ tm_format.append(specs.begin(), specs.end());
+ // By appending an extra space we can distinguish an empty result that
+ // indicates insufficient buffer size from a guaranteed non-empty result
+ // https://github.com/fmtlib/fmt/issues/2238
+ tm_format.push_back(' ');
+ tm_format.push_back('\0');
+ basic_memory_buffer<Char> buf;
+ size_t start = buf.size();
+ for (;;) {
+ size_t size = buf.capacity() - start;
+ size_t count = detail::strftime(&buf[start], size, &tm_format[0], &tm);
+ if (count != 0) {
+ buf.resize(start + count);
+ break;
+ }
+ const size_t MIN_GROWTH = 10;
+ buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
+ }
+ // Remove the extra space.
+ return std::copy(buf.begin(), buf.end() - 1, ctx.out());
+ }
+
+ basic_string_view<Char> specs;
+};
+
+FMT_BEGIN_DETAIL_NAMESPACE
+
+template <typename Period> FMT_CONSTEXPR inline const char* get_units() {
+ if (std::is_same<Period, std::atto>::value) return "as";
+ if (std::is_same<Period, std::femto>::value) return "fs";
+ if (std::is_same<Period, std::pico>::value) return "ps";
+ if (std::is_same<Period, std::nano>::value) return "ns";
+ if (std::is_same<Period, std::micro>::value) return "µs";
+ if (std::is_same<Period, std::milli>::value) return "ms";
+ if (std::is_same<Period, std::centi>::value) return "cs";
+ if (std::is_same<Period, std::deci>::value) return "ds";
+ if (std::is_same<Period, std::ratio<1>>::value) return "s";
+ if (std::is_same<Period, std::deca>::value) return "das";
+ if (std::is_same<Period, std::hecto>::value) return "hs";
+ if (std::is_same<Period, std::kilo>::value) return "ks";
+ if (std::is_same<Period, std::mega>::value) return "Ms";
+ if (std::is_same<Period, std::giga>::value) return "Gs";
+ if (std::is_same<Period, std::tera>::value) return "Ts";
+ if (std::is_same<Period, std::peta>::value) return "Ps";
+ if (std::is_same<Period, std::exa>::value) return "Es";
+ if (std::is_same<Period, std::ratio<60>>::value) return "m";
+ if (std::is_same<Period, std::ratio<3600>>::value) return "h";
+ return nullptr;
+}
+
+enum class numeric_system {
+ standard,
+ // Alternative numeric system, e.g. 十二 instead of 12 in ja_JP locale.
+ alternative
+};
+
+// 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) {
+ auto ptr = begin;
+ while (ptr != end) {
+ auto c = *ptr;
+ if (c == '}') break;
+ if (c != '%') {
+ ++ptr;
+ continue;
+ }
+ if (begin != ptr) handler.on_text(begin, ptr);
+ ++ptr; // consume '%'
+ if (ptr == end) FMT_THROW(format_error("invalid format"));
+ c = *ptr++;
+ switch (c) {
+ case '%':
+ handler.on_text(ptr - 1, ptr);
+ break;
+ case 'n': {
+ const Char newline[] = {'\n'};
+ handler.on_text(newline, newline + 1);
+ break;
+ }
+ case 't': {
+ const Char tab[] = {'\t'};
+ handler.on_text(tab, tab + 1);
+ break;
+ }
+ // Day of the week:
+ case 'a':
+ handler.on_abbr_weekday();
+ break;
+ case 'A':
+ handler.on_full_weekday();
+ break;
+ case 'w':
+ handler.on_dec0_weekday(numeric_system::standard);
+ break;
+ case 'u':
+ handler.on_dec1_weekday(numeric_system::standard);
+ break;
+ // Month:
+ case 'b':
+ handler.on_abbr_month();
+ break;
+ case 'B':
+ handler.on_full_month();
+ break;
+ // Hour, minute, second:
+ case 'H':
+ handler.on_24_hour(numeric_system::standard);
+ break;
+ case 'I':
+ handler.on_12_hour(numeric_system::standard);
+ break;
+ case 'M':
+ handler.on_minute(numeric_system::standard);
+ break;
+ case 'S':
+ handler.on_second(numeric_system::standard);
+ break;
+ // Other:
+ case 'c':
+ handler.on_datetime(numeric_system::standard);
+ break;
+ case 'x':
+ handler.on_loc_date(numeric_system::standard);
+ break;
+ case 'X':
+ handler.on_loc_time(numeric_system::standard);
+ break;
+ case 'D':
+ handler.on_us_date();
+ break;
+ case 'F':
+ handler.on_iso_date();
+ break;
+ case 'r':
+ handler.on_12_hour_time();
+ break;
+ case 'R':
+ handler.on_24_hour_time();
+ break;
+ case 'T':
+ handler.on_iso_time();
+ break;
+ case 'p':
+ handler.on_am_pm();
+ break;
+ case 'Q':
+ handler.on_duration_value();
+ break;
+ case 'q':
+ handler.on_duration_unit();
+ break;
+ case 'z':
+ handler.on_utc_offset();
+ break;
+ case 'Z':
+ handler.on_tz_name();
+ break;
+ // Alternative representation:
+ case 'E': {
+ if (ptr == end) FMT_THROW(format_error("invalid format"));
+ c = *ptr++;
+ switch (c) {
+ case 'c':
+ handler.on_datetime(numeric_system::alternative);
+ break;
+ case 'x':
+ handler.on_loc_date(numeric_system::alternative);
+ break;
+ case 'X':
+ handler.on_loc_time(numeric_system::alternative);
+ break;
+ default:
+ FMT_THROW(format_error("invalid format"));
+ }
+ break;
+ }
+ case 'O':
+ if (ptr == end) FMT_THROW(format_error("invalid format"));
+ c = *ptr++;
+ switch (c) {
+ case 'w':
+ handler.on_dec0_weekday(numeric_system::alternative);
+ break;
+ case 'u':
+ handler.on_dec1_weekday(numeric_system::alternative);
+ break;
+ case 'H':
+ handler.on_24_hour(numeric_system::alternative);
+ break;
+ case 'I':
+ handler.on_12_hour(numeric_system::alternative);
+ break;
+ case 'M':
+ handler.on_minute(numeric_system::alternative);
+ break;
+ case 'S':
+ handler.on_second(numeric_system::alternative);
+ break;
+ default:
+ FMT_THROW(format_error("invalid format"));
+ }
+ break;
+ default:
+ FMT_THROW(format_error("invalid format"));
+ }
+ begin = ptr;
+ }
+ if (begin != ptr) handler.on_text(begin, ptr);
+ return ptr;
+}
+
+template <typename Derived> struct null_chrono_spec_handler {
+ FMT_CONSTEXPR void unsupported() {
+ static_cast<Derived*>(this)->unsupported();
+ }
+ FMT_CONSTEXPR void on_abbr_weekday() { unsupported(); }
+ FMT_CONSTEXPR void on_full_weekday() { unsupported(); }
+ FMT_CONSTEXPR void on_dec0_weekday(numeric_system) { unsupported(); }
+ FMT_CONSTEXPR void on_dec1_weekday(numeric_system) { unsupported(); }
+ FMT_CONSTEXPR void on_abbr_month() { unsupported(); }
+ FMT_CONSTEXPR void on_full_month() { unsupported(); }
+ FMT_CONSTEXPR void on_24_hour(numeric_system) { unsupported(); }
+ FMT_CONSTEXPR void on_12_hour(numeric_system) { unsupported(); }
+ FMT_CONSTEXPR void on_minute(numeric_system) { unsupported(); }
+ FMT_CONSTEXPR void on_second(numeric_system) { unsupported(); }
+ FMT_CONSTEXPR void on_datetime(numeric_system) { unsupported(); }
+ FMT_CONSTEXPR void on_loc_date(numeric_system) { unsupported(); }
+ FMT_CONSTEXPR void on_loc_time(numeric_system) { unsupported(); }
+ FMT_CONSTEXPR void on_us_date() { unsupported(); }
+ FMT_CONSTEXPR void on_iso_date() { unsupported(); }
+ FMT_CONSTEXPR void on_12_hour_time() { unsupported(); }
+ FMT_CONSTEXPR void on_24_hour_time() { unsupported(); }
+ FMT_CONSTEXPR void on_iso_time() { unsupported(); }
+ 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_tz_name() { unsupported(); }
+};
+
+struct chrono_format_checker : null_chrono_spec_handler<chrono_format_checker> {
+ 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_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_unit() {}
+};
+
+template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
+inline bool isnan(T) {
+ return false;
+}
+template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
+inline bool isnan(T value) {
+ return std::isnan(value);
+}
+
+template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
+inline bool isfinite(T) {
+ return true;
+}
+template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
+inline bool isfinite(T value) {
+ return std::isfinite(value);
+}
+
+// Converts value to int and checks that it's in the range [0, upper).
+template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
+inline int to_nonnegative_int(T value, int upper) {
+ FMT_ASSERT(value >= 0 && to_unsigned(value) <= to_unsigned(upper),
+ "invalid value");
+ (void)upper;
+ return static_cast<int>(value);
+}
+template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
+inline int to_nonnegative_int(T value, int upper) {
+ FMT_ASSERT(
+ std::isnan(value) || (value >= 0 && value <= static_cast<T>(upper)),
+ "invalid value");
+ (void)upper;
+ 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);
+}
+template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
+inline T mod(T x, int y) {
+ return std::fmod(x, static_cast<T>(y));
+}
+
+// If T is an integral type, maps T to its unsigned counterpart, otherwise
+// leaves it unchanged (unlike std::make_unsigned).
+template <typename T, bool INTEGRAL = std::is_integral<T>::value>
+struct make_unsigned_or_unchanged {
+ using type = T;
+};
+
+template <typename T> struct make_unsigned_or_unchanged<T, true> {
+ using type = typename std::make_unsigned<T>::type;
+};
+
+#if FMT_SAFE_DURATION_CAST
+// throwing version of safe_duration_cast
+template <typename To, typename FromRep, typename FromPeriod>
+To fmt_safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from) {
+ int ec;
+ To to = safe_duration_cast::safe_duration_cast<To>(from, ec);
+ if (ec) FMT_THROW(format_error("cannot format duration"));
+ return to;
+}
+#endif
+
+template <typename Rep, typename Period,
+ FMT_ENABLE_IF(std::is_integral<Rep>::value)>
+inline std::chrono::duration<Rep, std::milli> get_milliseconds(
+ std::chrono::duration<Rep, Period> d) {
+ // this may overflow and/or the result may not fit in the
+ // target type.
+#if FMT_SAFE_DURATION_CAST
+ using CommonSecondsType =
+ typename std::common_type<decltype(d), std::chrono::seconds>::type;
+ const auto d_as_common = fmt_safe_duration_cast<CommonSecondsType>(d);
+ const auto d_as_whole_seconds =
+ fmt_safe_duration_cast<std::chrono::seconds>(d_as_common);
+ // this conversion should be nonproblematic
+ const auto diff = d_as_common - d_as_whole_seconds;
+ const auto ms =
+ fmt_safe_duration_cast<std::chrono::duration<Rep, std::milli>>(diff);
+ return ms;
+#else
+ auto s = std::chrono::duration_cast<std::chrono::seconds>(d);
+ return std::chrono::duration_cast<std::chrono::milliseconds>(d - s);
+#endif
+}
+
+template <typename Rep, typename Period,
+ FMT_ENABLE_IF(std::is_floating_point<Rep>::value)>
+inline std::chrono::duration<Rep, std::milli> get_milliseconds(
+ std::chrono::duration<Rep, Period> d) {
+ using common_type = typename std::common_type<Rep, std::intmax_t>::type;
+ auto ms = mod(d.count() * static_cast<common_type>(Period::num) /
+ static_cast<common_type>(Period::den) * 1000,
+ 1000);
+ return std::chrono::duration<Rep, std::milli>(static_cast<Rep>(ms));
+}
+
+template <typename Char, typename Rep, typename OutputIt,
+ FMT_ENABLE_IF(std::is_integral<Rep>::value)>
+OutputIt format_duration_value(OutputIt out, Rep val, int) {
+ return write<Char>(out, val);
+}
+
+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>();
+ specs.precision = precision;
+ specs.type = precision > 0 ? 'f' : 'g';
+ return write<Char>(out, val, specs);
+}
+
+template <typename Char, typename OutputIt>
+OutputIt copy_unit(string_view unit, OutputIt out, Char) {
+ return std::copy(unit.begin(), unit.end(), out);
+}
+
+template <typename OutputIt>
+OutputIt copy_unit(string_view unit, OutputIt out, wchar_t) {
+ // This works when wchar_t is UTF-32 because units only contain characters
+ // that have the same representation in UTF-16 and UTF-32.
+ utf8_to_utf16 u(unit);
+ return std::copy(u.c_str(), u.c_str() + u.size(), out);
+}
+
+template <typename Char, typename Period, typename OutputIt>
+OutputIt format_duration_unit(OutputIt out) {
+ if (const char* unit = get_units<Period>())
+ return copy_unit(string_view(unit), out, Char());
+ *out++ = '[';
+ out = write<Char>(out, Period::num);
+ if (const_check(Period::den != 1)) {
+ *out++ = '/';
+ out = write<Char>(out, Period::den);
+ }
+ *out++ = ']';
+ *out++ = 's';
+ return out;
+}
+
+template <typename FormatContext, typename OutputIt, typename Rep,
+ typename Period>
+struct chrono_formatter {
+ FormatContext& context;
+ OutputIt out;
+ int precision;
+ bool localized = false;
+ // rep is unsigned to avoid overflow.
+ using rep =
+ conditional_t<std::is_integral<Rep>::value && sizeof(Rep) < sizeof(int),
+ unsigned, typename make_unsigned_or_unchanged<Rep>::type>;
+ rep val;
+ using seconds = std::chrono::duration<rep>;
+ seconds s;
+ using milliseconds = std::chrono::duration<rep, std::milli>;
+ bool negative;
+
+ using char_type = typename FormatContext::char_type;
+
+ explicit chrono_formatter(FormatContext& ctx, OutputIt o,
+ std::chrono::duration<Rep, Period> d)
+ : context(ctx),
+ out(o),
+ val(static_cast<rep>(d.count())),
+ negative(false) {
+ if (d.count() < 0) {
+ val = 0 - val;
+ negative = true;
+ }
+
+ // this may overflow and/or the result may not fit in the
+ // target type.
+#if FMT_SAFE_DURATION_CAST
+ // might need checked conversion (rep!=Rep)
+ auto tmpval = std::chrono::duration<rep, Period>(val);
+ s = fmt_safe_duration_cast<seconds>(tmpval);
+#else
+ s = std::chrono::duration_cast<seconds>(
+ std::chrono::duration<rep, Period>(val));
+#endif
+ }
+
+ // returns true if nan or inf, writes to out.
+ bool handle_nan_inf() {
+ if (isfinite(val)) {
+ return false;
+ }
+ if (isnan(val)) {
+ write_nan();
+ return true;
+ }
+ // must be +-inf
+ if (val > 0) {
+ write_pinf();
+ } else {
+ write_ninf();
+ }
+ return true;
+ }
+
+ Rep hour() const { return static_cast<Rep>(mod((s.count() / 3600), 24)); }
+
+ Rep hour12() const {
+ Rep hour = static_cast<Rep>(mod((s.count() / 3600), 12));
+ return hour <= 0 ? 12 : hour;
+ }
+
+ Rep minute() const { return static_cast<Rep>(mod((s.count() / 60), 60)); }
+ Rep second() const { return static_cast<Rep>(mod(s.count(), 60)); }
+
+ std::tm time() const {
+ auto time = std::tm();
+ time.tm_hour = to_nonnegative_int(hour(), 24);
+ time.tm_min = to_nonnegative_int(minute(), 60);
+ time.tm_sec = to_nonnegative_int(second(), 60);
+ return time;
+ }
+
+ void write_sign() {
+ if (negative) {
+ *out++ = '-';
+ negative = false;
+ }
+ }
+
+ void write(Rep value, int width) {
+ 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;
+ }
+
+ void write_nan() { std::copy_n("nan", 3, out); }
+ void write_pinf() { std::copy_n("inf", 3, out); }
+ void write_ninf() { std::copy_n("-inf", 4, out); }
+
+ void format_localized(const tm& time, char format, char modifier = 0) {
+ if (isnan(val)) return write_nan();
+ const auto& loc = localized ? context.locale().template get<std::locale>()
+ : std::locale::classic();
+ out = detail::write(out, time, loc, format, modifier);
+ }
+
+ void on_text(const char_type* begin, const char_type* end) {
+ std::copy(begin, end, out);
+ }
+
+ // These are not implemented because durations don't have date information.
+ void on_abbr_weekday() {}
+ void on_full_weekday() {}
+ void on_dec0_weekday(numeric_system) {}
+ void on_dec1_weekday(numeric_system) {}
+ void on_abbr_month() {}
+ void on_full_month() {}
+ void on_datetime(numeric_system) {}
+ void on_loc_date(numeric_system) {}
+ void on_loc_time(numeric_system) {}
+ void on_us_date() {}
+ void on_iso_date() {}
+ void on_utc_offset() {}
+ void on_tz_name() {}
+
+ void on_24_hour(numeric_system ns) {
+ if (handle_nan_inf()) return;
+
+ if (ns == numeric_system::standard) return write(hour(), 2);
+ auto time = tm();
+ time.tm_hour = to_nonnegative_int(hour(), 24);
+ format_localized(time, 'H', 'O');
+ }
+
+ void on_12_hour(numeric_system ns) {
+ if (handle_nan_inf()) return;
+
+ if (ns == numeric_system::standard) return write(hour12(), 2);
+ auto time = tm();
+ time.tm_hour = to_nonnegative_int(hour12(), 12);
+ format_localized(time, 'I', 'O');
+ }
+
+ void on_minute(numeric_system ns) {
+ if (handle_nan_inf()) return;
+
+ if (ns == numeric_system::standard) return write(minute(), 2);
+ auto time = tm();
+ time.tm_min = to_nonnegative_int(minute(), 60);
+ format_localized(time, 'M', 'O');
+ }
+
+ void on_second(numeric_system ns) {
+ if (handle_nan_inf()) return;
+
+ if (ns == numeric_system::standard) {
+ write(second(), 2);
+#if FMT_SAFE_DURATION_CAST
+ // convert rep->Rep
+ using duration_rep = std::chrono::duration<rep, Period>;
+ using duration_Rep = std::chrono::duration<Rep, Period>;
+ auto tmpval = fmt_safe_duration_cast<duration_Rep>(duration_rep{val});
+#else
+ auto tmpval = std::chrono::duration<Rep, Period>(val);
+#endif
+ auto ms = get_milliseconds(tmpval);
+ if (ms != std::chrono::milliseconds(0)) {
+ *out++ = '.';
+ write(ms.count(), 3);
+ }
+ return;
+ }
+ auto time = tm();
+ time.tm_sec = to_nonnegative_int(second(), 60);
+ format_localized(time, 'S', 'O');
+ }
+
+ void on_12_hour_time() {
+ if (handle_nan_inf()) return;
+ format_localized(time(), 'r');
+ }
+
+ void on_24_hour_time() {
+ if (handle_nan_inf()) {
+ *out++ = ':';
+ handle_nan_inf();
+ return;
+ }
+
+ write(hour(), 2);
+ *out++ = ':';
+ write(minute(), 2);
+ }
+
+ void on_iso_time() {
+ on_24_hour_time();
+ *out++ = ':';
+ if (handle_nan_inf()) return;
+ write(second(), 2);
+ }
+
+ void on_am_pm() {
+ if (handle_nan_inf()) return;
+ format_localized(time(), 'p');
+ }
+
+ void on_duration_value() {
+ if (handle_nan_inf()) return;
+ write_sign();
+ out = format_duration_value<char_type>(out, val, precision);
+ }
+
+ void on_duration_unit() {
+ out = format_duration_unit<char_type, Period>(out);
+ }
+};
+
+FMT_END_DETAIL_NAMESPACE
+
+#if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907
+using weekday = std::chrono::weekday;
+#else
+// A fallback version of weekday.
+class weekday {
+ private:
+ unsigned char value;
+
+ public:
+ weekday() = default;
+ explicit constexpr weekday(unsigned wd) noexcept
+ : value(static_cast<unsigned char>(wd != 7 ? wd : 0)) {}
+ constexpr unsigned c_encoding() const noexcept { return value; }
+};
+#endif
+
+// A rudimentary weekday formatter.
+template <> struct formatter<weekday> {
+ private:
+ bool localized = false;
+
+ public:
+ FMT_CONSTEXPR auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
+ auto begin = ctx.begin(), end = ctx.end();
+ if (begin != end && *begin == 'L') {
+ ++begin;
+ localized = true;
+ }
+ return begin;
+ }
+
+ auto format(weekday wd, format_context& ctx) -> decltype(ctx.out()) {
+ auto time = std::tm();
+ time.tm_wday = static_cast<int>(wd.c_encoding());
+ const auto& loc = localized ? ctx.locale().template get<std::locale>()
+ : std::locale::classic();
+ return detail::write(ctx.out(), time, loc, 'a');
+ }
+};
+
+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 = parse_chrono_format(begin, end, detail::chrono_format_checker());
+ return {begin, end};
+ }
+
+ 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;
+ }
+
+ template <typename FormatContext>
+ auto format(const duration& d, FormatContext& ctx) const
+ -> decltype(ctx.out()) {
+ auto specs_copy = specs;
+ auto precision_copy = precision;
+ 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 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);
+ if (begin == end || *begin == '}') {
+ out = detail::format_duration_value<Char>(out, d.count(), precision_copy);
+ 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;
+ detail::parse_chrono_format(begin, end, f);
+ }
+ return detail::write(
+ ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs_copy);
+ }
+};
+
+FMT_MODULE_EXPORT_END
+FMT_END_NAMESPACE
+
+#endif // FMT_CHRONO_H_
diff --git a/wpiutil/src/main/native/fmtlib/include/fmt/color.h b/wpiutil/src/main/native/fmtlib/include/fmt/color.h
new file mode 100644
index 0000000..3d5490e
--- /dev/null
+++ b/wpiutil/src/main/native/fmtlib/include/fmt/color.h
@@ -0,0 +1,627 @@
+// Formatting library for C++ - color support
+//
+// Copyright (c) 2018 - present, Victor Zverovich and fmt contributors
+// All rights reserved.
+//
+// For the license information refer to format.h.
+
+#ifndef FMT_COLOR_H_
+#define FMT_COLOR_H_
+
+#include "format.h"
+
+// __declspec(deprecated) is broken in some MSVC versions.
+#if FMT_MSC_VER
+# define FMT_DEPRECATED_NONMSVC
+#else
+# define FMT_DEPRECATED_NONMSVC FMT_DEPRECATED
+#endif
+
+FMT_BEGIN_NAMESPACE
+FMT_MODULE_EXPORT_BEGIN
+
+enum class color : uint32_t {
+ alice_blue = 0xF0F8FF, // rgb(240,248,255)
+ antique_white = 0xFAEBD7, // rgb(250,235,215)
+ aqua = 0x00FFFF, // rgb(0,255,255)
+ aquamarine = 0x7FFFD4, // rgb(127,255,212)
+ azure = 0xF0FFFF, // rgb(240,255,255)
+ beige = 0xF5F5DC, // rgb(245,245,220)
+ bisque = 0xFFE4C4, // rgb(255,228,196)
+ black = 0x000000, // rgb(0,0,0)
+ blanched_almond = 0xFFEBCD, // rgb(255,235,205)
+ blue = 0x0000FF, // rgb(0,0,255)
+ blue_violet = 0x8A2BE2, // rgb(138,43,226)
+ brown = 0xA52A2A, // rgb(165,42,42)
+ burly_wood = 0xDEB887, // rgb(222,184,135)
+ cadet_blue = 0x5F9EA0, // rgb(95,158,160)
+ chartreuse = 0x7FFF00, // rgb(127,255,0)
+ chocolate = 0xD2691E, // rgb(210,105,30)
+ coral = 0xFF7F50, // rgb(255,127,80)
+ cornflower_blue = 0x6495ED, // rgb(100,149,237)
+ cornsilk = 0xFFF8DC, // rgb(255,248,220)
+ crimson = 0xDC143C, // rgb(220,20,60)
+ cyan = 0x00FFFF, // rgb(0,255,255)
+ dark_blue = 0x00008B, // rgb(0,0,139)
+ dark_cyan = 0x008B8B, // rgb(0,139,139)
+ dark_golden_rod = 0xB8860B, // rgb(184,134,11)
+ dark_gray = 0xA9A9A9, // rgb(169,169,169)
+ dark_green = 0x006400, // rgb(0,100,0)
+ dark_khaki = 0xBDB76B, // rgb(189,183,107)
+ dark_magenta = 0x8B008B, // rgb(139,0,139)
+ dark_olive_green = 0x556B2F, // rgb(85,107,47)
+ dark_orange = 0xFF8C00, // rgb(255,140,0)
+ dark_orchid = 0x9932CC, // rgb(153,50,204)
+ dark_red = 0x8B0000, // rgb(139,0,0)
+ dark_salmon = 0xE9967A, // rgb(233,150,122)
+ dark_sea_green = 0x8FBC8F, // rgb(143,188,143)
+ dark_slate_blue = 0x483D8B, // rgb(72,61,139)
+ dark_slate_gray = 0x2F4F4F, // rgb(47,79,79)
+ dark_turquoise = 0x00CED1, // rgb(0,206,209)
+ dark_violet = 0x9400D3, // rgb(148,0,211)
+ deep_pink = 0xFF1493, // rgb(255,20,147)
+ deep_sky_blue = 0x00BFFF, // rgb(0,191,255)
+ dim_gray = 0x696969, // rgb(105,105,105)
+ dodger_blue = 0x1E90FF, // rgb(30,144,255)
+ fire_brick = 0xB22222, // rgb(178,34,34)
+ floral_white = 0xFFFAF0, // rgb(255,250,240)
+ forest_green = 0x228B22, // rgb(34,139,34)
+ fuchsia = 0xFF00FF, // rgb(255,0,255)
+ gainsboro = 0xDCDCDC, // rgb(220,220,220)
+ ghost_white = 0xF8F8FF, // rgb(248,248,255)
+ gold = 0xFFD700, // rgb(255,215,0)
+ golden_rod = 0xDAA520, // rgb(218,165,32)
+ gray = 0x808080, // rgb(128,128,128)
+ green = 0x008000, // rgb(0,128,0)
+ green_yellow = 0xADFF2F, // rgb(173,255,47)
+ honey_dew = 0xF0FFF0, // rgb(240,255,240)
+ hot_pink = 0xFF69B4, // rgb(255,105,180)
+ indian_red = 0xCD5C5C, // rgb(205,92,92)
+ indigo = 0x4B0082, // rgb(75,0,130)
+ ivory = 0xFFFFF0, // rgb(255,255,240)
+ khaki = 0xF0E68C, // rgb(240,230,140)
+ lavender = 0xE6E6FA, // rgb(230,230,250)
+ lavender_blush = 0xFFF0F5, // rgb(255,240,245)
+ lawn_green = 0x7CFC00, // rgb(124,252,0)
+ lemon_chiffon = 0xFFFACD, // rgb(255,250,205)
+ light_blue = 0xADD8E6, // rgb(173,216,230)
+ light_coral = 0xF08080, // rgb(240,128,128)
+ light_cyan = 0xE0FFFF, // rgb(224,255,255)
+ light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210)
+ light_gray = 0xD3D3D3, // rgb(211,211,211)
+ light_green = 0x90EE90, // rgb(144,238,144)
+ light_pink = 0xFFB6C1, // rgb(255,182,193)
+ light_salmon = 0xFFA07A, // rgb(255,160,122)
+ light_sea_green = 0x20B2AA, // rgb(32,178,170)
+ light_sky_blue = 0x87CEFA, // rgb(135,206,250)
+ light_slate_gray = 0x778899, // rgb(119,136,153)
+ light_steel_blue = 0xB0C4DE, // rgb(176,196,222)
+ light_yellow = 0xFFFFE0, // rgb(255,255,224)
+ lime = 0x00FF00, // rgb(0,255,0)
+ lime_green = 0x32CD32, // rgb(50,205,50)
+ linen = 0xFAF0E6, // rgb(250,240,230)
+ magenta = 0xFF00FF, // rgb(255,0,255)
+ maroon = 0x800000, // rgb(128,0,0)
+ medium_aquamarine = 0x66CDAA, // rgb(102,205,170)
+ medium_blue = 0x0000CD, // rgb(0,0,205)
+ medium_orchid = 0xBA55D3, // rgb(186,85,211)
+ medium_purple = 0x9370DB, // rgb(147,112,219)
+ medium_sea_green = 0x3CB371, // rgb(60,179,113)
+ medium_slate_blue = 0x7B68EE, // rgb(123,104,238)
+ medium_spring_green = 0x00FA9A, // rgb(0,250,154)
+ medium_turquoise = 0x48D1CC, // rgb(72,209,204)
+ medium_violet_red = 0xC71585, // rgb(199,21,133)
+ midnight_blue = 0x191970, // rgb(25,25,112)
+ mint_cream = 0xF5FFFA, // rgb(245,255,250)
+ misty_rose = 0xFFE4E1, // rgb(255,228,225)
+ moccasin = 0xFFE4B5, // rgb(255,228,181)
+ navajo_white = 0xFFDEAD, // rgb(255,222,173)
+ navy = 0x000080, // rgb(0,0,128)
+ old_lace = 0xFDF5E6, // rgb(253,245,230)
+ olive = 0x808000, // rgb(128,128,0)
+ olive_drab = 0x6B8E23, // rgb(107,142,35)
+ orange = 0xFFA500, // rgb(255,165,0)
+ orange_red = 0xFF4500, // rgb(255,69,0)
+ orchid = 0xDA70D6, // rgb(218,112,214)
+ pale_golden_rod = 0xEEE8AA, // rgb(238,232,170)
+ pale_green = 0x98FB98, // rgb(152,251,152)
+ pale_turquoise = 0xAFEEEE, // rgb(175,238,238)
+ pale_violet_red = 0xDB7093, // rgb(219,112,147)
+ papaya_whip = 0xFFEFD5, // rgb(255,239,213)
+ peach_puff = 0xFFDAB9, // rgb(255,218,185)
+ peru = 0xCD853F, // rgb(205,133,63)
+ pink = 0xFFC0CB, // rgb(255,192,203)
+ plum = 0xDDA0DD, // rgb(221,160,221)
+ powder_blue = 0xB0E0E6, // rgb(176,224,230)
+ purple = 0x800080, // rgb(128,0,128)
+ rebecca_purple = 0x663399, // rgb(102,51,153)
+ red = 0xFF0000, // rgb(255,0,0)
+ rosy_brown = 0xBC8F8F, // rgb(188,143,143)
+ royal_blue = 0x4169E1, // rgb(65,105,225)
+ saddle_brown = 0x8B4513, // rgb(139,69,19)
+ salmon = 0xFA8072, // rgb(250,128,114)
+ sandy_brown = 0xF4A460, // rgb(244,164,96)
+ sea_green = 0x2E8B57, // rgb(46,139,87)
+ sea_shell = 0xFFF5EE, // rgb(255,245,238)
+ sienna = 0xA0522D, // rgb(160,82,45)
+ silver = 0xC0C0C0, // rgb(192,192,192)
+ sky_blue = 0x87CEEB, // rgb(135,206,235)
+ slate_blue = 0x6A5ACD, // rgb(106,90,205)
+ slate_gray = 0x708090, // rgb(112,128,144)
+ snow = 0xFFFAFA, // rgb(255,250,250)
+ spring_green = 0x00FF7F, // rgb(0,255,127)
+ steel_blue = 0x4682B4, // rgb(70,130,180)
+ tan = 0xD2B48C, // rgb(210,180,140)
+ teal = 0x008080, // rgb(0,128,128)
+ thistle = 0xD8BFD8, // rgb(216,191,216)
+ tomato = 0xFF6347, // rgb(255,99,71)
+ turquoise = 0x40E0D0, // rgb(64,224,208)
+ violet = 0xEE82EE, // rgb(238,130,238)
+ wheat = 0xF5DEB3, // rgb(245,222,179)
+ white = 0xFFFFFF, // rgb(255,255,255)
+ white_smoke = 0xF5F5F5, // rgb(245,245,245)
+ yellow = 0xFFFF00, // rgb(255,255,0)
+ yellow_green = 0x9ACD32 // rgb(154,205,50)
+}; // enum class color
+
+enum class terminal_color : uint8_t {
+ black = 30,
+ red,
+ green,
+ yellow,
+ blue,
+ magenta,
+ cyan,
+ white,
+ bright_black = 90,
+ bright_red,
+ bright_green,
+ bright_yellow,
+ bright_blue,
+ bright_magenta,
+ bright_cyan,
+ bright_white
+};
+
+enum class emphasis : uint8_t {
+ bold = 1,
+ italic = 1 << 1,
+ underline = 1 << 2,
+ strikethrough = 1 << 3
+};
+
+// rgb is a struct for red, green and blue colors.
+// Using the name "rgb" makes some editors show the color in a tooltip.
+struct rgb {
+ FMT_CONSTEXPR rgb() : r(0), g(0), b(0) {}
+ FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {}
+ FMT_CONSTEXPR rgb(uint32_t hex)
+ : r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {}
+ FMT_CONSTEXPR rgb(color hex)
+ : r((uint32_t(hex) >> 16) & 0xFF),
+ g((uint32_t(hex) >> 8) & 0xFF),
+ b(uint32_t(hex) & 0xFF) {}
+ uint8_t r;
+ uint8_t g;
+ uint8_t b;
+};
+
+FMT_BEGIN_DETAIL_NAMESPACE
+
+// color is a struct of either a rgb color or a terminal color.
+struct color_type {
+ FMT_CONSTEXPR color_type() FMT_NOEXCEPT : is_rgb(), value{} {}
+ FMT_CONSTEXPR color_type(color rgb_color) FMT_NOEXCEPT : is_rgb(true),
+ value{} {
+ value.rgb_color = static_cast<uint32_t>(rgb_color);
+ }
+ FMT_CONSTEXPR color_type(rgb rgb_color) FMT_NOEXCEPT : is_rgb(true), value{} {
+ value.rgb_color = (static_cast<uint32_t>(rgb_color.r) << 16) |
+ (static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b;
+ }
+ FMT_CONSTEXPR color_type(terminal_color term_color) FMT_NOEXCEPT : is_rgb(),
+ value{} {
+ value.term_color = static_cast<uint8_t>(term_color);
+ }
+ bool is_rgb;
+ union color_union {
+ uint8_t term_color;
+ uint32_t rgb_color;
+ } value;
+};
+
+FMT_END_DETAIL_NAMESPACE
+
+/** A text style consisting of foreground and background colors and emphasis. */
+class text_style {
+ public:
+ FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT
+ : set_foreground_color(),
+ set_background_color(),
+ ems(em) {}
+
+ FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) {
+ if (!set_foreground_color) {
+ set_foreground_color = rhs.set_foreground_color;
+ foreground_color = rhs.foreground_color;
+ } else if (rhs.set_foreground_color) {
+ if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
+ FMT_THROW(format_error("can't OR a terminal color"));
+ foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color;
+ }
+
+ if (!set_background_color) {
+ set_background_color = rhs.set_background_color;
+ background_color = rhs.background_color;
+ } else if (rhs.set_background_color) {
+ if (!background_color.is_rgb || !rhs.background_color.is_rgb)
+ FMT_THROW(format_error("can't OR a terminal color"));
+ background_color.value.rgb_color |= rhs.background_color.value.rgb_color;
+ }
+
+ ems = static_cast<emphasis>(static_cast<uint8_t>(ems) |
+ static_cast<uint8_t>(rhs.ems));
+ return *this;
+ }
+
+ friend FMT_CONSTEXPR text_style operator|(text_style lhs,
+ const text_style& rhs) {
+ return lhs |= rhs;
+ }
+
+ FMT_DEPRECATED_NONMSVC FMT_CONSTEXPR text_style& operator&=(
+ const text_style& rhs) {
+ return and_assign(rhs);
+ }
+
+ FMT_DEPRECATED_NONMSVC friend FMT_CONSTEXPR text_style
+ operator&(text_style lhs, const text_style& rhs) {
+ return lhs.and_assign(rhs);
+ }
+
+ FMT_CONSTEXPR bool has_foreground() const FMT_NOEXCEPT {
+ return set_foreground_color;
+ }
+ FMT_CONSTEXPR bool has_background() const FMT_NOEXCEPT {
+ return set_background_color;
+ }
+ FMT_CONSTEXPR bool has_emphasis() const FMT_NOEXCEPT {
+ return static_cast<uint8_t>(ems) != 0;
+ }
+ FMT_CONSTEXPR detail::color_type get_foreground() const FMT_NOEXCEPT {
+ FMT_ASSERT(has_foreground(), "no foreground specified for this style");
+ return foreground_color;
+ }
+ FMT_CONSTEXPR detail::color_type get_background() const FMT_NOEXCEPT {
+ FMT_ASSERT(has_background(), "no background specified for this style");
+ return background_color;
+ }
+ FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT {
+ FMT_ASSERT(has_emphasis(), "no emphasis specified for this style");
+ return ems;
+ }
+
+ private:
+ FMT_CONSTEXPR text_style(bool is_foreground,
+ detail::color_type text_color) FMT_NOEXCEPT
+ : set_foreground_color(),
+ set_background_color(),
+ ems() {
+ if (is_foreground) {
+ foreground_color = text_color;
+ set_foreground_color = true;
+ } else {
+ background_color = text_color;
+ set_background_color = true;
+ }
+ }
+
+ // DEPRECATED!
+ FMT_CONSTEXPR text_style& and_assign(const text_style& rhs) {
+ if (!set_foreground_color) {
+ set_foreground_color = rhs.set_foreground_color;
+ foreground_color = rhs.foreground_color;
+ } else if (rhs.set_foreground_color) {
+ if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
+ FMT_THROW(format_error("can't AND a terminal color"));
+ foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color;
+ }
+
+ if (!set_background_color) {
+ set_background_color = rhs.set_background_color;
+ background_color = rhs.background_color;
+ } else if (rhs.set_background_color) {
+ if (!background_color.is_rgb || !rhs.background_color.is_rgb)
+ FMT_THROW(format_error("can't AND a terminal color"));
+ background_color.value.rgb_color &= rhs.background_color.value.rgb_color;
+ }
+
+ ems = static_cast<emphasis>(static_cast<uint8_t>(ems) &
+ static_cast<uint8_t>(rhs.ems));
+ return *this;
+ }
+
+ friend FMT_CONSTEXPR_DECL text_style fg(detail::color_type foreground)
+ FMT_NOEXCEPT;
+
+ friend FMT_CONSTEXPR_DECL text_style bg(detail::color_type background)
+ FMT_NOEXCEPT;
+
+ detail::color_type foreground_color;
+ detail::color_type background_color;
+ bool set_foreground_color;
+ bool set_background_color;
+ emphasis ems;
+};
+
+/** Creates a text style from the foreground (text) color. */
+FMT_CONSTEXPR inline text_style fg(detail::color_type foreground) FMT_NOEXCEPT {
+ return text_style(true, foreground);
+}
+
+/** Creates a text style from the background color. */
+FMT_CONSTEXPR inline text_style bg(detail::color_type background) FMT_NOEXCEPT {
+ return text_style(false, background);
+}
+
+FMT_CONSTEXPR inline text_style operator|(emphasis lhs,
+ emphasis rhs) FMT_NOEXCEPT {
+ return text_style(lhs) | rhs;
+}
+
+FMT_BEGIN_DETAIL_NAMESPACE
+
+template <typename Char> struct ansi_color_escape {
+ FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color,
+ const char* esc) FMT_NOEXCEPT {
+ // If we have a terminal color, we need to output another escape code
+ // sequence.
+ if (!text_color.is_rgb) {
+ bool is_background = esc == string_view("\x1b[48;2;");
+ uint32_t value = text_color.value.term_color;
+ // Background ASCII codes are the same as the foreground ones but with
+ // 10 more.
+ if (is_background) value += 10u;
+
+ size_t index = 0;
+ buffer[index++] = static_cast<Char>('\x1b');
+ buffer[index++] = static_cast<Char>('[');
+
+ if (value >= 100u) {
+ buffer[index++] = static_cast<Char>('1');
+ value %= 100u;
+ }
+ buffer[index++] = static_cast<Char>('0' + value / 10u);
+ buffer[index++] = static_cast<Char>('0' + value % 10u);
+
+ buffer[index++] = static_cast<Char>('m');
+ buffer[index++] = static_cast<Char>('\0');
+ return;
+ }
+
+ for (int i = 0; i < 7; i++) {
+ buffer[i] = static_cast<Char>(esc[i]);
+ }
+ rgb color(text_color.value.rgb_color);
+ to_esc(color.r, buffer + 7, ';');
+ to_esc(color.g, buffer + 11, ';');
+ to_esc(color.b, buffer + 15, 'm');
+ buffer[19] = static_cast<Char>(0);
+ }
+ FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT {
+ uint8_t em_codes[4] = {};
+ uint8_t em_bits = static_cast<uint8_t>(em);
+ if (em_bits & static_cast<uint8_t>(emphasis::bold)) em_codes[0] = 1;
+ if (em_bits & static_cast<uint8_t>(emphasis::italic)) em_codes[1] = 3;
+ if (em_bits & static_cast<uint8_t>(emphasis::underline)) em_codes[2] = 4;
+ if (em_bits & static_cast<uint8_t>(emphasis::strikethrough))
+ em_codes[3] = 9;
+
+ size_t index = 0;
+ for (int i = 0; i < 4; ++i) {
+ if (!em_codes[i]) continue;
+ buffer[index++] = static_cast<Char>('\x1b');
+ buffer[index++] = static_cast<Char>('[');
+ buffer[index++] = static_cast<Char>('0' + em_codes[i]);
+ buffer[index++] = static_cast<Char>('m');
+ }
+ buffer[index++] = static_cast<Char>(0);
+ }
+ FMT_CONSTEXPR operator const Char*() const FMT_NOEXCEPT { return buffer; }
+
+ FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; }
+ FMT_CONSTEXPR_CHAR_TRAITS const Char* end() const FMT_NOEXCEPT {
+ return buffer + std::char_traits<Char>::length(buffer);
+ }
+
+ private:
+ Char buffer[7u + 3u * 4u + 1u];
+
+ static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out,
+ char delimiter) FMT_NOEXCEPT {
+ out[0] = static_cast<Char>('0' + c / 100);
+ out[1] = static_cast<Char>('0' + c / 10 % 10);
+ out[2] = static_cast<Char>('0' + c % 10);
+ out[3] = static_cast<Char>(delimiter);
+ }
+};
+
+template <typename Char>
+FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(
+ detail::color_type foreground) FMT_NOEXCEPT {
+ return ansi_color_escape<Char>(foreground, "\x1b[38;2;");
+}
+
+template <typename Char>
+FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(
+ detail::color_type background) FMT_NOEXCEPT {
+ return ansi_color_escape<Char>(background, "\x1b[48;2;");
+}
+
+template <typename Char>
+FMT_CONSTEXPR ansi_color_escape<Char> make_emphasis(emphasis em) FMT_NOEXCEPT {
+ return ansi_color_escape<Char>(em);
+}
+
+template <typename Char>
+inline void fputs(const Char* chars, FILE* stream) FMT_NOEXCEPT {
+ std::fputs(chars, stream);
+}
+
+template <>
+inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT {
+ std::fputws(chars, stream);
+}
+
+template <typename Char> inline void reset_color(FILE* stream) FMT_NOEXCEPT {
+ fputs("\x1b[0m", stream);
+}
+
+template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT {
+ fputs(L"\x1b[0m", stream);
+}
+
+template <typename Char>
+inline void reset_color(buffer<Char>& buffer) FMT_NOEXCEPT {
+ auto reset_color = string_view("\x1b[0m");
+ buffer.append(reset_color.begin(), reset_color.end());
+}
+
+template <typename Char>
+void vformat_to(buffer<Char>& buf, const text_style& ts,
+ basic_string_view<Char> format_str,
+ basic_format_args<buffer_context<type_identity_t<Char>>> args) {
+ bool has_style = false;
+ if (ts.has_emphasis()) {
+ has_style = true;
+ auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis());
+ buf.append(emphasis.begin(), emphasis.end());
+ }
+ if (ts.has_foreground()) {
+ has_style = true;
+ auto foreground = detail::make_foreground_color<Char>(ts.get_foreground());
+ buf.append(foreground.begin(), foreground.end());
+ }
+ if (ts.has_background()) {
+ has_style = true;
+ auto background = detail::make_background_color<Char>(ts.get_background());
+ buf.append(background.begin(), background.end());
+ }
+ detail::vformat_to(buf, format_str, args, {});
+ if (has_style) detail::reset_color<Char>(buf);
+}
+
+FMT_END_DETAIL_NAMESPACE
+
+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, to_string_view(format), args);
+ buf.push_back(Char(0));
+ detail::fputs(buf.data(), f);
+}
+
+/**
+ \rst
+ Formats a string and prints it to the specified file stream using ANSI
+ escape sequences to specify text formatting.
+
+ **Example**::
+
+ fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
+ "Elapsed time: {0:.2f} seconds", 1.23);
+ \endrst
+ */
+template <typename S, typename... Args,
+ FMT_ENABLE_IF(detail::is_string<S>::value)>
+void print(std::FILE* f, const text_style& ts, const S& format_str,
+ const Args&... args) {
+ vprint(f, ts, format_str,
+ fmt::make_args_checked<Args...>(format_str, args...));
+}
+
+/**
+ \rst
+ Formats a string and prints it to stdout using ANSI escape sequences to
+ specify text formatting.
+
+ **Example**::
+
+ fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
+ "Elapsed time: {0:.2f} seconds", 1.23);
+ \endrst
+ */
+template <typename S, typename... Args,
+ FMT_ENABLE_IF(detail::is_string<S>::value)>
+void print(const text_style& ts, const S& format_str, const Args&... args) {
+ return print(stdout, ts, format_str, args...);
+}
+
+template <typename S, typename Char = char_t<S>>
+inline std::basic_string<Char> vformat(
+ const text_style& ts, const S& format_str,
+ basic_format_args<buffer_context<type_identity_t<Char>>> args) {
+ basic_memory_buffer<Char> buf;
+ detail::vformat_to(buf, ts, to_string_view(format_str), args);
+ return fmt::to_string(buf);
+}
+
+/**
+ \rst
+ Formats arguments and returns the result as a string using ANSI
+ escape sequences to specify text formatting.
+
+ **Example**::
+
+ #include <fmt/color.h>
+ std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red),
+ "The answer is {}", 42);
+ \endrst
+*/
+template <typename S, typename... Args, typename Char = char_t<S>>
+inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
+ const Args&... args) {
+ return fmt::vformat(ts, to_string_view(format_str),
+ fmt::make_args_checked<Args...>(format_str, args...));
+}
+
+/**
+ Formats a string with the given text_style and writes the output to ``out``.
+ */
+template <typename OutputIt, typename Char,
+ FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
+OutputIt vformat_to(
+ OutputIt out, const text_style& ts, basic_string_view<Char> format_str,
+ 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);
+}
+
+/**
+ \rst
+ Formats arguments with the given text_style, writes the result to the output
+ iterator ``out`` and returns the iterator past the end of the output range.
+
+ **Example**::
+
+ std::vector<char> out;
+ fmt::format_to(std::back_inserter(out),
+ fmt::emphasis::bold | fg(fmt::color::red), "{}", 42);
+ \endrst
+*/
+template <typename OutputIt, typename S, typename... Args,
+ bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value&&
+ detail::is_string<S>::value>
+inline auto format_to(OutputIt out, const text_style& ts, const S& format_str,
+ Args&&... args) ->
+ typename std::enable_if<enable, OutputIt>::type {
+ return vformat_to(out, ts, to_string_view(format_str),
+ fmt::make_args_checked<Args...>(format_str, args...));
+}
+
+FMT_MODULE_EXPORT_END
+FMT_END_NAMESPACE
+
+#endif // FMT_COLOR_H_
diff --git a/wpiutil/src/main/native/fmtlib/include/fmt/compile.h b/wpiutil/src/main/native/fmtlib/include/fmt/compile.h
new file mode 100644
index 0000000..00000c9
--- /dev/null
+++ b/wpiutil/src/main/native/fmtlib/include/fmt/compile.h
@@ -0,0 +1,639 @@
+// Formatting library for C++ - experimental format string compilation
+//
+// Copyright (c) 2012 - present, Victor Zverovich and fmt contributors
+// All rights reserved.
+//
+// For the license information refer to format.h.
+
+#ifndef FMT_COMPILE_H_
+#define FMT_COMPILE_H_
+
+#include "format.h"
+
+FMT_BEGIN_NAMESPACE
+namespace detail {
+
+// An output iterator that counts the number of objects written to it and
+// discards them.
+class counting_iterator {
+ private:
+ size_t count_;
+
+ public:
+ using iterator_category = std::output_iterator_tag;
+ using difference_type = std::ptrdiff_t;
+ using pointer = void;
+ using reference = void;
+ using _Unchecked_type = counting_iterator; // Mark iterator as checked.
+
+ struct value_type {
+ template <typename T> void operator=(const T&) {}
+ };
+
+ counting_iterator() : count_(0) {}
+
+ size_t count() const { return count_; }
+
+ counting_iterator& operator++() {
+ ++count_;
+ return *this;
+ }
+ counting_iterator operator++(int) {
+ auto it = *this;
+ ++*this;
+ return it;
+ }
+
+ friend counting_iterator operator+(counting_iterator it, difference_type n) {
+ it.count_ += static_cast<size_t>(n);
+ return it;
+ }
+
+ value_type operator*() const { return {}; }
+};
+
+template <typename Char, typename InputIt>
+inline counting_iterator copy_str(InputIt begin, InputIt end,
+ counting_iterator it) {
+ 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;
+ using _Unchecked_type =
+ truncating_iterator_base; // Mark iterator as checked.
+
+ 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 {};
+
+template <typename S>
+struct is_compiled_string : std::is_base_of<compiled_string, S> {};
+
+/**
+ \rst
+ Converts a string literal *s* into a format string that will be parsed at
+ compile time and converted into efficient formatting code. Requires C++17
+ ``constexpr if`` compiler support.
+
+ **Example**::
+
+ // Converts 42 into std::string using the most efficient method and no
+ // runtime format string processing.
+ std::string s = fmt::format(FMT_COMPILE("{}"), 42);
+ \endrst
+ */
+#ifdef __cpp_if_constexpr
+# define FMT_COMPILE(s) \
+ FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit)
+#else
+# define FMT_COMPILE(s) FMT_STRING(s)
+#endif
+
+#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
+template <typename Char, size_t N,
+ fmt::detail_exported::fixed_string<Char, N> Str>
+struct udl_compiled_string : compiled_string {
+ using char_type = Char;
+ constexpr operator basic_string_view<char_type>() const {
+ return {Str.data, N - 1};
+ }
+};
+#endif
+
+template <typename T, typename... Tail>
+const T& first(const T& value, const Tail&...) {
+ return value;
+}
+
+#ifdef __cpp_if_constexpr
+template <typename... Args> struct type_list {};
+
+// Returns a reference to the argument at index N from [first, rest...].
+template <int N, typename T, typename... Args>
+constexpr const auto& get([[maybe_unused]] const T& first,
+ [[maybe_unused]] const Args&... rest) {
+ static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
+ if constexpr (N == 0)
+ return first;
+ else
+ return get<N - 1>(rest...);
+}
+
+template <typename Char, typename... Args>
+constexpr int get_arg_index_by_name(basic_string_view<Char> name,
+ type_list<Args...>) {
+ return get_arg_index_by_name<Args...>(name);
+}
+
+template <int N, typename> struct get_type_impl;
+
+template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> {
+ using type = remove_cvref_t<decltype(get<N>(std::declval<Args>()...))>;
+};
+
+template <int N, typename T>
+using get_type = typename get_type_impl<N, T>::type;
+
+template <typename T> struct is_compiled_format : std::false_type {};
+
+template <typename Char> struct text {
+ basic_string_view<Char> data;
+ using char_type = Char;
+
+ template <typename OutputIt, typename... Args>
+ constexpr OutputIt format(OutputIt out, const Args&...) const {
+ return write<Char>(out, data);
+ }
+};
+
+template <typename Char>
+struct is_compiled_format<text<Char>> : std::true_type {};
+
+template <typename Char>
+constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
+ size_t size) {
+ return {{&s[pos], size}};
+}
+
+template <typename Char> struct code_unit {
+ Char value;
+ using char_type = Char;
+
+ template <typename OutputIt, typename... Args>
+ constexpr OutputIt format(OutputIt out, const Args&...) const {
+ return write<Char>(out, value);
+ }
+};
+
+// This ensures that the argument type is convertible to `const T&`.
+template <typename T, int N, typename... Args>
+constexpr const T& get_arg_checked(const Args&... args) {
+ const auto& arg = get<N>(args...);
+ if constexpr (detail::is_named_arg<remove_cvref_t<decltype(arg)>>()) {
+ return arg.value;
+ } else {
+ return arg;
+ }
+}
+
+template <typename Char>
+struct is_compiled_format<code_unit<Char>> : std::true_type {};
+
+// A replacement field that refers to argument N.
+template <typename Char, typename T, int N> struct field {
+ using char_type = Char;
+
+ template <typename OutputIt, typename... Args>
+ constexpr OutputIt format(OutputIt out, const Args&... args) const {
+ return write<Char>(out, get_arg_checked<T, N>(args...));
+ }
+};
+
+template <typename Char, typename T, int N>
+struct is_compiled_format<field<Char, T, N>> : std::true_type {};
+
+// A replacement field that refers to argument with name.
+template <typename Char> struct runtime_named_field {
+ using char_type = Char;
+ basic_string_view<Char> name;
+
+ template <typename OutputIt, typename T>
+ constexpr static bool try_format_argument(
+ OutputIt& out,
+ // [[maybe_unused]] due to unused-but-set-parameter warning in GCC 7,8,9
+ [[maybe_unused]] basic_string_view<Char> arg_name, const T& arg) {
+ if constexpr (is_named_arg<typename std::remove_cv<T>::type>::value) {
+ if (arg_name == arg.name) {
+ out = write<Char>(out, arg.value);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ template <typename OutputIt, typename... Args>
+ constexpr OutputIt format(OutputIt out, const Args&... args) const {
+ bool found = (try_format_argument(out, name, args) || ...);
+ if (!found) {
+ throw format_error("argument with specified name is not found");
+ }
+ return out;
+ }
+};
+
+template <typename Char>
+struct is_compiled_format<runtime_named_field<Char>> : std::true_type {};
+
+// A replacement field that refers to argument N and has format specifiers.
+template <typename Char, typename T, int N> struct spec_field {
+ using char_type = Char;
+ formatter<T, Char> fmt;
+
+ template <typename OutputIt, typename... Args>
+ constexpr FMT_INLINE OutputIt format(OutputIt out,
+ const Args&... args) const {
+ const auto& vargs =
+ fmt::make_format_args<basic_format_context<OutputIt, Char>>(args...);
+ basic_format_context<OutputIt, Char> ctx(out, vargs);
+ return fmt.format(get_arg_checked<T, N>(args...), ctx);
+ }
+};
+
+template <typename Char, typename T, int N>
+struct is_compiled_format<spec_field<Char, T, N>> : std::true_type {};
+
+template <typename L, typename R> struct concat {
+ L lhs;
+ R rhs;
+ using char_type = typename L::char_type;
+
+ template <typename OutputIt, typename... Args>
+ constexpr OutputIt format(OutputIt out, const Args&... args) const {
+ out = lhs.format(out, args...);
+ return rhs.format(out, args...);
+ }
+};
+
+template <typename L, typename R>
+struct is_compiled_format<concat<L, R>> : std::true_type {};
+
+template <typename L, typename R>
+constexpr concat<L, R> make_concat(L lhs, R rhs) {
+ return {lhs, rhs};
+}
+
+struct unknown_format {};
+
+template <typename Char>
+constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) {
+ for (size_t size = str.size(); pos != size; ++pos) {
+ if (str[pos] == '{' || str[pos] == '}') break;
+ }
+ return pos;
+}
+
+template <typename Args, size_t POS, int ID, typename S>
+constexpr auto compile_format_string(S format_str);
+
+template <typename Args, size_t POS, int ID, typename T, typename S>
+constexpr auto parse_tail(T head, S format_str) {
+ if constexpr (POS !=
+ basic_string_view<typename S::char_type>(format_str).size()) {
+ constexpr auto tail = compile_format_string<Args, POS, ID>(format_str);
+ if constexpr (std::is_same<remove_cvref_t<decltype(tail)>,
+ unknown_format>())
+ return tail;
+ else
+ return make_concat(head, tail);
+ } else {
+ return head;
+ }
+}
+
+template <typename T, typename Char> struct parse_specs_result {
+ formatter<T, Char> fmt;
+ size_t end;
+ int next_arg_id;
+};
+
+constexpr int 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 = basic_format_parse_context<Char>(str, {}, next_arg_id);
+ auto f = formatter<T, Char>();
+ auto end = f.parse(ctx);
+ return {f, pos + fmt::detail::to_unsigned(end - str.data()) + 1,
+ next_arg_id == 0 ? manual_indexing_id : ctx.next_arg_id()};
+}
+
+template <typename Char> struct arg_id_handler {
+ arg_ref<Char> arg_id;
+
+ constexpr int operator()() {
+ FMT_ASSERT(false, "handler cannot be used with automatic indexing");
+ return 0;
+ }
+ constexpr int operator()(int id) {
+ arg_id = arg_ref<Char>(id);
+ return 0;
+ }
+ constexpr int operator()(basic_string_view<Char> id) {
+ arg_id = arg_ref<Char>(id);
+ return 0;
+ }
+
+ constexpr void on_error(const char* message) { throw format_error(message); }
+};
+
+template <typename Char> struct parse_arg_id_result {
+ arg_ref<Char> arg_id;
+ const Char* arg_id_end;
+};
+
+template <int ID, typename Char>
+constexpr auto parse_arg_id(const Char* begin, const Char* end) {
+ auto handler = arg_id_handler<Char>{arg_ref<Char>{}};
+ auto arg_id_end = parse_arg_id(begin, end, handler);
+ return parse_arg_id_result<Char>{handler.arg_id, arg_id_end};
+}
+
+template <typename T, typename Enable = void> struct field_type {
+ using type = remove_cvref_t<T>;
+};
+
+template <typename T>
+struct field_type<T, enable_if_t<detail::is_named_arg<T>::value>> {
+ using type = remove_cvref_t<decltype(T::value)>;
+};
+
+template <typename T, typename Args, size_t END_POS, int ARG_INDEX, int NEXT_ID,
+ typename S>
+constexpr auto parse_replacement_field_then_tail(S format_str) {
+ using char_type = typename S::char_type;
+ constexpr auto str = basic_string_view<char_type>(format_str);
+ constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type();
+ if constexpr (c == '}') {
+ return parse_tail<Args, END_POS + 1, NEXT_ID>(
+ field<char_type, typename field_type<T>::type, ARG_INDEX>(),
+ format_str);
+ } else if constexpr (c == ':') {
+ constexpr auto result = parse_specs<typename field_type<T>::type>(
+ str, END_POS + 1, NEXT_ID == manual_indexing_id ? 0 : NEXT_ID);
+ return parse_tail<Args, result.end, result.next_arg_id>(
+ spec_field<char_type, typename field_type<T>::type, ARG_INDEX>{
+ result.fmt},
+ format_str);
+ }
+}
+
+// Compiles a non-empty format string and returns the compiled representation
+// or unknown_format() on unrecognized input.
+template <typename Args, size_t POS, int ID, typename S>
+constexpr auto compile_format_string(S format_str) {
+ using char_type = typename S::char_type;
+ constexpr auto str = basic_string_view<char_type>(format_str);
+ if constexpr (str[POS] == '{') {
+ if constexpr (POS + 1 == str.size())
+ throw format_error("unmatched '{' in format string");
+ if constexpr (str[POS + 1] == '{') {
+ return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
+ } else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') {
+ static_assert(ID != manual_indexing_id,
+ "cannot switch from manual to automatic argument indexing");
+ constexpr auto next_id =
+ ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
+ return parse_replacement_field_then_tail<get_type<ID, Args>, Args,
+ POS + 1, ID, next_id>(
+ format_str);
+ } else {
+ constexpr auto arg_id_result =
+ parse_arg_id<ID>(str.data() + POS + 1, str.data() + str.size());
+ constexpr auto arg_id_end_pos = arg_id_result.arg_id_end - str.data();
+ constexpr char_type c =
+ arg_id_end_pos != str.size() ? str[arg_id_end_pos] : char_type();
+ static_assert(c == '}' || c == ':', "missing '}' in format string");
+ if constexpr (arg_id_result.arg_id.kind == arg_id_kind::index) {
+ static_assert(
+ ID == manual_indexing_id || ID == 0,
+ "cannot switch from automatic to manual argument indexing");
+ constexpr auto arg_index = arg_id_result.arg_id.val.index;
+ return parse_replacement_field_then_tail<get_type<arg_index, Args>,
+ Args, arg_id_end_pos,
+ arg_index, manual_indexing_id>(
+ format_str);
+ } 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) {
+ 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 (str[POS] == '}') {
+ if constexpr (POS + 1 == str.size())
+ throw format_error("unmatched '}' in format string");
+ return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
+ } else {
+ constexpr auto end = parse_text(str, POS + 1);
+ if constexpr (end - POS > 1) {
+ return parse_tail<Args, end, ID>(make_text(str, POS, end - POS),
+ format_str);
+ } else {
+ return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]},
+ format_str);
+ }
+ }
+}
+
+template <typename... Args, typename S,
+ FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
+constexpr auto compile(S format_str) {
+ constexpr auto str = basic_string_view<typename S::char_type>(format_str);
+ if constexpr (str.size() == 0) {
+ return detail::make_text(str, 0, 0);
+ } else {
+ constexpr auto result =
+ detail::compile_format_string<detail::type_list<Args...>, 0, 0>(
+ format_str);
+ return result;
+ }
+}
+#endif // __cpp_if_constexpr
+} // namespace detail
+
+FMT_MODULE_EXPORT_BEGIN
+
+#ifdef __cpp_if_constexpr
+
+template <typename CompiledFormat, typename... Args,
+ typename Char = typename CompiledFormat::char_type,
+ FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
+FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf,
+ const Args&... args) {
+ auto s = std::basic_string<Char>();
+ cf.format(std::back_inserter(s), args...);
+ return s;
+}
+
+template <typename OutputIt, typename CompiledFormat, typename... Args,
+ FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
+constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf,
+ const Args&... args) {
+ return cf.format(out, args...);
+}
+
+template <typename S, typename... Args,
+ FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
+FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
+ Args&&... args) {
+ if constexpr (std::is_same<typename S::char_type, char>::value) {
+ constexpr auto str = basic_string_view<typename S::char_type>(S());
+ if constexpr (str.size() == 2 && str[0] == '{' && str[1] == '}') {
+ const auto& first = detail::first(args...);
+ if constexpr (detail::is_named_arg<
+ remove_cvref_t<decltype(first)>>::value) {
+ return fmt::to_string(first.value);
+ } else {
+ return fmt::to_string(first);
+ }
+ }
+ }
+ constexpr auto compiled = detail::compile<Args...>(S());
+ if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
+ detail::unknown_format>()) {
+ return format(static_cast<basic_string_view<typename S::char_type>>(S()),
+ std::forward<Args>(args)...);
+ } else {
+ return format(compiled, std::forward<Args>(args)...);
+ }
+}
+
+template <typename OutputIt, typename S, typename... Args,
+ FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
+FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
+ constexpr auto compiled = detail::compile<Args...>(S());
+ if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
+ detail::unknown_format>()) {
+ return format_to(out,
+ static_cast<basic_string_view<typename S::char_type>>(S()),
+ std::forward<Args>(args)...);
+ } else {
+ return format_to(out, compiled, std::forward<Args>(args)...);
+ }
+}
+#endif
+
+template <typename OutputIt, typename S, typename... Args,
+ 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 = format_to(detail::truncating_iterator<OutputIt>(out, n), format_str,
+ std::forward<Args>(args)...);
+ return {it.base(), it.count()};
+}
+
+template <typename S, typename... Args,
+ FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
+size_t formatted_size(const S& format_str, const Args&... args) {
+ return format_to(detail::counting_iterator(), format_str, args...).count();
+}
+
+template <typename S, typename... Args,
+ FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
+void print(std::FILE* f, const S& format_str, const Args&... args) {
+ memory_buffer buffer;
+ format_to(std::back_inserter(buffer), format_str, args...);
+ detail::print(f, {buffer.data(), buffer.size()});
+}
+
+template <typename S, typename... Args,
+ FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
+void print(const S& format_str, const Args&... args) {
+ print(stdout, format_str, args...);
+}
+
+#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
+inline namespace literals {
+template <detail_exported::fixed_string Str>
+constexpr detail::udl_compiled_string<
+ remove_cvref_t<decltype(Str.data[0])>,
+ sizeof(Str.data) / sizeof(decltype(Str.data[0])), Str>
+operator""_cf() {
+ return {};
+}
+} // namespace literals
+#endif
+
+FMT_MODULE_EXPORT_END
+FMT_END_NAMESPACE
+
+#endif // FMT_COMPILE_H_
diff --git a/wpiutil/src/main/native/fmtlib/include/fmt/core.h b/wpiutil/src/main/native/fmtlib/include/fmt/core.h
new file mode 100644
index 0000000..d058398
--- /dev/null
+++ b/wpiutil/src/main/native/fmtlib/include/fmt/core.h
@@ -0,0 +1,3002 @@
+// Formatting library for C++ - the core API for char/UTF-8
+//
+// Copyright (c) 2012 - present, Victor Zverovich
+// All rights reserved.
+//
+// For the license information refer to format.h.
+
+#ifndef FMT_CORE_H_
+#define FMT_CORE_H_
+
+#include <cstdio> // std::FILE
+#include <cstring>
+#include <iterator>
+#include <limits>
+#include <string>
+#include <type_traits>
+
+// The fmt library version in the form major * 10000 + minor * 100 + patch.
+#define FMT_VERSION 80001
+
+#ifdef __clang__
+# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
+#else
+# define FMT_CLANG_VERSION 0
+#endif
+
+#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)
+# define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
+# define FMT_GCC_PRAGMA(arg) _Pragma(arg)
+#else
+# define FMT_GCC_VERSION 0
+# define FMT_GCC_PRAGMA(arg)
+#endif
+
+#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
+# define FMT_HAS_GXX_CXX11 FMT_GCC_VERSION
+#else
+# define FMT_HAS_GXX_CXX11 0
+#endif
+
+#if defined(__INTEL_COMPILER)
+# define FMT_ICC_VERSION __INTEL_COMPILER
+#else
+# define FMT_ICC_VERSION 0
+#endif
+
+#ifdef __NVCC__
+# define FMT_NVCC __NVCC__
+#else
+# define FMT_NVCC 0
+#endif
+
+#ifdef _MSC_VER
+# define FMT_MSC_VER _MSC_VER
+# define FMT_MSC_WARNING(...) __pragma(warning(__VA_ARGS__))
+#else
+# define FMT_MSC_VER 0
+# define FMT_MSC_WARNING(...)
+#endif
+
+#ifdef __has_feature
+# define FMT_HAS_FEATURE(x) __has_feature(x)
+#else
+# define FMT_HAS_FEATURE(x) 0
+#endif
+
+#if defined(__has_include) && \
+ (!defined(__INTELLISENSE__) || FMT_MSC_VER > 1900) && \
+ (!FMT_ICC_VERSION || FMT_ICC_VERSION >= 1600)
+# define FMT_HAS_INCLUDE(x) __has_include(x)
+#else
+# define FMT_HAS_INCLUDE(x) 0
+#endif
+
+#ifdef __has_cpp_attribute
+# define FMT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x)
+#else
+# define FMT_HAS_CPP_ATTRIBUTE(x) 0
+#endif
+
+#define FMT_HAS_CPP14_ATTRIBUTE(attribute) \
+ (__cplusplus >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute))
+
+#define FMT_HAS_CPP17_ATTRIBUTE(attribute) \
+ (__cplusplus >= 201703L && FMT_HAS_CPP_ATTRIBUTE(attribute))
+
+// Check if relaxed C++14 constexpr is supported.
+// GCC doesn't allow throw in constexpr until version 6 (bug 67371).
+#ifndef FMT_USE_CONSTEXPR
+# define FMT_USE_CONSTEXPR \
+ (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VER >= 1910 || \
+ (FMT_GCC_VERSION >= 600 && __cplusplus >= 201402L)) && \
+ !FMT_NVCC && !FMT_ICC_VERSION
+#endif
+#if FMT_USE_CONSTEXPR
+# define FMT_CONSTEXPR constexpr
+# define FMT_CONSTEXPR_DECL constexpr
+#else
+# define FMT_CONSTEXPR
+# define FMT_CONSTEXPR_DECL
+#endif
+
+// Check if constexpr std::char_traits<>::compare,length is supported.
+#if defined(__GLIBCXX__)
+# if __cplusplus >= 201703L && defined(_GLIBCXX_RELEASE) && \
+ _GLIBCXX_RELEASE >= 7 // GCC 7+ libstdc++ has _GLIBCXX_RELEASE.
+# define FMT_CONSTEXPR_CHAR_TRAITS constexpr
+# endif
+#elif defined(_LIBCPP_VERSION) && __cplusplus >= 201703L && \
+ _LIBCPP_VERSION >= 4000
+# define FMT_CONSTEXPR_CHAR_TRAITS constexpr
+#elif FMT_MSC_VER >= 1914 && _MSVC_LANG >= 201703L
+# define FMT_CONSTEXPR_CHAR_TRAITS constexpr
+#endif
+#ifndef FMT_CONSTEXPR_CHAR_TRAITS
+# define FMT_CONSTEXPR_CHAR_TRAITS
+#endif
+
+#ifndef FMT_OVERRIDE
+# if FMT_HAS_FEATURE(cxx_override_control) || \
+ (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900
+# define FMT_OVERRIDE override
+# else
+# define FMT_OVERRIDE
+# endif
+#endif
+
+// Check if exceptions are disabled.
+#ifndef FMT_EXCEPTIONS
+# if (defined(__GNUC__) && !defined(__EXCEPTIONS)) || \
+ FMT_MSC_VER && !_HAS_EXCEPTIONS
+# define FMT_EXCEPTIONS 0
+# else
+# define FMT_EXCEPTIONS 1
+# endif
+#endif
+
+// Define FMT_USE_NOEXCEPT to make fmt use noexcept (C++11 feature).
+#ifndef FMT_USE_NOEXCEPT
+# define FMT_USE_NOEXCEPT 0
+#endif
+
+#if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \
+ (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900
+# define FMT_DETECTED_NOEXCEPT noexcept
+# define FMT_HAS_CXX11_NOEXCEPT 1
+#else
+# define FMT_DETECTED_NOEXCEPT throw()
+# define FMT_HAS_CXX11_NOEXCEPT 0
+#endif
+
+#ifndef FMT_NOEXCEPT
+# if FMT_EXCEPTIONS || FMT_HAS_CXX11_NOEXCEPT
+# define FMT_NOEXCEPT FMT_DETECTED_NOEXCEPT
+# else
+# define FMT_NOEXCEPT
+# endif
+#endif
+
+// [[noreturn]] is disabled on MSVC and NVCC because of bogus unreachable code
+// warnings.
+#if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VER && \
+ !FMT_NVCC
+# define FMT_NORETURN [[noreturn]]
+#else
+# define FMT_NORETURN
+#endif
+
+#ifndef FMT_MAYBE_UNUSED
+# if FMT_HAS_CPP17_ATTRIBUTE(maybe_unused)
+# define FMT_MAYBE_UNUSED [[maybe_unused]]
+# else
+# define FMT_MAYBE_UNUSED
+# endif
+#endif
+
+#if __cplusplus == 201103L || __cplusplus == 201402L
+# if defined(__INTEL_COMPILER) || defined(__PGI)
+# define FMT_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
+#elif FMT_HAS_CPP17_ATTRIBUTE(fallthrough) || \
+ (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
+# define FMT_FALLTHROUGH [[fallthrough]]
+#else
+# define FMT_FALLTHROUGH
+#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))
+# else
+# define FMT_INLINE inline
+# endif
+#endif
+
+#ifndef FMT_USE_INLINE_NAMESPACES
+# if FMT_HAS_FEATURE(cxx_inline_namespaces) || FMT_GCC_VERSION >= 404 || \
+ (FMT_MSC_VER >= 1900 && (!defined(_MANAGED) || !_MANAGED))
+# define FMT_USE_INLINE_NAMESPACES 1
+# else
+# define FMT_USE_INLINE_NAMESPACES 0
+# endif
+#endif
+
+#ifndef FMT_BEGIN_NAMESPACE
+# if FMT_USE_INLINE_NAMESPACES
+# define FMT_INLINE_NAMESPACE inline namespace
+# define FMT_END_NAMESPACE \
+ } \
+ }
+# else
+# define FMT_INLINE_NAMESPACE namespace
+# define FMT_END_NAMESPACE \
+ } \
+ using namespace v8; \
+ }
+# endif
+# define FMT_BEGIN_NAMESPACE \
+ namespace fmt { \
+ FMT_INLINE_NAMESPACE v8 {
+#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 }
+#endif
+
+#if !defined(FMT_HEADER_ONLY) && defined(_WIN32)
+# define FMT_CLASS_API FMT_MSC_WARNING(suppress : 4275)
+# ifdef FMT_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(__GNUC__) || defined(__clang__)
+# define FMT_API __attribute__((visibility("default")))
+# endif
+# endif
+#endif
+#ifndef FMT_API
+# define FMT_API
+#endif
+
+#if FMT_GCC_VERSION
+# define FMT_GCC_VISIBILITY_HIDDEN __attribute__((visibility("hidden")))
+#else
+# define FMT_GCC_VISIBILITY_HIDDEN
+#endif
+
+// libc++ supports string_view in pre-c++17.
+#if (FMT_HAS_INCLUDE(<string_view>) && \
+ (__cplusplus > 201402L || defined(_LIBCPP_VERSION))) || \
+ (defined(_MSVC_LANG) && _MSVC_LANG > 201402L && _MSC_VER >= 1910)
+# include <string_view>
+# define FMT_USE_STRING_VIEW
+#elif FMT_HAS_INCLUDE("experimental/string_view") && __cplusplus >= 201402L
+# include <experimental/string_view>
+# define FMT_USE_EXPERIMENTAL_STRING_VIEW
+#endif
+
+#ifndef FMT_UNICODE
+# define FMT_UNICODE !FMT_MSC_VER
+#endif
+
+#ifndef FMT_CONSTEVAL
+# if ((FMT_GCC_VERSION >= 1000 || FMT_CLANG_VERSION >= 1101) && \
+ __cplusplus > 201703L) || \
+ (defined(__cpp_consteval) && \
+ !FMT_MSC_VER) // consteval is broken in MSVC.
+# define FMT_CONSTEVAL consteval
+# define FMT_HAS_CONSTEVAL
+# else
+# define FMT_CONSTEVAL
+# endif
+#endif
+
+#ifndef FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
+# if defined(__cpp_nontype_template_args) && \
+ ((FMT_GCC_VERSION >= 903 && __cplusplus >= 201709L) || \
+ __cpp_nontype_template_args >= 201911L)
+# define FMT_USE_NONTYPE_TEMPLATE_PARAMETERS 1
+# else
+# define FMT_USE_NONTYPE_TEMPLATE_PARAMETERS 0
+# endif
+#endif
+
+// Enable minimal optimizations for more compact code in debug mode.
+FMT_GCC_PRAGMA("GCC push_options")
+#ifndef __OPTIMIZE__
+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>
+using enable_if_t = typename std::enable_if<B, T>::type;
+template <bool B, typename T, typename F>
+using conditional_t = typename std::conditional<B, T, F>::type;
+template <bool B> using bool_constant = std::integral_constant<bool, B>;
+template <typename T>
+using remove_reference_t = typename std::remove_reference<T>::type;
+template <typename T>
+using remove_cvref_t = typename std::remove_cv<remove_reference_t<T>>::type;
+template <typename T> struct type_identity { using type = T; };
+template <typename T> using type_identity_t = typename type_identity<T>::type;
+
+struct monostate {
+ constexpr monostate() {}
+};
+
+// Suppress "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.
+template <typename... T> FMT_CONSTEXPR void ignore_unused(const T&...) {}
+
+// An enable_if helper to be used in template parameters which results in much
+// shorter symbols: https://godbolt.org/z/sWw4vP. Extra parentheses are needed
+// to workaround a bug in MSVC 2019 (see #1140 and #1186).
+#ifdef FMT_DOC
+# define FMT_ENABLE_IF(...)
+#else
+# define FMT_ENABLE_IF(...) enable_if_t<(__VA_ARGS__), int> = 0
+#endif
+
+FMT_BEGIN_DETAIL_NAMESPACE
+
+constexpr FMT_INLINE auto is_constant_evaluated() FMT_NOEXCEPT -> bool {
+#ifdef __cpp_lib_is_constant_evaluated
+ return std::is_constant_evaluated();
+#else
+ return false;
+#endif
+}
+
+// A function to suppress "conditional expression is constant" warnings.
+template <typename T> constexpr auto const_check(T value) -> T { return value; }
+
+FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
+ const char* message);
+
+#ifndef FMT_ASSERT
+# ifdef NDEBUG
+// FMT_ASSERT is not empty to avoid -Werror=empty-body.
+# define FMT_ASSERT(condition, message) \
+ ::fmt::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)))
+# endif
+#endif
+
+#if defined(FMT_USE_STRING_VIEW)
+template <typename Char> using std_string_view = std::basic_string_view<Char>;
+#elif defined(FMT_USE_EXPERIMENTAL_STRING_VIEW)
+template <typename Char>
+using std_string_view = std::experimental::basic_string_view<Char>;
+#else
+template <typename T> struct std_string_view {};
+#endif
+
+#ifdef FMT_USE_INT128
+// Do nothing.
+#elif defined(__SIZEOF_INT128__) && !FMT_NVCC && \
+ !(FMT_CLANG_VERSION && FMT_MSC_VER)
+# define FMT_USE_INT128 1
+using int128_t = __int128_t;
+using uint128_t = __uint128_t;
+template <typename T> inline auto convert_for_visit(T value) -> T {
+ return value;
+}
+#else
+# define FMT_USE_INT128 0
+#endif
+#if !FMT_USE_INT128
+enum class int128_t {};
+enum class uint128_t {};
+// Reduce template instantiations.
+template <typename T> inline auto convert_for_visit(T) -> monostate {
+ return {};
+}
+#endif
+
+// Casts a nonnegative integer to unsigned.
+template <typename Int>
+FMT_CONSTEXPR auto to_unsigned(Int value) ->
+ typename std::make_unsigned<Int>::type {
+ FMT_ASSERT(value >= 0, "negative value");
+ return static_cast<typename std::make_unsigned<Int>::type>(value);
+}
+
+FMT_MSC_WARNING(suppress : 4566) constexpr unsigned char micro[] = "\u00B5";
+
+constexpr auto is_utf8() -> bool {
+ // Avoid buggy sign extensions in MSVC's constant evaluation mode.
+ // https://developercommunity.visualstudio.com/t/C-difference-in-behavior-for-unsigned/1233612
+ using uchar = unsigned char;
+ return FMT_UNICODE || (sizeof(micro) == 3 && uchar(micro[0]) == 0xC2 &&
+ uchar(micro[1]) == 0xB5);
+}
+FMT_END_DETAIL_NAMESPACE
+
+/**
+ An implementation of ``std::basic_string_view`` for pre-C++17. It provides a
+ subset of the API. ``fmt::basic_string_view`` is used for format strings even
+ if ``std::string_view`` is available to prevent issues when a library is
+ compiled with a different ``-std`` option than the client code (which is not
+ recommended).
+ */
+template <typename Char> class basic_string_view {
+ private:
+ const Char* data_;
+ size_t size_;
+
+ public:
+ using value_type = Char;
+ using iterator = const Char*;
+
+ constexpr basic_string_view() FMT_NOEXCEPT : data_(nullptr), size_(0) {}
+
+ /** Constructs a string reference object from a C string and a size. */
+ constexpr basic_string_view(const Char* s, size_t count) FMT_NOEXCEPT
+ : data_(s),
+ size_(count) {}
+
+ /**
+ \rst
+ Constructs a string reference object from a C string computing
+ the size with ``std::char_traits<Char>::length``.
+ \endrst
+ */
+ FMT_CONSTEXPR_CHAR_TRAITS
+ FMT_INLINE
+ basic_string_view(const Char* s) : data_(s) {
+ if (detail::const_check(std::is_same<Char, char>::value &&
+ !detail::is_constant_evaluated()))
+ size_ = std::strlen(reinterpret_cast<const char*>(s));
+ else
+ size_ = std::char_traits<Char>::length(s);
+ }
+
+ /** Constructs a string reference from a ``std::basic_string`` object. */
+ template <typename Traits, typename Alloc>
+ FMT_CONSTEXPR basic_string_view(
+ const std::basic_string<Char, Traits, Alloc>& s) FMT_NOEXCEPT
+ : data_(s.data()),
+ size_(s.size()) {}
+
+ template <typename S, FMT_ENABLE_IF(std::is_same<
+ S, detail::std_string_view<Char>>::value)>
+ FMT_CONSTEXPR basic_string_view(S s) FMT_NOEXCEPT : data_(s.data()),
+ size_(s.size()) {}
+
+ /** Returns a pointer to the string data. */
+ constexpr auto data() const -> const Char* { return data_; }
+
+ /** Returns the string size. */
+ constexpr auto size() const -> size_t { return size_; }
+
+ constexpr auto begin() const -> iterator { return data_; }
+ constexpr auto end() const -> iterator { return data_ + size_; }
+
+ constexpr auto operator[](size_t pos) const -> const Char& {
+ return data_[pos];
+ }
+
+ FMT_CONSTEXPR void remove_prefix(size_t n) {
+ data_ += n;
+ size_ -= n;
+ }
+
+ // 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_;
+ int result = std::char_traits<Char>::compare(data_, other.data_, str_size);
+ if (result == 0)
+ result = size_ == other.size_ ? 0 : (size_ < other.size_ ? -1 : 1);
+ return result;
+ }
+
+ FMT_CONSTEXPR_CHAR_TRAITS friend auto operator==(basic_string_view lhs,
+ basic_string_view rhs)
+ -> bool {
+ return lhs.compare(rhs) == 0;
+ }
+ friend auto operator!=(basic_string_view lhs, basic_string_view rhs) -> bool {
+ return lhs.compare(rhs) != 0;
+ }
+ friend auto operator<(basic_string_view lhs, basic_string_view rhs) -> bool {
+ return lhs.compare(rhs) < 0;
+ }
+ friend auto operator<=(basic_string_view lhs, basic_string_view rhs) -> bool {
+ return lhs.compare(rhs) <= 0;
+ }
+ friend auto operator>(basic_string_view lhs, basic_string_view rhs) -> bool {
+ return lhs.compare(rhs) > 0;
+ }
+ friend auto operator>=(basic_string_view lhs, basic_string_view rhs) -> bool {
+ return lhs.compare(rhs) >= 0;
+ }
+};
+
+using string_view = basic_string_view<char>;
+
+/** Specifies if ``T`` is a character type. Can be specialized by users. */
+template <typename T> struct is_char : std::false_type {};
+template <> struct is_char<char> : std::true_type {};
+
+// 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;
+}
+template <typename Char, typename Traits, typename Alloc>
+inline auto to_string_view(const std::basic_string<Char, Traits, Alloc>& s)
+ -> basic_string_view<Char> {
+ return s;
+}
+template <typename Char>
+constexpr auto to_string_view(basic_string_view<Char> s)
+ -> basic_string_view<Char> {
+ return s;
+}
+template <typename Char,
+ FMT_ENABLE_IF(!std::is_empty<detail::std_string_view<Char>>::value)>
+inline auto to_string_view(detail::std_string_view<Char> s)
+ -> basic_string_view<Char> {
+ return s;
+}
+
+// A base class for compile-time strings. It is defined in the fmt namespace to
+// make formatting functions visible via ADL, e.g. format(FMT_STRING("{}"), 42).
+struct compile_string {};
+
+template <typename S>
+struct is_compile_string : std::is_base_of<compile_string, S> {};
+
+template <typename S, FMT_ENABLE_IF(is_compile_string<S>::value)>
+constexpr auto to_string_view(const S& s)
+ -> basic_string_view<typename S::char_type> {
+ return basic_string_view<typename S::char_type>(s);
+}
+
+FMT_BEGIN_DETAIL_NAMESPACE
+
+void to_string_view(...);
+using fmt::v8::to_string_view;
+
+// 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.
+template <typename S>
+struct is_string : std::is_class<decltype(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>> {
+ using result = decltype(to_string_view(std::declval<S>()));
+ using type = typename result::value_type;
+};
+
+// Reports a compile-time error if S is not a valid format string.
+template <typename..., typename S, FMT_ENABLE_IF(!is_compile_string<S>::value)>
+FMT_INLINE void check_format_string(const S&) {
+#ifdef FMT_ENFORCE_COMPILE_STRING
+ static_assert(is_compile_string<S>::value,
+ "FMT_ENFORCE_COMPILE_STRING requires all format strings to use "
+ "FMT_STRING.");
+#endif
+}
+template <typename..., typename S, FMT_ENABLE_IF(is_compile_string<S>::value)>
+void check_format_string(S);
+
+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 FMT_API void on_error(const char* message);
+};
+FMT_END_DETAIL_NAMESPACE
+
+/** String's character type. */
+template <typename S> using char_t = typename detail::char_t_impl<S>::type;
+
+/**
+ \rst
+ Parsing context consisting of a format string range being parsed and an
+ argument counter for automatic indexing.
+ 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 {
+ private:
+ basic_string_view<Char> format_str_;
+ int next_arg_id_;
+
+ public:
+ using char_type = Char;
+ using iterator = typename basic_string_view<Char>::iterator;
+
+ 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) {}
+
+ /**
+ Returns an iterator to the beginning of the format string range being
+ parsed.
+ */
+ constexpr auto begin() const FMT_NOEXCEPT -> iterator {
+ return format_str_.begin();
+ }
+
+ /**
+ Returns an iterator past the end of the format string range being parsed.
+ */
+ constexpr auto end() const FMT_NOEXCEPT -> iterator {
+ return format_str_.end();
+ }
+
+ /** Advances the begin iterator to ``it``. */
+ FMT_CONSTEXPR void advance_to(iterator it) {
+ format_str_.remove_prefix(detail::to_unsigned(it - begin()));
+ }
+
+ /**
+ Reports an error if using the manual argument indexing; otherwise returns
+ the next argument index and switches to the automatic indexing.
+ */
+ FMT_CONSTEXPR auto next_arg_id() -> int {
+ // Don't check if the argument id is valid to avoid overhead and because it
+ // will be checked during formatting anyway.
+ if (next_arg_id_ >= 0) return next_arg_id_++;
+ on_error("cannot switch from manual to automatic argument indexing");
+ return 0;
+ }
+
+ /**
+ Reports an error if using the automatic argument indexing; otherwise
+ switches to the manual indexing.
+ */
+ FMT_CONSTEXPR void check_arg_id(int) {
+ if (next_arg_id_ > 0)
+ on_error("cannot switch from automatic to manual argument indexing");
+ else
+ next_arg_id_ = -1;
+ }
+
+ FMT_CONSTEXPR void check_arg_id(basic_string_view<Char>) {}
+
+ FMT_CONSTEXPR void on_error(const char* message) {
+ ErrorHandler::on_error(message);
+ }
+
+ constexpr auto error_handler() const -> ErrorHandler { return *this; }
+};
+
+using format_parse_context = basic_format_parse_context<char>;
+
+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
+
+// Extracts a reference to the container from back_insert_iterator.
+template <typename Container>
+inline auto get_container(std::back_insert_iterator<Container> it)
+ -> Container& {
+ using bi_iterator = std::back_insert_iterator<Container>;
+ struct accessor : bi_iterator {
+ accessor(bi_iterator iter) : bi_iterator(iter) {}
+ using bi_iterator::container;
+ };
+ return *accessor(it).container;
+}
+
+template <typename Char, typename InputIt, typename OutputIt>
+FMT_CONSTEXPR auto copy_str(InputIt begin, InputIt end, OutputIt out)
+ -> OutputIt {
+ while (begin != end) *out++ = static_cast<Char>(*begin++);
+ return out;
+}
+
+template <typename Char, FMT_ENABLE_IF(std::is_same<Char, char>::value)>
+FMT_CONSTEXPR auto copy_str(const Char* begin, const Char* end, Char* out)
+ -> Char* {
+ if (is_constant_evaluated())
+ return copy_str<Char, const Char*, Char*>(begin, end, out);
+ auto size = to_unsigned(end - begin);
+ memcpy(out, begin, size);
+ return out + size;
+}
+
+/**
+ \rst
+ A contiguous memory buffer with an optional growing ability. It is an internal
+ class and shouldn't be used directly, only via `~fmt::basic_memory_buffer`.
+ \endrst
+ */
+template <typename T> class buffer {
+ private:
+ T* ptr_;
+ size_t size_;
+ size_t capacity_;
+
+ protected:
+ // Don't initialize ptr_ since it is not accessed to save a few cycles.
+ FMT_MSC_WARNING(suppress : 26495)
+ buffer(size_t sz) FMT_NOEXCEPT : size_(sz), capacity_(sz) {}
+
+ buffer(T* p = nullptr, size_t sz = 0, size_t cap = 0) FMT_NOEXCEPT
+ : ptr_(p),
+ size_(sz),
+ capacity_(cap) {}
+
+ ~buffer() = default;
+ buffer(buffer&&) = default;
+
+ /** Sets the buffer data and capacity. */
+ void set(T* buf_data, size_t buf_capacity) FMT_NOEXCEPT {
+ ptr_ = buf_data;
+ capacity_ = buf_capacity;
+ }
+
+ /** Increases the buffer capacity to hold at least *capacity* elements. */
+ virtual void grow(size_t capacity) = 0;
+
+ public:
+ using value_type = T;
+ using const_reference = const T&;
+
+ buffer(const buffer&) = delete;
+ void operator=(const buffer&) = delete;
+
+ auto begin() FMT_NOEXCEPT -> T* { return ptr_; }
+ auto end() FMT_NOEXCEPT -> T* { return ptr_ + size_; }
+
+ auto begin() const FMT_NOEXCEPT -> const T* { return ptr_; }
+ auto end() const FMT_NOEXCEPT -> const T* { return ptr_ + size_; }
+
+ /** Returns the size of this buffer. */
+ auto size() const FMT_NOEXCEPT -> size_t { return size_; }
+
+ /** Returns the capacity of this buffer. */
+ auto capacity() const FMT_NOEXCEPT -> size_t { return capacity_; }
+
+ /** Returns a pointer to the buffer data. */
+ auto data() FMT_NOEXCEPT -> T* { return ptr_; }
+
+ /** Returns a pointer to the buffer data. */
+ auto data() const FMT_NOEXCEPT -> const T* { return ptr_; }
+
+ /** Clears this buffer. */
+ void clear() { size_ = 0; }
+
+ // Tries resizing the buffer to contain *count* elements. If T is a POD type
+ // the new elements may not be initialized.
+ void try_resize(size_t count) {
+ try_reserve(count);
+ size_ = count <= capacity_ ? count : capacity_;
+ }
+
+ // Tries increasing the buffer capacity to *new_capacity*. It can increase the
+ // capacity by a smaller amount than requested but guarantees there is space
+ // for at least one additional element either by increasing the capacity or by
+ // flushing the buffer if it is full.
+ void try_reserve(size_t new_capacity) {
+ if (new_capacity > capacity_) grow(new_capacity);
+ }
+
+ void push_back(const T& value) {
+ try_reserve(size_ + 1);
+ ptr_[size_++] = value;
+ }
+
+ /** Appends data to the end of the buffer. */
+ template <typename U> void append(const U* begin, const U* end);
+
+ template <typename I> auto operator[](I index) -> T& { return ptr_[index]; }
+ template <typename I> auto operator[](I index) const -> const T& {
+ return ptr_[index];
+ }
+};
+
+struct buffer_traits {
+ explicit buffer_traits(size_t) {}
+ auto count() const -> size_t { return 0; }
+ auto limit(size_t size) -> size_t { return size; }
+};
+
+class fixed_buffer_traits {
+ private:
+ size_t count_ = 0;
+ size_t limit_;
+
+ public:
+ explicit fixed_buffer_traits(size_t limit) : limit_(limit) {}
+ auto count() const -> size_t { return count_; }
+ auto limit(size_t size) -> size_t {
+ size_t n = limit_ > count_ ? limit_ - count_ : 0;
+ count_ += size;
+ return size < n ? size : n;
+ }
+};
+
+// A buffer that writes to an output iterator when flushed.
+template <typename OutputIt, typename T, typename Traits = buffer_traits>
+class iterator_buffer final : public Traits, public buffer<T> {
+ private:
+ OutputIt out_;
+ enum { buffer_size = 256 };
+ T data_[buffer_size];
+
+ protected:
+ void grow(size_t) final FMT_OVERRIDE {
+ if (this->size() == buffer_size) flush();
+ }
+
+ void flush() {
+ auto size = this->size();
+ this->clear();
+ out_ = copy_str<T>(data_, data_ + this->limit(size), out_);
+ }
+
+ public:
+ explicit iterator_buffer(OutputIt out, size_t n = buffer_size)
+ : Traits(n), buffer<T>(data_, 0, buffer_size), out_(out) {}
+ iterator_buffer(iterator_buffer&& other)
+ : Traits(other), buffer<T>(data_, 0, buffer_size), out_(other.out_) {}
+ ~iterator_buffer() { flush(); }
+
+ auto out() -> OutputIt {
+ flush();
+ return out_;
+ }
+ auto count() const -> size_t { return Traits::count() + this->size(); }
+};
+
+template <typename T> class iterator_buffer<T*, T> final : public buffer<T> {
+ protected:
+ void grow(size_t) final FMT_OVERRIDE {}
+
+ public:
+ explicit iterator_buffer(T* out, size_t = 0) : buffer<T>(out, 0, ~size_t()) {}
+
+ auto out() -> T* { return &*this->end(); }
+};
+
+// A buffer that writes to a container with the contiguous storage.
+template <typename Container>
+class iterator_buffer<std::back_insert_iterator<Container>,
+ enable_if_t<is_contiguous<Container>::value,
+ typename Container::value_type>>
+ final : public buffer<typename Container::value_type> {
+ private:
+ Container& container_;
+
+ protected:
+ void grow(size_t capacity) final FMT_OVERRIDE {
+ container_.resize(capacity);
+ this->set(&container_[0], capacity);
+ }
+
+ public:
+ explicit iterator_buffer(Container& c)
+ : buffer<typename Container::value_type>(c.size()), container_(c) {}
+ explicit iterator_buffer(std::back_insert_iterator<Container> out, size_t = 0)
+ : iterator_buffer(get_container(out)) {}
+ auto out() -> std::back_insert_iterator<Container> {
+ return std::back_inserter(container_);
+ }
+};
+
+// A buffer that counts the number of code units written discarding the output.
+template <typename T = char> class counting_buffer final : public buffer<T> {
+ private:
+ enum { buffer_size = 256 };
+ T data_[buffer_size];
+ size_t count_ = 0;
+
+ protected:
+ void grow(size_t) final FMT_OVERRIDE {
+ if (this->size() != buffer_size) return;
+ count_ += this->size();
+ this->clear();
+ }
+
+ public:
+ counting_buffer() : buffer<T>(data_, 0, buffer_size) {}
+
+ auto count() -> size_t { return count_ + this->size(); }
+};
+
+template <typename T>
+using buffer_appender = conditional_t<std::is_same<T, char>::value, appender,
+ std::back_insert_iterator<buffer<T>>>;
+
+// Maps an output iterator to a buffer.
+template <typename T, typename OutputIt>
+auto get_buffer(OutputIt out) -> iterator_buffer<OutputIt, T> {
+ return iterator_buffer<OutputIt, T>(out);
+}
+
+template <typename Buffer>
+auto get_iterator(Buffer& buf) -> 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 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 =
+ std::is_constructible<fallback_formatter<T, Char>>;
+
+struct view {};
+
+template <typename Char, typename T> struct named_arg : view {
+ const Char* name;
+ const T& value;
+ named_arg(const Char* n, const T& v) : name(n), value(v) {}
+};
+
+template <typename Char> struct named_arg_info {
+ const Char* name;
+ int id;
+};
+
+template <typename T, typename Char, size_t NUM_ARGS, size_t NUM_NAMED_ARGS>
+struct arg_data {
+ // args_[0].named_args points to named_args_ to avoid bloating format_args.
+ // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
+ T args_[1 + (NUM_ARGS != 0 ? NUM_ARGS : +1)];
+ named_arg_info<Char> named_args_[NUM_NAMED_ARGS];
+
+ template <typename... U>
+ arg_data(const U&... init) : args_{T(named_args_, NUM_NAMED_ARGS), init...} {}
+ arg_data(const arg_data& other) = delete;
+ auto args() const -> const T* { return args_ + 1; }
+ auto named_args() -> named_arg_info<Char>* { return named_args_; }
+};
+
+template <typename T, typename Char, size_t NUM_ARGS>
+struct arg_data<T, Char, NUM_ARGS, 0> {
+ // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
+ T args_[NUM_ARGS != 0 ? NUM_ARGS : +1];
+
+ template <typename... U>
+ FMT_CONSTEXPR FMT_INLINE arg_data(const U&... init) : args_{init...} {}
+ FMT_CONSTEXPR FMT_INLINE auto args() const -> const T* { return args_; }
+ FMT_CONSTEXPR FMT_INLINE auto named_args() -> std::nullptr_t {
+ return nullptr;
+ }
+};
+
+template <typename Char>
+inline void init_named_args(named_arg_info<Char>*, int, int) {}
+
+template <typename T> struct is_named_arg : std::false_type {};
+template <typename T> struct is_statically_named_arg : std::false_type {};
+
+template <typename T, typename Char>
+struct is_named_arg<named_arg<Char, T>> : std::true_type {};
+
+template <typename Char, typename T, typename... Tail,
+ FMT_ENABLE_IF(!is_named_arg<T>::value)>
+void init_named_args(named_arg_info<Char>* named_args, int arg_count,
+ int named_arg_count, const T&, const Tail&... args) {
+ init_named_args(named_args, arg_count + 1, named_arg_count, args...);
+}
+
+template <typename Char, typename T, typename... Tail,
+ FMT_ENABLE_IF(is_named_arg<T>::value)>
+void init_named_args(named_arg_info<Char>* named_args, int arg_count,
+ int named_arg_count, const T& arg, const Tail&... args) {
+ named_args[named_arg_count++] = {arg.name, arg_count};
+ init_named_args(named_args, arg_count + 1, named_arg_count, args...);
+}
+
+template <typename... Args>
+FMT_CONSTEXPR FMT_INLINE void init_named_args(std::nullptr_t, int, int,
+ const Args&...) {}
+
+template <bool B = false> constexpr auto count() -> size_t { return B ? 1 : 0; }
+template <bool B1, bool B2, bool... Tail> constexpr auto count() -> size_t {
+ return (B1 ? 1 : 0) + count<B2, Tail...>();
+}
+
+template <typename... Args> constexpr auto count_named_args() -> size_t {
+ return count<is_named_arg<Args>::value...>();
+}
+
+enum class type {
+ none_type,
+ // Integer types should go first,
+ int_type,
+ uint_type,
+ long_long_type,
+ ulong_long_type,
+ int128_type,
+ uint128_type,
+ bool_type,
+ char_type,
+ last_integer_type = char_type,
+ // followed by floating-point types.
+ float_type,
+ double_type,
+ long_double_type,
+ last_numeric_type = long_double_type,
+ cstring_type,
+ string_type,
+ pointer_type,
+ custom_type
+};
+
+// Maps core type T to the corresponding type enum constant.
+template <typename T, typename Char>
+struct type_constant : std::integral_constant<type, type::custom_type> {};
+
+#define FMT_TYPE_CONSTANT(Type, constant) \
+ template <typename Char> \
+ struct type_constant<Type, Char> \
+ : std::integral_constant<type, type::constant> {}
+
+FMT_TYPE_CONSTANT(int, int_type);
+FMT_TYPE_CONSTANT(unsigned, uint_type);
+FMT_TYPE_CONSTANT(long long, long_long_type);
+FMT_TYPE_CONSTANT(unsigned long long, ulong_long_type);
+FMT_TYPE_CONSTANT(int128_t, int128_type);
+FMT_TYPE_CONSTANT(uint128_t, uint128_type);
+FMT_TYPE_CONSTANT(bool, bool_type);
+FMT_TYPE_CONSTANT(Char, char_type);
+FMT_TYPE_CONSTANT(float, float_type);
+FMT_TYPE_CONSTANT(double, double_type);
+FMT_TYPE_CONSTANT(long double, long_double_type);
+FMT_TYPE_CONSTANT(const Char*, cstring_type);
+FMT_TYPE_CONSTANT(basic_string_view<Char>, string_type);
+FMT_TYPE_CONSTANT(const void*, pointer_type);
+
+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;
+}
+
+template <typename Char> struct string_value {
+ const Char* data;
+ size_t size;
+};
+
+template <typename Char> struct named_arg_value {
+ const named_arg_info<Char>* data;
+ size_t size;
+};
+
+template <typename Context> struct custom_value {
+ using parse_context = typename Context::parse_context_type;
+ const void* value;
+ void (*format)(const void* arg, parse_context& parse_ctx, Context& ctx);
+};
+
+// A formatting argument value.
+template <typename Context> class value {
+ public:
+ using char_type = typename Context::char_type;
+
+ union {
+ monostate no_value;
+ int int_value;
+ unsigned uint_value;
+ long long long_long_value;
+ unsigned long long ulong_long_value;
+ int128_t int128_value;
+ uint128_t uint128_value;
+ bool bool_value;
+ char_type char_value;
+ float float_value;
+ double double_value;
+ long double long_double_value;
+ const void* pointer;
+ string_value<char_type> string;
+ custom_value<Context> custom;
+ named_arg_value<char_type> named_args;
+ };
+
+ constexpr FMT_INLINE value() : no_value() {}
+ constexpr FMT_INLINE value(int val) : int_value(val) {}
+ constexpr FMT_INLINE value(unsigned val) : uint_value(val) {}
+ constexpr FMT_INLINE value(long long val) : long_long_value(val) {}
+ constexpr FMT_INLINE value(unsigned long long val) : ulong_long_value(val) {}
+ FMT_INLINE value(int128_t val) : int128_value(val) {}
+ FMT_INLINE value(uint128_t val) : uint128_value(val) {}
+ FMT_INLINE value(float val) : float_value(val) {}
+ FMT_INLINE value(double val) : double_value(val) {}
+ FMT_INLINE value(long double val) : long_double_value(val) {}
+ constexpr FMT_INLINE value(bool val) : bool_value(val) {}
+ constexpr FMT_INLINE value(char_type val) : char_value(val) {}
+ FMT_CONSTEXPR FMT_INLINE value(const char_type* val) {
+ string.data = val;
+ if (is_constant_evaluated()) string.size = {};
+ }
+ FMT_CONSTEXPR FMT_INLINE value(basic_string_view<char_type> val) {
+ string.data = val.data();
+ string.size = val.size();
+ }
+ FMT_INLINE value(const void* val) : pointer(val) {}
+ 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(const T& val) {
+ custom.value = &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<
+ T, conditional_t<has_formatter<T, Context>::value,
+ typename Context::template formatter_type<T>,
+ fallback_formatter<T, char_type>>>;
+ }
+
+ private:
+ // Formats an argument of a custom type, such as a user-defined class.
+ template <typename T, typename Formatter>
+ static void format_custom_arg(const void* arg,
+ typename Context::parse_context_type& parse_ctx,
+ Context& ctx) {
+ Formatter f;
+ parse_ctx.advance_to(f.parse(parse_ctx));
+ ctx.advance_to(f.format(*static_cast<const T*>(arg), ctx));
+ }
+};
+
+template <typename Context, typename T>
+FMT_CONSTEXPR auto make_arg(const 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>;
+
+struct unformattable {};
+
+// Maps formatting arguments to core types.
+template <typename Context> struct arg_mapper {
+ using char_type = typename Context::char_type;
+
+ FMT_CONSTEXPR FMT_INLINE auto map(signed char val) -> int { return val; }
+ FMT_CONSTEXPR FMT_INLINE auto map(unsigned char val) -> unsigned {
+ return val;
+ }
+ FMT_CONSTEXPR FMT_INLINE auto map(short val) -> int { return val; }
+ FMT_CONSTEXPR FMT_INLINE auto map(unsigned short val) -> unsigned {
+ return val;
+ }
+ FMT_CONSTEXPR FMT_INLINE auto map(int val) -> int { return val; }
+ FMT_CONSTEXPR FMT_INLINE auto map(unsigned val) -> unsigned { return val; }
+ FMT_CONSTEXPR FMT_INLINE auto map(long val) -> long_type { return val; }
+ FMT_CONSTEXPR FMT_INLINE auto map(unsigned long val) -> ulong_type {
+ return val;
+ }
+ FMT_CONSTEXPR FMT_INLINE auto map(long long val) -> long long { return val; }
+ FMT_CONSTEXPR FMT_INLINE auto map(unsigned long long val)
+ -> unsigned long long {
+ return val;
+ }
+ FMT_CONSTEXPR FMT_INLINE auto map(int128_t val) -> int128_t { return val; }
+ FMT_CONSTEXPR FMT_INLINE auto map(uint128_t val) -> uint128_t { return val; }
+ FMT_CONSTEXPR FMT_INLINE auto map(bool val) -> bool { return val; }
+
+ template <typename T, FMT_ENABLE_IF(is_char<T>::value)>
+ FMT_CONSTEXPR FMT_INLINE auto map(T val) -> char_type {
+ static_assert(
+ std::is_same<T, char>::value || std::is_same<T, char_type>::value,
+ "mixing character types is disallowed");
+ return val;
+ }
+
+ FMT_CONSTEXPR FMT_INLINE auto map(float val) -> float { return val; }
+ FMT_CONSTEXPR FMT_INLINE auto map(double val) -> double { return val; }
+ FMT_CONSTEXPR FMT_INLINE auto map(long double val) -> long double {
+ return val;
+ }
+
+ FMT_CONSTEXPR FMT_INLINE auto map(char_type* val) -> const char_type* {
+ return val;
+ }
+ FMT_CONSTEXPR FMT_INLINE auto map(const char_type* val) -> const char_type* {
+ return val;
+ }
+ template <typename T, FMT_ENABLE_IF(is_string<T>::value)>
+ FMT_CONSTEXPR FMT_INLINE auto map(const T& val)
+ -> basic_string_view<char_type> {
+ static_assert(std::is_same<char_type, char_t<T>>::value,
+ "mixing character types is disallowed");
+ return to_string_view(val);
+ }
+ template <typename T,
+ FMT_ENABLE_IF(
+ std::is_constructible<basic_string_view<char_type>, T>::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_constructible<std_string_view<char_type>, T>::value &&
+ !std::is_constructible<basic_string_view<char_type>, T>::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(const signed char* val) -> const char* {
+ static_assert(std::is_same<char_type, char>::value, "invalid string type");
+ return reinterpret_cast<const char*>(val);
+ }
+ FMT_CONSTEXPR FMT_INLINE auto map(const unsigned char* val) -> const char* {
+ static_assert(std::is_same<char_type, char>::value, "invalid string type");
+ return reinterpret_cast<const char*>(val);
+ }
+ FMT_CONSTEXPR FMT_INLINE auto map(signed char* val) -> const char* {
+ const auto* const_val = val;
+ return map(const_val);
+ }
+ FMT_CONSTEXPR FMT_INLINE auto map(unsigned char* val) -> const char* {
+ const auto* const_val = val;
+ return map(const_val);
+ }
+
+ FMT_CONSTEXPR FMT_INLINE auto map(void* val) -> const void* { return val; }
+ FMT_CONSTEXPR FMT_INLINE auto map(const void* val) -> const void* {
+ return val;
+ }
+ FMT_CONSTEXPR FMT_INLINE auto map(std::nullptr_t val) -> const void* {
+ return val;
+ }
+
+ // We use SFINAE instead of a const T* parameter to avoid conflicting with
+ // the C array overload.
+ template <typename T>
+ FMT_CONSTEXPR auto map(T) -> enable_if_t<std::is_pointer<T>::value, int> {
+ // 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.
+ static_assert(!sizeof(T), "formatting of non-void pointers is disallowed");
+ return 0;
+ }
+
+ template <typename T, std::size_t N>
+ FMT_CONSTEXPR FMT_INLINE auto map(const T (&values)[N]) -> const T (&)[N] {
+ return values;
+ }
+
+ template <typename T,
+ FMT_ENABLE_IF(std::is_enum<T>::value &&
+ !has_formatter<T, Context>::value &&
+ !has_fallback_formatter<T, char_type>::value)>
+ FMT_CONSTEXPR FMT_INLINE auto map(const T& val)
+ -> decltype(std::declval<arg_mapper>().map(
+ static_cast<typename std::underlying_type<T>::type>(val))) {
+ return map(static_cast<typename std::underlying_type<T>::type>(val));
+ }
+ template <typename T,
+ FMT_ENABLE_IF(!is_string<T>::value && !is_char<T>::value &&
+ (has_formatter<T, Context>::value ||
+ has_fallback_formatter<T, char_type>::value))>
+ FMT_CONSTEXPR FMT_INLINE auto map(const T& val) -> const T& {
+ return 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)) {
+ return map(named_arg.value);
+ }
+
+ auto map(...) -> unformattable { return {}; }
+};
+
+// A type constant after applying arg_mapper<Context>.
+template <typename T, typename Context>
+using mapped_type_constant =
+ type_constant<decltype(arg_mapper<Context>().map(std::declval<const T&>())),
+ typename Context::char_type>;
+
+enum { packed_arg_bits = 4 };
+// Maximum number of arguments with packed types.
+enum { max_packed_args = 62 / packed_arg_bits };
+enum : unsigned long long { is_unpacked_bit = 1ULL << 63 };
+enum : unsigned long long { has_named_args_bit = 1ULL << 62 };
+
+FMT_END_DETAIL_NAMESPACE
+
+// 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 T>
+ friend auto get_buffer(appender out) -> detail::buffer<char>& {
+ return detail::get_container(out);
+ }
+
+ public:
+ using std::back_insert_iterator<detail::buffer<char>>::back_insert_iterator;
+ appender(base it) : base(it) {}
+ using _Unchecked_type = appender; // Mark iterator as checked.
+
+ auto operator++() -> appender& {
+ base::operator++();
+ return *this;
+ }
+
+ auto operator++(int) -> appender {
+ auto tmp = *this;
+ ++*this;
+ return tmp;
+ }
+};
+
+// A formatting argument. It is a trivially copyable/constructible type to
+// allow storage in basic_memory_buffer.
+template <typename Context> class basic_format_arg {
+ private:
+ detail::value<Context> value_;
+ detail::type type_;
+
+ template <typename ContextType, typename T>
+ friend FMT_CONSTEXPR auto detail::make_arg(const T& value)
+ -> basic_format_arg<ContextType>;
+
+ template <typename Visitor, typename Ctx>
+ friend FMT_CONSTEXPR auto visit_format_arg(Visitor&& vis,
+ const basic_format_arg<Ctx>& arg)
+ -> decltype(vis(0));
+
+ friend class basic_format_args<Context>;
+ friend class dynamic_format_arg_store<Context>;
+
+ using char_type = typename Context::char_type;
+
+ template <typename T, typename Char, size_t NUM_ARGS, size_t NUM_NAMED_ARGS>
+ friend struct detail::arg_data;
+
+ basic_format_arg(const detail::named_arg_info<char_type>* args, size_t size)
+ : value_(args, size) {}
+
+ public:
+ class handle {
+ public:
+ explicit handle(detail::custom_value<Context> custom) : custom_(custom) {}
+
+ void format(typename Context::parse_context_type& parse_ctx,
+ Context& ctx) const {
+ custom_.format(custom_.value, parse_ctx, ctx);
+ }
+
+ private:
+ detail::custom_value<Context> custom_;
+ };
+
+ constexpr basic_format_arg() : type_(detail::type::none_type) {}
+
+ constexpr explicit operator bool() const FMT_NOEXCEPT {
+ return type_ != detail::type::none_type;
+ }
+
+ auto type() const -> detail::type { return type_; }
+
+ auto is_integral() const -> bool { return detail::is_integral_type(type_); }
+ auto is_arithmetic() const -> bool {
+ return detail::is_arithmetic_type(type_);
+ }
+};
+
+/**
+ \rst
+ Visits an argument dispatching to the appropriate visit method based on
+ the argument type. For example, if the argument type is ``double`` then
+ ``vis(value)`` will be called with the value of type ``double``.
+ \endrst
+ */
+template <typename Visitor, typename Context>
+FMT_CONSTEXPR FMT_INLINE auto visit_format_arg(
+ Visitor&& vis, const basic_format_arg<Context>& arg) -> decltype(vis(0)) {
+ switch (arg.type_) {
+ case detail::type::none_type:
+ break;
+ case detail::type::int_type:
+ return vis(arg.value_.int_value);
+ case detail::type::uint_type:
+ return vis(arg.value_.uint_value);
+ case detail::type::long_long_type:
+ return vis(arg.value_.long_long_value);
+ case detail::type::ulong_long_type:
+ return vis(arg.value_.ulong_long_value);
+ case detail::type::int128_type:
+ return vis(detail::convert_for_visit(arg.value_.int128_value));
+ case detail::type::uint128_type:
+ return vis(detail::convert_for_visit(arg.value_.uint128_value));
+ case detail::type::bool_type:
+ return vis(arg.value_.bool_value);
+ case detail::type::char_type:
+ return vis(arg.value_.char_value);
+ case detail::type::float_type:
+ return vis(arg.value_.float_value);
+ case detail::type::double_type:
+ return vis(arg.value_.double_value);
+ case detail::type::long_double_type:
+ return vis(arg.value_.long_double_value);
+ case detail::type::cstring_type:
+ return vis(arg.value_.string.data);
+ case detail::type::string_type:
+ using sv = basic_string_view<typename Context::char_type>;
+ return vis(sv(arg.value_.string.data, arg.value_.string.size));
+ case detail::type::pointer_type:
+ return vis(arg.value_.pointer);
+ case detail::type::custom_type:
+ return vis(typename basic_format_arg<Context>::handle(arg.value_.custom));
+ }
+ 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;
+}
+
+#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 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 FMT_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 auto make_arg(const T& value) -> basic_format_arg<Context> {
+ basic_format_arg<Context> arg;
+ arg.type_ = mapped_type_constant<T, Context>::value;
+ arg.value_ = arg_mapper<Context>().map(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(const T& val) -> value<Context> {
+ const auto& arg = arg_mapper<Context>().map(val);
+ static_assert(
+ !std::is_same<decltype(arg), const unformattable&>::value,
+ "Cannot format an argument. To make type T formattable provide a "
+ "formatter<T> specialization: https://fmt.dev/latest/api.html#udt");
+ return {arg};
+}
+
+template <bool IS_PACKED, typename Context, type, typename T,
+ FMT_ENABLE_IF(!IS_PACKED)>
+inline auto make_arg(const 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_;
+ detail::locale_ref loc_;
+
+ public:
+ using iterator = OutputIt;
+ using format_arg = basic_format_arg<basic_format_context>;
+ using parse_context_type = basic_format_parse_context<Char>;
+ template <typename T> using formatter_type = formatter<T, char_type>;
+
+ 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.
+ */
+ constexpr basic_format_context(
+ OutputIt out, basic_format_args<basic_format_context> ctx_args,
+ detail::locale_ref loc = detail::locale_ref())
+ : 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 {
+ return args_.get(name);
+ }
+ FMT_CONSTEXPR auto arg_id(basic_string_view<char_type> name) -> int {
+ return args_.get_id(name);
+ }
+ auto args() const -> const basic_format_args<basic_format_context>& {
+ return args_;
+ }
+
+ FMT_CONSTEXPR auto error_handler() -> detail::error_handler { return {}; }
+ void on_error(const char* message) { error_handler().on_error(message); }
+
+ // Returns an iterator to the beginning of the output range.
+ FMT_CONSTEXPR auto out() -> iterator { return out_; }
+
+ // Advances the begin iterator to ``it``.
+ void advance_to(iterator it) {
+ if (!detail::is_back_insert_iterator<iterator>()) out_ = it;
+ }
+
+ FMT_CONSTEXPR auto locale() -> detail::locale_ref { return loc_; }
+};
+
+template <typename Char>
+using buffer_context =
+ 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_same<decltype(detail::arg_mapper<buffer_context<Char>>().map(
+ std::declval<T>())),
+ detail::unformattable>::value &&
+ !detail::has_fallback_formatter<T, Char>::value>;
+
+/**
+ \rst
+ An array of references to arguments. It can be implicitly converted into
+ `~fmt::basic_format_args` for passing into type-erased formatting functions
+ such as `~fmt::vformat`.
+ \endrst
+ */
+template <typename Context, typename... Args>
+class format_arg_store
+#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
+ // Workaround a GCC template argument substitution bug.
+ : public basic_format_args<Context>
+#endif
+{
+ private:
+ static const size_t num_args = sizeof...(Args);
+ static const 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>,
+ basic_format_arg<Context>>;
+
+ detail::arg_data<value_type, typename Context::char_type, num_args,
+ num_named_args>
+ data_;
+
+ friend class basic_format_args<Context>;
+
+ static constexpr unsigned long long desc =
+ (is_packed ? detail::encode_types<Context, Args...>()
+ : detail::is_unpacked_bit | num_args) |
+ (num_named_args != 0
+ ? static_cast<unsigned long long>(detail::has_named_args_bit)
+ : 0);
+
+ public:
+ FMT_CONSTEXPR FMT_INLINE format_arg_store(const Args&... 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<Args, Context>::value>(args)...} {
+ detail::init_named_args(data_.named_args(), 0, 0, args...);
+ }
+};
+
+/**
+ \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`.
+ See `~fmt::arg` for lifetime considerations.
+ \endrst
+ */
+template <typename Context = format_context, typename... Args>
+constexpr auto make_format_args(const Args&... args)
+ -> format_arg_store<Context, Args...> {
+ return {args...};
+}
+
+/**
+ \rst
+ Returns a named argument to be used in a formatting function.
+ It should only be used in a call to a formatting function or
+ `dynamic_format_arg_store::push_back`.
+
+ **Example**::
+
+ fmt::print("Elapsed time: {s:.2f} seconds", fmt::arg("s", 1.23));
+ \endrst
+ */
+template <typename Char, typename T>
+inline auto arg(const Char* name, const T& arg) -> detail::named_arg<Char, T> {
+ static_assert(!detail::is_named_arg<T>(), "nested named arguments");
+ return {name, arg};
+}
+
+/**
+ \rst
+ A view of a collection of formatting arguments. To avoid lifetime issues it
+ should only be used as a parameter type in type-erased functions such as
+ ``vformat``::
+
+ void vlog(string_view format_str, format_args args); // OK
+ format_args args = make_format_args(42); // Error: dangling reference
+ \endrst
+ */
+template <typename Context> class basic_format_args {
+ public:
+ using size_type = int;
+ using format_arg = basic_format_arg<Context>;
+
+ private:
+ // A descriptor that contains information about formatting arguments.
+ // If the number of arguments is less or equal to max_packed_args then
+ // argument types are passed in the descriptor. This reduces binary code size
+ // per formatting function call.
+ unsigned long long desc_;
+ union {
+ // If is_packed() returns true then argument values are stored in values_;
+ // otherwise they are stored in args_. This is done to improve cache
+ // locality and reduce compiled code size since storing larger objects
+ // may require more code (at least on x86-64) even if the same amount of
+ // data is actually copied to stack. It saves ~10% on the bloat test.
+ const detail::value<Context>* values_;
+ const format_arg* args_;
+ };
+
+ constexpr auto is_packed() const -> bool {
+ return (desc_ & detail::is_unpacked_bit) == 0;
+ }
+ auto has_named_args() const -> bool {
+ return (desc_ & detail::has_named_args_bit) != 0;
+ }
+
+ FMT_CONSTEXPR auto type(int index) const -> detail::type {
+ int shift = index * detail::packed_arg_bits;
+ unsigned int mask = (1 << detail::packed_arg_bits) - 1;
+ return static_cast<detail::type>((desc_ >> shift) & mask);
+ }
+
+ constexpr FMT_INLINE basic_format_args(unsigned long long desc,
+ const detail::value<Context>* values)
+ : desc_(desc), values_(values) {}
+ constexpr basic_format_args(unsigned long long desc, const format_arg* args)
+ : desc_(desc), args_(args) {}
+
+ public:
+ constexpr basic_format_args() : desc_(0), args_(nullptr) {}
+
+ /**
+ \rst
+ Constructs a `basic_format_args` object from `~fmt::format_arg_store`.
+ \endrst
+ */
+ template <typename... Args>
+ constexpr FMT_INLINE basic_format_args(
+ const format_arg_store<Context, Args...>& store)
+ : basic_format_args(format_arg_store<Context, Args...>::desc,
+ store.data_.args()) {}
+
+ /**
+ \rst
+ Constructs a `basic_format_args` object from
+ `~fmt::dynamic_format_arg_store`.
+ \endrst
+ */
+ constexpr FMT_INLINE basic_format_args(
+ const dynamic_format_arg_store<Context>& store)
+ : basic_format_args(store.get_types(), store.data()) {}
+
+ /**
+ \rst
+ Constructs a `basic_format_args` object from a dynamic set of arguments.
+ \endrst
+ */
+ constexpr basic_format_args(const format_arg* args, int count)
+ : basic_format_args(detail::is_unpacked_bit | detail::to_unsigned(count),
+ args) {}
+
+ /** Returns the argument with the specified id. */
+ FMT_CONSTEXPR auto get(int id) const -> format_arg {
+ format_arg arg;
+ if (!is_packed()) {
+ if (id < max_size()) arg = args_[id];
+ return arg;
+ }
+ if (id >= detail::max_packed_args) return arg;
+ arg.type_ = type(id);
+ if (arg.type_ == detail::type::none_type) return arg;
+ arg.value_ = values_[id];
+ return arg;
+ }
+
+ template <typename Char>
+ auto get(basic_string_view<Char> name) const -> format_arg {
+ int id = get_id(name);
+ return id >= 0 ? get(id) : format_arg();
+ }
+
+ template <typename Char>
+ auto get_id(basic_string_view<Char> name) const -> int {
+ if (!has_named_args()) return -1;
+ const auto& named_args =
+ (is_packed() ? values_[-1] : args_[-1].value_).named_args;
+ for (size_t i = 0; i < named_args.size; ++i) {
+ if (named_args.data[i].name == name) return named_args.data[i].id;
+ }
+ return -1;
+ }
+
+ auto max_size() const -> int {
+ unsigned long long max_packed = detail::max_packed_args;
+ return static_cast<int>(is_packed() ? max_packed
+ : desc_ & ~detail::is_unpacked_bit);
+ }
+};
+
+/** 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>;
+
+// We cannot use enum classes as bit fields because of a gcc bug
+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414.
+namespace align {
+enum type { none, left, right, center, numeric };
+}
+using align_t = align::type;
+namespace sign {
+enum type { none, minus, plus, space };
+}
+using sign_t = sign::type;
+
+FMT_BEGIN_DETAIL_NAMESPACE
+
+void throw_format_error(const char* message);
+
+// Workaround an array initialization issue in gcc 4.8.
+template <typename Char> struct fill_t {
+ private:
+ enum { max_size = 4 };
+ Char data_[max_size] = {Char(' '), Char(0), Char(0), Char(0)};
+ unsigned char size_ = 1;
+
+ public:
+ FMT_CONSTEXPR void operator=(basic_string_view<Char> s) {
+ auto size = s.size();
+ if (size > max_size) return throw_format_error("invalid fill");
+ for (size_t i = 0; i < size; ++i) data_[i] = s[i];
+ size_ = static_cast<unsigned char>(size);
+ }
+
+ constexpr auto size() const -> size_t { return size_; }
+ constexpr auto data() const -> const Char* { return data_; }
+
+ FMT_CONSTEXPR auto operator[](size_t index) -> Char& { return data_[index]; }
+ FMT_CONSTEXPR auto operator[](size_t index) const -> const Char& {
+ return data_[index];
+ }
+};
+FMT_END_DETAIL_NAMESPACE
+
+// Format specifiers for built-in and string types.
+template <typename Char> struct basic_format_specs {
+ int width;
+ int precision;
+ char type;
+ align_t align : 4;
+ sign_t sign : 3;
+ bool alt : 1; // Alternate form ('#').
+ bool localized : 1;
+ detail::fill_t<Char> fill;
+
+ constexpr basic_format_specs()
+ : width(0),
+ precision(-1),
+ type(0),
+ align(align::none),
+ sign(sign::none),
+ alt(false),
+ localized(false) {}
+};
+
+using format_specs = basic_format_specs<char>;
+
+FMT_BEGIN_DETAIL_NAMESPACE
+
+enum class arg_id_kind { none, index, name };
+
+// An argument reference.
+template <typename Char> struct arg_ref {
+ FMT_CONSTEXPR arg_ref() : kind(arg_id_kind::none), val() {}
+
+ FMT_CONSTEXPR explicit arg_ref(int index)
+ : kind(arg_id_kind::index), val(index) {}
+ FMT_CONSTEXPR explicit arg_ref(basic_string_view<Char> name)
+ : kind(arg_id_kind::name), val(name) {}
+
+ FMT_CONSTEXPR auto operator=(int idx) -> arg_ref& {
+ kind = arg_id_kind::index;
+ val.index = idx;
+ return *this;
+ }
+
+ arg_id_kind kind;
+ union value {
+ FMT_CONSTEXPR value(int id = 0) : index{id} {}
+ FMT_CONSTEXPR value(basic_string_view<Char> n) : name(n) {}
+
+ int index;
+ basic_string_view<Char> name;
+ } val;
+};
+
+// Format specifiers with width and precision resolved at formatting rather
+// than parsing time to allow re-using the same parsed specifiers with
+// different sets of arguments (precompilation of format strings).
+template <typename Char>
+struct dynamic_format_specs : basic_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(Char type) {
+ specs_.type = static_cast<char>(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);
+ return arg_ref_type(arg_id);
+ }
+
+ FMT_CONSTEXPR auto make_arg_ref(auto_id) -> arg_ref_type {
+ return arg_ref_type(context_.next_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.
+template <typename Char, FMT_ENABLE_IF(std::is_integral<Char>::value)>
+constexpr auto to_ascii(Char value) -> Char {
+ return value;
+}
+template <typename Char, FMT_ENABLE_IF(std::is_enum<Char>::value)>
+constexpr auto to_ascii(Char value) ->
+ typename std::underlying_type<Char>::type {
+ return value;
+}
+
+template <typename Char>
+FMT_CONSTEXPR auto code_point_length(const Char* begin) -> int {
+ if (const_check(sizeof(Char) != 1)) return 1;
+ constexpr char lengths[] = {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, 0};
+ int len = lengths[static_cast<unsigned char>(*begin) >> 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.
+ return len + !len;
+}
+
+// Return the result via the out param to workaround gcc bug 77539.
+template <bool IS_CONSTEXPR, typename T, typename Ptr = const T*>
+FMT_CONSTEXPR auto find(Ptr first, Ptr last, T value, Ptr& out) -> bool {
+ for (out = first; out != last; ++out) {
+ if (*out == value) return true;
+ }
+ return false;
+}
+
+template <>
+inline auto find<false, char>(const char* first, const char* last, char value,
+ const char*& out) -> bool {
+ out = static_cast<const char*>(
+ std::memchr(first, value, to_unsigned(last - first)));
+ return out != nullptr;
+}
+
+// Parses the range [begin, end) as an unsigned integer. This function assumes
+// that the range is non-empty and the first character is a digit.
+template <typename Char>
+FMT_CONSTEXPR auto parse_nonnegative_int(const Char*& begin, const Char* end,
+ int error_value) noexcept -> int {
+ FMT_ASSERT(begin != end && '0' <= *begin && *begin <= '9', "");
+ unsigned value = 0, prev = 0;
+ auto p = begin;
+ do {
+ prev = value;
+ value = value * 10 + unsigned(*p - '0');
+ ++p;
+ } while (p != end && '0' <= *p && *p <= '9');
+ auto num_digits = p - begin;
+ begin = p;
+ if (num_digits <= std::numeric_limits<int>::digits10)
+ return static_cast<int>(value);
+ // Check for overflow.
+ const unsigned max = to_unsigned((std::numeric_limits<int>::max)());
+ return num_digits == std::numeric_limits<int>::digits10 + 1 &&
+ prev * 10ull + unsigned(p[-1] - '0') <= max
+ ? static_cast<int>(value)
+ : 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 (p >= end) 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;
+ }
+ return begin;
+}
+
+template <typename Char> FMT_CONSTEXPR bool is_name_start(Char c) {
+ return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c;
+}
+
+template <typename Char, typename IDHandler>
+FMT_CONSTEXPR auto do_parse_arg_id(const Char* begin, const Char* end,
+ IDHandler&& handler) -> const Char* {
+ FMT_ASSERT(begin != end, "");
+ Char c = *begin;
+ if (c >= '0' && c <= '9') {
+ int index = 0;
+ if (c != '0')
+ index =
+ parse_nonnegative_int(begin, end, (std::numeric_limits<int>::max)());
+ else
+ ++begin;
+ if (begin == end || (*begin != '}' && *begin != ':'))
+ handler.on_error("invalid format string");
+ else
+ handler(index);
+ return begin;
+ }
+ if (!is_name_start(c)) {
+ handler.on_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)));
+ return it;
+}
+
+template <typename Char, typename IDHandler>
+FMT_CONSTEXPR FMT_INLINE auto parse_arg_id(const Char* begin, const Char* end,
+ IDHandler&& handler) -> const Char* {
+ Char c = *begin;
+ if (c != '}' && c != ':') return do_parse_arg_id(begin, end, handler);
+ handler();
+ 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;
+
+ 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_ASSERT(begin != end, "");
+ if ('0' <= *begin && *begin <= '9') {
+ int width = parse_nonnegative_int(begin, end, -1);
+ if (width != -1)
+ handler.on_width(width);
+ else
+ handler.on_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;
+ }
+ 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;
+}
+
+// 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)
+ -> const Char* {
+ if (begin + 1 < end && begin[1] == '}' && is_ascii_letter(*begin) &&
+ *begin != 'L') {
+ handler.on_type(*begin++);
+ return begin;
+ }
+
+ if (begin == end) return begin;
+
+ 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);
+ if (begin == end) return begin;
+ }
+
+ if (*begin == 'L') {
+ handler.on_localized();
+ ++begin;
+ }
+
+ // Parse type.
+ if (begin != end && *begin != '}') handler.on_type(*begin++);
+ return begin;
+}
+
+template <typename Char, typename Handler>
+FMT_CONSTEXPR auto parse_replacement_field(const Char* begin, const Char* end,
+ Handler&& handler) -> const Char* {
+ struct id_adapter {
+ 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) {
+ arg_id = handler.on_arg_id(id);
+ }
+ FMT_CONSTEXPR void on_error(const char* message) {
+ if (message) handler.on_error(message);
+ }
+ };
+
+ ++begin;
+ if (begin == end) return handler.on_error("invalid format string"), end;
+ if (*begin == '}') {
+ handler.on_replacement_field(handler.on_arg_id(), begin);
+ } else if (*begin == '{') {
+ handler.on_text(begin, begin + 1);
+ } else {
+ auto adapter = id_adapter{handler, 0};
+ begin = parse_arg_id(begin, end, adapter);
+ Char c = begin != end ? *begin : Char();
+ if (c == '}') {
+ handler.on_replacement_field(adapter.arg_id, begin);
+ } else if (c == ':') {
+ begin = handler.on_format_specs(adapter.arg_id, begin + 1, end);
+ if (begin == end || *begin != '}')
+ return handler.on_error("unknown format specifier"), end;
+ } else {
+ return handler.on_error("missing '}' in format string"), end;
+ }
+ }
+ return begin + 1;
+}
+
+template <bool IS_CONSTEXPR, typename Char, typename Handler>
+FMT_CONSTEXPR FMT_INLINE void parse_format_string(
+ basic_string_view<Char> format_str, Handler&& handler) {
+ // this is most likely a name-lookup defect in msvc's modules implementation
+ using detail::find;
+
+ auto begin = format_str.data();
+ auto end = begin + format_str.size();
+ if (end - begin < 32) {
+ // Use a simple loop instead of memchr for small strings.
+ const Char* p = begin;
+ while (p != end) {
+ auto c = *p++;
+ if (c == '{') {
+ handler.on_text(begin, p - 1);
+ begin = p = parse_replacement_field(p - 1, end, handler);
+ } else if (c == '}') {
+ if (p == end || *p != '}')
+ return handler.on_error("unmatched '}' in format string");
+ handler.on_text(begin, p);
+ begin = ++p;
+ }
+ }
+ handler.on_text(begin, end);
+ return;
+ }
+ struct writer {
+ FMT_CONSTEXPR void operator()(const Char* pbegin, const Char* pend) {
+ if (pbegin == pend) return;
+ for (;;) {
+ const Char* p = nullptr;
+ if (!find<IS_CONSTEXPR>(pbegin, pend, Char('}'), p))
+ return handler_.on_text(pbegin, pend);
+ ++p;
+ if (p == pend || *p != '}')
+ return handler_.on_error("unmatched '}' in format string");
+ handler_.on_text(pbegin, p);
+ pbegin = p + 1;
+ }
+ }
+ Handler& handler_;
+ } write{handler};
+ while (begin != end) {
+ // Doing two passes with memchr (one for '{' and another for '}') is up to
+ // 2.5x faster than the naive one-pass implementation on big format strings.
+ const Char* p = begin;
+ if (*begin != '{' && !find<IS_CONSTEXPR>(begin + 1, end, Char('{'), p))
+ return write(begin, end);
+ write(begin, p);
+ begin = parse_replacement_field(p, end, handler);
+ }
+}
+
+template <typename T, typename ParseContext>
+FMT_CONSTEXPR auto parse_format_specs(ParseContext& ctx)
+ -> decltype(ctx.begin()) {
+ using char_type = typename ParseContext::char_type;
+ using context = buffer_context<char_type>;
+ using mapped_type = conditional_t<
+ mapped_type_constant<T, context>::value != type::custom_type,
+ decltype(arg_mapper<context>().map(std::declval<const T&>())), T>;
+ auto f = conditional_t<has_formatter<mapped_type, context>::value,
+ formatter<mapped_type, char_type>,
+ fallback_formatter<T, char_type>>();
+ return f.parse(ctx);
+}
+
+// A parse context with extra argument id checks. It is only used at compile
+// time because adding checks at runtime would introduce substantial overhead
+// and would be redundant since argument ids are checked when arguments are
+// retrieved anyway.
+template <typename Char, typename ErrorHandler = error_handler>
+class compile_parse_context
+ : public basic_format_parse_context<Char, ErrorHandler> {
+ private:
+ int num_args_;
+ using base = basic_format_parse_context<Char, ErrorHandler>;
+
+ public:
+ explicit FMT_CONSTEXPR compile_parse_context(
+ basic_string_view<Char> format_str,
+ int num_args = (std::numeric_limits<int>::max)(), ErrorHandler eh = {})
+ : base(format_str, eh), num_args_(num_args) {}
+
+ FMT_CONSTEXPR auto next_arg_id() -> int {
+ int id = base::next_arg_id();
+ if (id >= num_args_) this->on_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");
+ }
+ using base::check_arg_id;
+};
+
+template <typename ErrorHandler>
+FMT_CONSTEXPR void check_int_type_spec(char spec, ErrorHandler&& eh) {
+ switch (spec) {
+ case 0:
+ case 'd':
+ case 'x':
+ case 'X':
+ case 'b':
+ case 'B':
+ case 'o':
+ case 'c':
+ break;
+ default:
+ eh.on_error("invalid type specifier");
+ break;
+ }
+}
+
+// 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 {
+ if (specs.type && specs.type != 'c') {
+ 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");
+ 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 use_grisu : 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 0:
+ result.format = float_format::general;
+ break;
+ case 'G':
+ result.upper = true;
+ FMT_FALLTHROUGH;
+ case 'g':
+ result.format = float_format::general;
+ break;
+ case 'E':
+ result.upper = true;
+ FMT_FALLTHROUGH;
+ case 'e':
+ result.format = float_format::exp;
+ result.showpoint |= specs.precision != 0;
+ break;
+ case 'F':
+ result.upper = true;
+ FMT_FALLTHROUGH;
+ case 'f':
+ result.format = float_format::fixed;
+ result.showpoint |= specs.precision != 0;
+ break;
+ case 'A':
+ result.upper = true;
+ FMT_FALLTHROUGH;
+ case 'a':
+ result.format = float_format::hex;
+ break;
+ default:
+ eh.on_error("invalid type specifier");
+ break;
+ }
+ return result;
+}
+
+template <typename Char, typename ErrorHandler = error_handler>
+FMT_CONSTEXPR auto check_cstring_type_spec(Char spec, ErrorHandler&& eh = {})
+ -> bool {
+ if (spec == 0 || spec == 's') return true;
+ if (spec != 'p') eh.on_error("invalid type specifier");
+ return false;
+}
+
+template <typename Char, typename ErrorHandler = error_handler>
+FMT_CONSTEXPR void check_string_type_spec(Char spec, ErrorHandler&& eh = {}) {
+ if (spec != 0 && spec != 's') eh.on_error("invalid type specifier");
+}
+
+template <typename Char, typename ErrorHandler>
+FMT_CONSTEXPR void check_pointer_type_spec(Char spec, ErrorHandler&& eh) {
+ if (spec != 0 && spec != 'p') 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::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_PARAMETERS
+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 (name == T::name) return N;
+ }
+ if constexpr (sizeof...(Args) > 0) {
+ return get_arg_index_by_name<N + 1, Args...>(name);
+ } else {
+ (void)name; // Workaround an MSVC bug about "unused" parameter.
+ return invalid_arg_index;
+ }
+}
+#endif
+
+template <typename... Args, typename Char>
+FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view<Char> name) -> int {
+#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
+ if constexpr (sizeof...(Args) > 0) {
+ return get_arg_index_by_name<0, Args...>(name);
+ } else {
+ (void)name;
+ return invalid_arg_index;
+ }
+#else
+ (void)name;
+ return invalid_arg_index;
+#endif
+}
+
+template <typename Char, typename ErrorHandler, typename... Args>
+class format_string_checker {
+ private:
+ using parse_context_type = compile_parse_context<Char, ErrorHandler>;
+ enum { num_args = sizeof...(Args) };
+
+ // Format specifier parsing function.
+ using parse_func = const Char* (*)(parse_context_type&);
+
+ parse_context_type context_;
+ parse_func parse_funcs_[num_args > 0 ? num_args : 1];
+
+ public:
+ explicit FMT_CONSTEXPR format_string_checker(
+ basic_string_view<Char> format_str, ErrorHandler eh)
+ : context_(format_str, num_args, eh),
+ parse_funcs_{&parse_format_specs<Args, parse_context_type>...} {}
+
+ FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
+
+ FMT_CONSTEXPR auto on_arg_id() -> int { return context_.next_arg_id(); }
+ FMT_CONSTEXPR auto on_arg_id(int id) -> int {
+ return context_.check_arg_id(id), id;
+ }
+ FMT_CONSTEXPR auto on_arg_id(basic_string_view<Char> id) -> int {
+#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
+ 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;
+#else
+ (void)id;
+ on_error("compile-time checks for named arguments require C++20 support");
+ return 0;
+#endif
+ }
+
+ FMT_CONSTEXPR void on_replacement_field(int, const Char*) {}
+
+ FMT_CONSTEXPR auto on_format_specs(int id, const Char* begin, const Char*)
+ -> const Char* {
+ context_.advance_to(context_.begin() + (begin - &*context_.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);
+ }
+};
+
+template <typename... Args, typename S,
+ enable_if_t<(is_compile_string<S>::value), int>>
+void check_format_string(S format_str) {
+ FMT_CONSTEXPR auto s = to_string_view(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);
+}
+
+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 = {});
+
+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
+
+// A formatter specialization for the core types corresponding to detail::type
+// constants.
+template <typename T, typename Char>
+struct formatter<T, Char,
+ enable_if_t<detail::type_constant<T, Char>::value !=
+ detail::type::custom_type>> {
+ private:
+ 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>;
+ 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 || specs_.type == 's') 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;
+ }
+
+ template <typename FormatContext>
+ FMT_CONSTEXPR auto format(const T& val, FormatContext& ctx) const
+ -> decltype(ctx.out());
+};
+
+template <typename Char> struct basic_runtime { basic_string_view<Char> str; };
+
+template <typename Char, typename... Args> class basic_format_string {
+ private:
+ basic_string_view<Char> str_;
+
+ public:
+ template <typename S,
+ FMT_ENABLE_IF(
+ std::is_convertible<const S&, basic_string_view<Char>>::value)>
+ FMT_CONSTEVAL basic_format_string(const S& s) : str_(s) {
+ static_assert(
+ detail::count<
+ (std::is_base_of<detail::view, remove_reference_t<Args>>::value &&
+ std::is_reference<Args>::value)...>() == 0,
+ "passing views as lvalues is disallowed");
+#ifdef FMT_HAS_CONSTEVAL
+ if constexpr (detail::count_named_args<Args...>() == 0) {
+ using checker = detail::format_string_checker<Char, detail::error_handler,
+ 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) {}
+
+ FMT_INLINE operator basic_string_view<Char>() const { return str_; }
+};
+
+#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
+// Workaround broken conversion on older gcc.
+template <typename... Args> using format_string = string_view;
+template <typename S> auto runtime(const S& s) -> basic_string_view<char_t<S>> {
+ return s;
+}
+#else
+template <typename... Args>
+using format_string = basic_format_string<char, type_identity_t<Args>...>;
+// Creates a runtime format string.
+template <typename S> auto runtime(const S& s) -> basic_runtime<char_t<S>> {
+ return {{s}};
+}
+#endif
+
+FMT_API auto vformat(string_view fmt, format_args args) -> std::string;
+
+/**
+ \rst
+ Formats ``args`` according to specifications in ``fmt`` and returns the result
+ as a string.
+
+ **Example**::
+
+ #include <fmt/core.h>
+ std::string message = fmt::format("The answer is {}", 42);
+ \endrst
+*/
+template <typename... T>
+FMT_INLINE auto format(format_string<T...> fmt, T&&... args) -> std::string {
+ return vformat(fmt, fmt::make_format_args(args...));
+}
+
+/** Formats a string and writes the output to ``out``. */
+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);
+ detail::vformat_to(buf, string_view(fmt), args, {});
+ return detail::get_iterator(buf);
+}
+
+/**
+ \rst
+ Formats ``args`` according to specifications in ``fmt``, writes the result to
+ the output iterator ``out`` and returns the iterator past the end of the output
+ range.
+
+ **Example**::
+
+ auto out = std::vector<char>();
+ fmt::format_to(std::back_inserter(out), "{}", 42);
+ \endrst
+ */
+template <typename OutputIt, typename... T,
+ FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
+FMT_INLINE auto format_to(OutputIt out, format_string<T...> fmt, T&&... args)
+ -> OutputIt {
+ return vformat_to(out, fmt, fmt::make_format_args(args...));
+}
+
+template <typename OutputIt> struct format_to_n_result {
+ /** Iterator past the end of the output range. */
+ OutputIt out;
+ /** Total (not truncated) output size. */
+ size_t size;
+};
+
+template <typename OutputIt, typename... T,
+ FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
+auto vformat_to_n(OutputIt out, size_t n, string_view fmt, format_args args)
+ -> format_to_n_result<OutputIt> {
+ using buffer =
+ detail::iterator_buffer<OutputIt, char, detail::fixed_buffer_traits>;
+ auto buf = buffer(out, n);
+ detail::vformat_to(buf, fmt, args, {});
+ return {buf.out(), buf.count()};
+}
+
+/**
+ \rst
+ Formats ``args`` according to specifications in ``fmt``, writes up to ``n``
+ characters of the result to the output iterator ``out`` and returns the total
+ (not truncated) output size and the iterator past the end of the output range.
+ \endrst
+ */
+template <typename OutputIt, typename... T,
+ FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
+FMT_INLINE auto format_to_n(OutputIt out, size_t n, format_string<T...> fmt,
+ const T&... args) -> format_to_n_result<OutputIt> {
+ return vformat_to_n(out, n, fmt, fmt::make_format_args(args...));
+}
+
+/** Returns the number of chars in the output of ``format(fmt, args...)``. */
+template <typename... T>
+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...), {});
+ return buf.count();
+}
+
+FMT_API void vprint(string_view fmt, format_args args);
+FMT_API void vprint(std::FILE* f, string_view fmt, format_args args);
+
+/**
+ \rst
+ Formats ``args`` according to specifications in ``fmt`` and writes the output
+ to ``stdout``.
+
+ **Example**::
+
+ fmt::print("Elapsed time: {0:.2f} seconds", 1.23);
+ \endrst
+ */
+template <typename... T>
+FMT_INLINE void print(format_string<T...> fmt, T&&... args) {
+ const auto& vargs = fmt::make_format_args(args...);
+ return detail::is_utf8() ? vprint(fmt, vargs)
+ : detail::vprint_mojibake(stdout, fmt, vargs);
+}
+
+/**
+ \rst
+ Formats ``args`` according to specifications in ``fmt`` and writes the
+ output to the file ``f``.
+
+ **Example**::
+
+ fmt::print(stderr, "Don't {}!", "panic");
+ \endrst
+ */
+template <typename... T>
+FMT_INLINE void print(std::FILE* f, format_string<T...> fmt, T&&... args) {
+ const auto& vargs = fmt::make_format_args(args...);
+ return detail::is_utf8() ? vprint(f, fmt, vargs)
+ : detail::vprint_mojibake(f, fmt, vargs);
+}
+
+FMT_MODULE_EXPORT_END
+FMT_GCC_PRAGMA("GCC pop_options")
+FMT_END_NAMESPACE
+
+#ifdef FMT_HEADER_ONLY
+# include "format.h"
+#endif
+#endif // FMT_CORE_H_
diff --git a/wpiutil/src/main/native/fmtlib/include/fmt/format-inl.h b/wpiutil/src/main/native/fmtlib/include/fmt/format-inl.h
new file mode 100644
index 0000000..94a36d1
--- /dev/null
+++ b/wpiutil/src/main/native/fmtlib/include/fmt/format-inl.h
@@ -0,0 +1,2620 @@
+// Formatting library for C++ - implementation
+//
+// Copyright (c) 2012 - 2016, Victor Zverovich
+// All rights reserved.
+//
+// For the license information refer to format.h.
+
+#ifndef FMT_FORMAT_INL_H_
+#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
+# include <locale>
+#endif
+
+#ifdef _WIN32
+# include <io.h> // _isatty
+#endif
+
+#include "format.h"
+
+FMT_BEGIN_NAMESPACE
+namespace detail {
+
+FMT_FUNC void assert_fail(const char* file, int line, const char* message) {
+ // Use unchecked std::fprintf to avoid triggering another assertion when
+ // writing to stderr fails
+ std::fprintf(stderr, "%s:%d: assertion failed: %s", file, line, message);
+ // Chosen instead of std::abort to satisfy Clang in CUDA mode during device
+ // code pass.
+ std::terminate();
+}
+
+#ifndef _MSC_VER
+# define FMT_SNPRINTF snprintf
+#else // _MSC_VER
+inline int fmt_snprintf(char* buffer, size_t size, const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+ int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args);
+ va_end(args);
+ return result;
+}
+# define FMT_SNPRINTF fmt_snprintf
+#endif // _MSC_VER
+
+FMT_FUNC void format_error_code(detail::buffer<char>& out, int error_code,
+ string_view message) FMT_NOEXCEPT {
+ // Report error code making sure that the output fits into
+ // inline_buffer_size to avoid dynamic memory allocation and potential
+ // bad_alloc.
+ out.try_resize(0);
+ static const char SEP[] = ": ";
+ static const char ERROR_STR[] = "error ";
+ // Subtract 2 to account for terminating null characters in SEP and ERROR_STR.
+ size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2;
+ auto abs_value = static_cast<uint32_or_64_or_128_t<int>>(error_code);
+ if (detail::is_negative(error_code)) {
+ abs_value = 0 - abs_value;
+ ++error_code_size;
+ }
+ error_code_size += detail::to_unsigned(detail::count_digits(abs_value));
+ auto it = buffer_appender<char>(out);
+ if (message.size() <= inline_buffer_size - error_code_size)
+ format_to(it, FMT_STRING("{}{}"), message, SEP);
+ format_to(it, FMT_STRING("{}{}"), ERROR_STR, error_code);
+ FMT_ASSERT(out.size() <= inline_buffer_size, "");
+}
+
+FMT_FUNC void report_error(format_func func, int error_code,
+ const char* message) FMT_NOEXCEPT {
+ memory_buffer full_message;
+ func(full_message, error_code, message);
+ // Don't use fwrite_fully because the latter may throw.
+ if (std::fwrite(full_message.data(), full_message.size(), 1, stderr) > 0)
+ std::fputc('\n', stderr);
+}
+
+// A wrapper around fwrite that throws on error.
+inline void fwrite_fully(const void* ptr, size_t size, size_t count,
+ FILE* stream) {
+ size_t written = std::fwrite(ptr, size, count, stream);
+ if (written < count) FMT_THROW(system_error(errno, "cannot write to file"));
+}
+
+#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
+template <typename Locale>
+locale_ref::locale_ref(const Locale& loc) : locale_(&loc) {
+ static_assert(std::is_same<Locale, std::locale>::value, "");
+}
+
+template <typename Locale> Locale locale_ref::get() const {
+ static_assert(std::is_same<Locale, std::locale>::value, "");
+ return locale_ ? *static_cast<const std::locale*>(locale_) : std::locale();
+}
+
+template <typename Char>
+FMT_FUNC auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result<Char> {
+ auto& facet = std::use_facet<std::numpunct<Char>>(loc.get<std::locale>());
+ auto grouping = facet.grouping();
+ auto thousands_sep = grouping.empty() ? Char() : facet.thousands_sep();
+ return {std::move(grouping), thousands_sep};
+}
+template <typename Char> FMT_FUNC Char decimal_point_impl(locale_ref loc) {
+ return std::use_facet<std::numpunct<Char>>(loc.get<std::locale>())
+ .decimal_point();
+}
+#else
+template <typename Char>
+FMT_FUNC auto thousands_sep_impl(locale_ref) -> thousands_sep_result<Char> {
+ return {"\03", FMT_STATIC_THOUSANDS_SEPARATOR};
+}
+template <typename Char> FMT_FUNC Char decimal_point_impl(locale_ref) {
+ return '.';
+}
+#endif
+} // namespace detail
+
+#if !FMT_MSC_VER
+FMT_API FMT_FUNC format_error::~format_error() FMT_NOEXCEPT = default;
+#endif
+
+FMT_FUNC std::system_error vsystem_error(int error_code, string_view format_str,
+ format_args args) {
+ auto ec = std::error_code(error_code, std::generic_category());
+ return std::system_error(ec, vformat(format_str, args));
+}
+
+namespace detail {
+
+template <> FMT_FUNC int count_digits<4>(detail::fallback_uintptr n) {
+ // fallback_uintptr is always stored in little endian.
+ int i = static_cast<int>(sizeof(void*)) - 1;
+ while (i > 0 && n.value[i] == 0) --i;
+ auto char_digits = std::numeric_limits<unsigned char>::digits / 4;
+ return i >= 0 ? i * char_digits + count_digits<4, unsigned>(n.value[i]) : 1;
+}
+
+#if __cplusplus < 201703L
+template <typename T> constexpr const char basic_data<T>::digits[][2];
+template <typename T> constexpr const char basic_data<T>::hex_digits[];
+template <typename T> constexpr const char basic_data<T>::signs[];
+template <typename T> constexpr const unsigned basic_data<T>::prefixes[];
+template <typename T> constexpr const char basic_data<T>::left_padding_shifts[];
+template <typename T>
+constexpr const char basic_data<T>::right_padding_shifts[];
+#endif
+
+template <typename T> struct bits {
+ static FMT_CONSTEXPR_DECL const int value =
+ static_cast<int>(sizeof(T) * std::numeric_limits<unsigned char>::digits);
+};
+
+class fp;
+template <int SHIFT = 0> fp normalize(fp value);
+
+// Lower (upper) boundary is a value half way between a floating-point value
+// and its predecessor (successor). Boundaries have the same exponent as the
+// value so only significands are stored.
+struct boundaries {
+ uint64_t lower;
+ uint64_t upper;
+};
+
+// A handmade floating-point number f * pow(2, e).
+class fp {
+ private:
+ using significand_type = uint64_t;
+
+ template <typename Float>
+ using is_supported_float = bool_constant<sizeof(Float) == sizeof(uint64_t) ||
+ sizeof(Float) == sizeof(uint32_t)>;
+
+ public:
+ significand_type f;
+ int e;
+
+ // All sizes are in bits.
+ // Subtract 1 to account for an implicit most significant bit in the
+ // normalized form.
+ static FMT_CONSTEXPR_DECL const int double_significand_size =
+ std::numeric_limits<double>::digits - 1;
+ static FMT_CONSTEXPR_DECL const uint64_t implicit_bit =
+ 1ULL << double_significand_size;
+ static FMT_CONSTEXPR_DECL const int significand_size =
+ bits<significand_type>::value;
+
+ fp() : f(0), e(0) {}
+ fp(uint64_t f_val, int e_val) : f(f_val), e(e_val) {}
+
+ // Constructs fp from an IEEE754 double. It is a template to prevent compile
+ // errors on platforms where double is not IEEE754.
+ template <typename Double> explicit fp(Double d) { assign(d); }
+
+ // Assigns d to this and return true iff predecessor is closer than successor.
+ template <typename Float, FMT_ENABLE_IF(is_supported_float<Float>::value)>
+ bool assign(Float d) {
+ // Assume float is in the format [sign][exponent][significand].
+ using limits = std::numeric_limits<Float>;
+ const int float_significand_size = limits::digits - 1;
+ const int exponent_size =
+ bits<Float>::value - float_significand_size - 1; // -1 for sign
+ const uint64_t float_implicit_bit = 1ULL << float_significand_size;
+ const uint64_t significand_mask = float_implicit_bit - 1;
+ const uint64_t exponent_mask = (~0ULL >> 1) & ~significand_mask;
+ const int exponent_bias = (1 << exponent_size) - limits::max_exponent - 1;
+ constexpr bool is_double = sizeof(Float) == sizeof(uint64_t);
+ auto u = bit_cast<conditional_t<is_double, uint64_t, uint32_t>>(d);
+ f = u & significand_mask;
+ int biased_e =
+ static_cast<int>((u & exponent_mask) >> float_significand_size);
+ // Predecessor is closer if d is a normalized power of 2 (f == 0) other than
+ // the smallest normalized number (biased_e > 1).
+ bool is_predecessor_closer = f == 0 && biased_e > 1;
+ if (biased_e != 0)
+ f += float_implicit_bit;
+ else
+ biased_e = 1; // Subnormals use biased exponent 1 (min exponent).
+ e = biased_e - exponent_bias - float_significand_size;
+ return is_predecessor_closer;
+ }
+
+ template <typename Float, FMT_ENABLE_IF(!is_supported_float<Float>::value)>
+ bool assign(Float) {
+ *this = fp();
+ return false;
+ }
+};
+
+// Normalizes the value converted from double and multiplied by (1 << SHIFT).
+template <int SHIFT> fp normalize(fp value) {
+ // Handle subnormals.
+ const auto shifted_implicit_bit = fp::implicit_bit << SHIFT;
+ while ((value.f & shifted_implicit_bit) == 0) {
+ value.f <<= 1;
+ --value.e;
+ }
+ // Subtract 1 to account for hidden bit.
+ const auto offset =
+ fp::significand_size - fp::double_significand_size - SHIFT - 1;
+ value.f <<= offset;
+ value.e -= offset;
+ return value;
+}
+
+inline bool operator==(fp x, fp y) { return x.f == y.f && x.e == y.e; }
+
+// Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking.
+inline uint64_t multiply(uint64_t lhs, uint64_t rhs) {
+#if FMT_USE_INT128
+ auto product = static_cast<__uint128_t>(lhs) * rhs;
+ auto f = static_cast<uint64_t>(product >> 64);
+ return (static_cast<uint64_t>(product) & (1ULL << 63)) != 0 ? f + 1 : f;
+#else
+ // Multiply 32-bit parts of significands.
+ uint64_t mask = (1ULL << 32) - 1;
+ uint64_t a = lhs >> 32, b = lhs & mask;
+ uint64_t c = rhs >> 32, d = rhs & mask;
+ uint64_t ac = a * c, bc = b * c, ad = a * d, bd = b * d;
+ // Compute mid 64-bit of result and round.
+ uint64_t mid = (bd >> 32) + (ad & mask) + (bc & mask) + (1U << 31);
+ return ac + (ad >> 32) + (bc >> 32) + (mid >> 32);
+#endif
+}
+
+inline fp operator*(fp x, fp y) { return {multiply(x.f, y.f), x.e + y.e + 64}; }
+
+// 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`.
+inline fp get_cached_power(int min_exponent, int& pow10_exponent) {
+ // Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340.
+ // These are generated by support/compute-powers.py.
+ static constexpr const uint64_t pow10_significands[] = {
+ 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,
+ };
+
+ // Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding
+ // to significands above.
+ static constexpr const int16_t pow10_exponents[] = {
+ -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};
+
+ const int shift = 32;
+ const auto significand = static_cast<int64_t>(data::log10_2_significand);
+ int index = static_cast<int>(
+ ((min_exponent + fp::significand_size - 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;
+ return {pow10_significands[index], pow10_exponents[index]};
+}
+
+// A simple accumulator to hold the sums of terms in bigint::square if uint128_t
+// is not available.
+struct accumulator {
+ uint64_t lower;
+ uint64_t upper;
+
+ accumulator() : lower(0), upper(0) {}
+ explicit operator uint32_t() const { return static_cast<uint32_t>(lower); }
+
+ void operator+=(uint64_t n) {
+ lower += n;
+ if (lower < n) ++upper;
+ }
+ void operator>>=(int shift) {
+ FMT_ASSERT(shift == 32, "");
+ (void)shift;
+ lower = (upper << 32) | (lower >> 32);
+ upper >>= 32;
+ }
+};
+
+class bigint {
+ private:
+ // A bigint is stored as an array of bigits (big digits), with bigit at index
+ // 0 being the least significant one.
+ using bigit = uint32_t;
+ using double_bigit = uint64_t;
+ enum { bigits_capacity = 32 };
+ basic_memory_buffer<bigit, bigits_capacity> bigits_;
+ int exp_;
+
+ bigit operator[](int index) const { return bigits_[to_unsigned(index)]; }
+ bigit& operator[](int index) { return bigits_[to_unsigned(index)]; }
+
+ static FMT_CONSTEXPR_DECL const int bigit_bits = bits<bigit>::value;
+
+ friend struct formatter<bigint>;
+
+ void subtract_bigits(int index, bigit other, bigit& borrow) {
+ auto result = static_cast<double_bigit>((*this)[index]) - other - borrow;
+ (*this)[index] = static_cast<bigit>(result);
+ borrow = static_cast<bigit>(result >> (bigit_bits * 2 - 1));
+ }
+
+ void remove_leading_zeros() {
+ int num_bigits = static_cast<int>(bigits_.size()) - 1;
+ while (num_bigits > 0 && (*this)[num_bigits] == 0) --num_bigits;
+ bigits_.resize(to_unsigned(num_bigits + 1));
+ }
+
+ // Computes *this -= other assuming aligned bigints and *this >= other.
+ void subtract_aligned(const bigint& other) {
+ FMT_ASSERT(other.exp_ >= exp_, "unaligned bigints");
+ FMT_ASSERT(compare(*this, other) >= 0, "");
+ bigit borrow = 0;
+ int i = other.exp_ - exp_;
+ for (size_t j = 0, n = other.bigits_.size(); j != n; ++i, ++j)
+ subtract_bigits(i, other.bigits_[j], borrow);
+ while (borrow > 0) subtract_bigits(i, 0, borrow);
+ remove_leading_zeros();
+ }
+
+ void multiply(uint32_t value) {
+ const double_bigit wide_value = value;
+ bigit carry = 0;
+ for (size_t i = 0, n = bigits_.size(); i < n; ++i) {
+ double_bigit result = bigits_[i] * wide_value + carry;
+ bigits_[i] = static_cast<bigit>(result);
+ carry = static_cast<bigit>(result >> bigit_bits);
+ }
+ if (carry != 0) bigits_.push_back(carry);
+ }
+
+ void multiply(uint64_t value) {
+ const bigit mask = ~bigit(0);
+ const double_bigit lower = value & mask;
+ const double_bigit upper = value >> bigit_bits;
+ double_bigit carry = 0;
+ for (size_t i = 0, n = bigits_.size(); i < n; ++i) {
+ double_bigit result = bigits_[i] * lower + (carry & mask);
+ carry =
+ bigits_[i] * upper + (result >> bigit_bits) + (carry >> bigit_bits);
+ bigits_[i] = static_cast<bigit>(result);
+ }
+ while (carry != 0) {
+ bigits_.push_back(carry & mask);
+ carry >>= bigit_bits;
+ }
+ }
+
+ public:
+ bigint() : exp_(0) {}
+ explicit bigint(uint64_t n) { assign(n); }
+ ~bigint() { FMT_ASSERT(bigits_.capacity() <= bigits_capacity, ""); }
+
+ bigint(const bigint&) = delete;
+ void operator=(const bigint&) = delete;
+
+ void assign(const bigint& other) {
+ auto size = other.bigits_.size();
+ bigits_.resize(size);
+ auto data = other.bigits_.data();
+ std::copy(data, data + size, make_checked(bigits_.data(), size));
+ exp_ = other.exp_;
+ }
+
+ void assign(uint64_t n) {
+ size_t num_bigits = 0;
+ do {
+ bigits_[num_bigits++] = n & ~bigit(0);
+ n >>= bigit_bits;
+ } while (n != 0);
+ bigits_.resize(num_bigits);
+ exp_ = 0;
+ }
+
+ int num_bigits() const { return static_cast<int>(bigits_.size()) + exp_; }
+
+ FMT_NOINLINE bigint& operator<<=(int shift) {
+ FMT_ASSERT(shift >= 0, "");
+ exp_ += shift / bigit_bits;
+ shift %= bigit_bits;
+ if (shift == 0) return *this;
+ bigit carry = 0;
+ for (size_t i = 0, n = bigits_.size(); i < n; ++i) {
+ bigit c = bigits_[i] >> (bigit_bits - shift);
+ bigits_[i] = (bigits_[i] << shift) + carry;
+ carry = c;
+ }
+ if (carry != 0) bigits_.push_back(carry);
+ return *this;
+ }
+
+ template <typename Int> bigint& operator*=(Int value) {
+ FMT_ASSERT(value > 0, "");
+ multiply(uint32_or_64_or_128_t<Int>(value));
+ return *this;
+ }
+
+ friend int compare(const bigint& lhs, const bigint& rhs) {
+ int num_lhs_bigits = lhs.num_bigits(), num_rhs_bigits = rhs.num_bigits();
+ if (num_lhs_bigits != num_rhs_bigits)
+ return num_lhs_bigits > num_rhs_bigits ? 1 : -1;
+ int i = static_cast<int>(lhs.bigits_.size()) - 1;
+ int j = static_cast<int>(rhs.bigits_.size()) - 1;
+ int end = i - j;
+ if (end < 0) end = 0;
+ for (; i >= end; --i, --j) {
+ bigit lhs_bigit = lhs[i], rhs_bigit = rhs[j];
+ if (lhs_bigit != rhs_bigit) return lhs_bigit > rhs_bigit ? 1 : -1;
+ }
+ if (i != j) return i > j ? 1 : -1;
+ return 0;
+ }
+
+ // Returns compare(lhs1 + lhs2, rhs).
+ friend int add_compare(const bigint& lhs1, const bigint& lhs2,
+ const bigint& rhs) {
+ int max_lhs_bigits = (std::max)(lhs1.num_bigits(), lhs2.num_bigits());
+ int num_rhs_bigits = rhs.num_bigits();
+ if (max_lhs_bigits + 1 < num_rhs_bigits) return -1;
+ if (max_lhs_bigits > num_rhs_bigits) return 1;
+ auto get_bigit = [](const bigint& n, int i) -> bigit {
+ return i >= n.exp_ && i < n.num_bigits() ? n[i - n.exp_] : 0;
+ };
+ double_bigit borrow = 0;
+ int min_exp = (std::min)((std::min)(lhs1.exp_, lhs2.exp_), rhs.exp_);
+ for (int i = num_rhs_bigits - 1; i >= min_exp; --i) {
+ double_bigit sum =
+ static_cast<double_bigit>(get_bigit(lhs1, i)) + get_bigit(lhs2, i);
+ bigit rhs_bigit = get_bigit(rhs, i);
+ if (sum > rhs_bigit + borrow) return 1;
+ borrow = rhs_bigit + borrow - sum;
+ if (borrow > 1) return -1;
+ borrow <<= bigit_bits;
+ }
+ return borrow != 0 ? -1 : 0;
+ }
+
+ // Assigns pow(10, exp) to this bigint.
+ void assign_pow10(int exp) {
+ FMT_ASSERT(exp >= 0, "");
+ if (exp == 0) return assign(1);
+ // Find the top bit.
+ int bitmask = 1;
+ while (exp >= bitmask) bitmask <<= 1;
+ bitmask >>= 1;
+ // pow(10, exp) = pow(5, exp) * pow(2, exp). First compute pow(5, exp) by
+ // repeated squaring and multiplication.
+ assign(5);
+ bitmask >>= 1;
+ while (bitmask != 0) {
+ square();
+ if ((exp & bitmask) != 0) *this *= 5;
+ bitmask >>= 1;
+ }
+ *this <<= exp; // Multiply by pow(2, exp) by shifting.
+ }
+
+ void square() {
+ int num_bigits = static_cast<int>(bigits_.size());
+ int num_result_bigits = 2 * num_bigits;
+ basic_memory_buffer<bigit, bigits_capacity> n(std::move(bigits_));
+ bigits_.resize(to_unsigned(num_result_bigits));
+ using accumulator_t = conditional_t<FMT_USE_INT128, uint128_t, accumulator>;
+ auto sum = accumulator_t();
+ for (int bigit_index = 0; bigit_index < num_bigits; ++bigit_index) {
+ // Compute bigit at position bigit_index of the result by adding
+ // cross-product terms n[i] * n[j] such that i + j == bigit_index.
+ for (int i = 0, j = bigit_index; j >= 0; ++i, --j) {
+ // Most terms are multiplied twice which can be optimized in the future.
+ sum += static_cast<double_bigit>(n[i]) * n[j];
+ }
+ (*this)[bigit_index] = static_cast<bigit>(sum);
+ sum >>= bits<bigit>::value; // Compute the carry.
+ }
+ // Do the same for the top half.
+ for (int bigit_index = num_bigits; bigit_index < num_result_bigits;
+ ++bigit_index) {
+ for (int j = num_bigits - 1, i = bigit_index - j; i < num_bigits;)
+ sum += static_cast<double_bigit>(n[i++]) * n[j--];
+ (*this)[bigit_index] = static_cast<bigit>(sum);
+ sum >>= bits<bigit>::value;
+ }
+ remove_leading_zeros();
+ exp_ *= 2;
+ }
+
+ // If this bigint has a bigger exponent than other, adds trailing zero to make
+ // exponents equal. This simplifies some operations such as subtraction.
+ void align(const bigint& other) {
+ int exp_difference = exp_ - other.exp_;
+ if (exp_difference <= 0) return;
+ int num_bigits = static_cast<int>(bigits_.size());
+ bigits_.resize(to_unsigned(num_bigits + exp_difference));
+ for (int i = num_bigits - 1, j = i + exp_difference; i >= 0; --i, --j)
+ bigits_[j] = bigits_[i];
+ std::uninitialized_fill_n(bigits_.data(), exp_difference, 0);
+ exp_ -= exp_difference;
+ }
+
+ // Divides this bignum by divisor, assigning the remainder to this and
+ // returning the quotient.
+ int divmod_assign(const bigint& divisor) {
+ FMT_ASSERT(this != &divisor, "");
+ if (compare(*this, divisor) < 0) return 0;
+ FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1u] != 0, "");
+ align(divisor);
+ int quotient = 0;
+ do {
+ subtract_aligned(divisor);
+ ++quotient;
+ } while (compare(*this, divisor) >= 0);
+ return quotient;
+ }
+};
+
+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.
+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.
+};
+}
+
+inline uint64_t power_of_10_64(int exp) {
+ static constexpr const uint64_t data[] = {1, FMT_POWERS_OF_10(1),
+ FMT_POWERS_OF_10(1000000000ULL),
+ 10000000000000000000ULL};
+ return data[exp];
+}
+
+// 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).
+template <typename Handler>
+FMT_INLINE digits::result grisu_gen_digits(fp value, uint64_t error, int& exp,
+ Handler& handler) {
+ 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.
+ // Divide by 10 to prevent overflow.
+ auto result = handler.on_start(power_of_10_64(exp - 1) << -one.e,
+ value.f / 10, error * 10, exp);
+ if (result != digits::more) return result;
+ // 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;
+ result = handler.on_digit(static_cast<char>('0' + digit),
+ power_of_10_64(exp) << -one.e, remainder, error,
+ exp, 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;
+ result = handler.on_digit(digit, one.f, fractional, error, exp, false);
+ if (result != digits::more) return result;
+ }
+}
+
+// The fixed precision digit handler.
+struct fixed_handler {
+ char* buf;
+ int size;
+ int precision;
+ int exp10;
+ bool fixed;
+
+ digits::result on_start(uint64_t divisor, uint64_t remainder, uint64_t error,
+ int& exp) {
+ // Non-fixed formats require at least one digit and no precision adjustment.
+ if (!fixed) return digits::more;
+ // Adjust fixed precision by exponent because it is relative to decimal
+ // point.
+ precision += exp + exp10;
+ // Check if precision is satisfied just by leading zeros, e.g.
+ // format("{:.2f}", 0.001) gives "0.00" without generating any digits.
+ if (precision > 0) return digits::more;
+ if (precision < 0) return digits::done;
+ auto dir = get_round_direction(divisor, remainder, error);
+ if (dir == round_direction::unknown) return digits::error;
+ buf[size++] = dir == round_direction::up ? '1' : '0';
+ return digits::done;
+ }
+
+ digits::result on_digit(char digit, uint64_t divisor, uint64_t remainder,
+ uint64_t error, int, 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;
+ }
+};
+
+// A 128-bit integer type used internally,
+struct uint128_wrapper {
+ uint128_wrapper() = default;
+
+#if FMT_USE_INT128
+ uint128_t internal_;
+
+ constexpr uint128_wrapper(uint64_t high, uint64_t low) FMT_NOEXCEPT
+ : internal_{static_cast<uint128_t>(low) |
+ (static_cast<uint128_t>(high) << 64)} {}
+
+ constexpr uint128_wrapper(uint128_t u) : internal_{u} {}
+
+ constexpr uint64_t high() const FMT_NOEXCEPT {
+ return uint64_t(internal_ >> 64);
+ }
+ constexpr uint64_t low() const FMT_NOEXCEPT { return uint64_t(internal_); }
+
+ uint128_wrapper& operator+=(uint64_t n) FMT_NOEXCEPT {
+ internal_ += n;
+ return *this;
+ }
+#else
+ uint64_t high_;
+ uint64_t low_;
+
+ constexpr uint128_wrapper(uint64_t high, uint64_t low) FMT_NOEXCEPT
+ : high_{high},
+ low_{low} {}
+
+ constexpr uint64_t high() const FMT_NOEXCEPT { return high_; }
+ constexpr uint64_t low() const FMT_NOEXCEPT { return low_; }
+
+ uint128_wrapper& operator+=(uint64_t n) FMT_NOEXCEPT {
+# if defined(_MSC_VER) && defined(_M_X64)
+ unsigned char carry = _addcarry_u64(0, low_, n, &low_);
+ _addcarry_u64(carry, high_, 0, &high_);
+ return *this;
+# else
+ uint64_t sum = low_ + n;
+ high_ += (sum < low_ ? 1 : 0);
+ low_ = sum;
+ return *this;
+# endif
+ }
+#endif
+};
+
+// Implementation of Dragonbox algorithm: https://github.com/jk-jeon/dragonbox.
+namespace dragonbox {
+// Computes 128-bit result of multiplication of two 64-bit unsigned integers.
+inline uint128_wrapper umul128(uint64_t x, uint64_t y) FMT_NOEXCEPT {
+#if FMT_USE_INT128
+ return static_cast<uint128_t>(x) * static_cast<uint128_t>(y);
+#elif defined(_MSC_VER) && defined(_M_X64)
+ uint128_wrapper result;
+ result.low_ = _umul128(x, y, &result.high_);
+ return result;
+#else
+ const uint64_t mask = (uint64_t(1) << 32) - uint64_t(1);
+
+ 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
+}
+
+// Computes upper 64 bits of multiplication of two 64-bit unsigned integers.
+inline uint64_t umul128_upper64(uint64_t x, uint64_t y) FMT_NOEXCEPT {
+#if FMT_USE_INT128
+ auto p = static_cast<uint128_t>(x) * static_cast<uint128_t>(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 64 bits of multiplication of a 64-bit unsigned integer and a
+// 128-bit unsigned integer.
+inline uint64_t umul192_upper64(uint64_t x, uint128_wrapper y) FMT_NOEXCEPT {
+ uint128_wrapper g0 = umul128(x, y.high());
+ g0 += umul128_upper64(x, y.low());
+ return g0.high();
+}
+
+// Computes upper 32 bits of multiplication of a 32-bit unsigned integer and a
+// 64-bit unsigned integer.
+inline uint32_t umul96_upper32(uint32_t x, uint64_t y) FMT_NOEXCEPT {
+ return static_cast<uint32_t>(umul128_upper64(x, y));
+}
+
+// Computes middle 64 bits of multiplication of a 64-bit unsigned integer and a
+// 128-bit unsigned integer.
+inline uint64_t umul192_middle64(uint64_t x, uint128_wrapper y) FMT_NOEXCEPT {
+ uint64_t g01 = x * y.high();
+ uint64_t g10 = umul128_upper64(x, y.low());
+ return g01 + g10;
+}
+
+// Computes lower 64 bits of multiplication of a 32-bit unsigned integer and a
+// 64-bit unsigned integer.
+inline uint64_t umul96_lower64(uint32_t x, uint64_t y) FMT_NOEXCEPT {
+ return x * y;
+}
+
+// Computes floor(log10(pow(2, e))) for e in [-1700, 1700] using the method from
+// https://fmt.dev/papers/Grisu-Exact.pdf#page=5, section 3.4.
+inline int floor_log10_pow2(int e) FMT_NOEXCEPT {
+ FMT_ASSERT(e <= 1700 && e >= -1700, "too large exponent");
+ const int shift = 22;
+ return (e * static_cast<int>(data::log10_2_significand >> (64 - shift))) >>
+ shift;
+}
+
+// Various fast log computations.
+inline int floor_log2_pow10(int e) FMT_NOEXCEPT {
+ FMT_ASSERT(e <= 1233 && e >= -1233, "too large exponent");
+ const uint64_t log2_10_integer_part = 3;
+ const uint64_t log2_10_fractional_digits = 0x5269e12f346e2bf9;
+ const int shift_amount = 19;
+ return (e * static_cast<int>(
+ (log2_10_integer_part << shift_amount) |
+ (log2_10_fractional_digits >> (64 - shift_amount)))) >>
+ shift_amount;
+}
+inline int floor_log10_pow2_minus_log10_4_over_3(int e) FMT_NOEXCEPT {
+ FMT_ASSERT(e <= 1700 && e >= -1700, "too large exponent");
+ const uint64_t log10_4_over_3_fractional_digits = 0x1ffbfc2bbc780375;
+ const int shift_amount = 22;
+ return (e * static_cast<int>(data::log10_2_significand >>
+ (64 - shift_amount)) -
+ static_cast<int>(log10_4_over_3_fractional_digits >>
+ (64 - shift_amount))) >>
+ shift_amount;
+}
+
+// Returns true iff x is divisible by pow(2, exp).
+inline bool divisible_by_power_of_2(uint32_t x, int exp) FMT_NOEXCEPT {
+ FMT_ASSERT(exp >= 1, "");
+ FMT_ASSERT(x != 0, "");
+#ifdef FMT_BUILTIN_CTZ
+ return FMT_BUILTIN_CTZ(x) >= exp;
+#else
+ return exp < num_bits<uint32_t>() && x == ((x >> exp) << exp);
+#endif
+}
+inline bool divisible_by_power_of_2(uint64_t x, int exp) FMT_NOEXCEPT {
+ FMT_ASSERT(exp >= 1, "");
+ FMT_ASSERT(x != 0, "");
+#ifdef FMT_BUILTIN_CTZLL
+ return FMT_BUILTIN_CTZLL(x) >= exp;
+#else
+ return exp < num_bits<uint64_t>() && x == ((x >> exp) << exp);
+#endif
+}
+
+// Table entry type for divisibility test.
+template <typename T> struct divtest_table_entry {
+ T mod_inv;
+ T max_quotient;
+};
+
+// Returns true iff x is divisible by pow(5, exp).
+inline bool divisible_by_power_of_5(uint32_t x, int exp) FMT_NOEXCEPT {
+ FMT_ASSERT(exp <= 10, "too large exponent");
+ static constexpr const divtest_table_entry<uint32_t> divtest_table[] = {
+ {0x00000001, 0xffffffff}, {0xcccccccd, 0x33333333},
+ {0xc28f5c29, 0x0a3d70a3}, {0x26e978d5, 0x020c49ba},
+ {0x3afb7e91, 0x0068db8b}, {0x0bcbe61d, 0x0014f8b5},
+ {0x68c26139, 0x000431bd}, {0xae8d46a5, 0x0000d6bf},
+ {0x22e90e21, 0x00002af3}, {0x3a2e9c6d, 0x00000897},
+ {0x3ed61f49, 0x000001b7}};
+ return x * divtest_table[exp].mod_inv <= divtest_table[exp].max_quotient;
+}
+inline bool divisible_by_power_of_5(uint64_t x, int exp) FMT_NOEXCEPT {
+ FMT_ASSERT(exp <= 23, "too large exponent");
+ static constexpr const divtest_table_entry<uint64_t> divtest_table[] = {
+ {0x0000000000000001, 0xffffffffffffffff},
+ {0xcccccccccccccccd, 0x3333333333333333},
+ {0x8f5c28f5c28f5c29, 0x0a3d70a3d70a3d70},
+ {0x1cac083126e978d5, 0x020c49ba5e353f7c},
+ {0xd288ce703afb7e91, 0x0068db8bac710cb2},
+ {0x5d4e8fb00bcbe61d, 0x0014f8b588e368f0},
+ {0x790fb65668c26139, 0x000431bde82d7b63},
+ {0xe5032477ae8d46a5, 0x0000d6bf94d5e57a},
+ {0xc767074b22e90e21, 0x00002af31dc46118},
+ {0x8e47ce423a2e9c6d, 0x0000089705f4136b},
+ {0x4fa7f60d3ed61f49, 0x000001b7cdfd9d7b},
+ {0x0fee64690c913975, 0x00000057f5ff85e5},
+ {0x3662e0e1cf503eb1, 0x000000119799812d},
+ {0xa47a2cf9f6433fbd, 0x0000000384b84d09},
+ {0x54186f653140a659, 0x00000000b424dc35},
+ {0x7738164770402145, 0x0000000024075f3d},
+ {0xe4a4d1417cd9a041, 0x000000000734aca5},
+ {0xc75429d9e5c5200d, 0x000000000170ef54},
+ {0xc1773b91fac10669, 0x000000000049c977},
+ {0x26b172506559ce15, 0x00000000000ec1e4},
+ {0xd489e3a9addec2d1, 0x000000000002f394},
+ {0x90e860bb892c8d5d, 0x000000000000971d},
+ {0x502e79bf1b6f4f79, 0x0000000000001e39},
+ {0xdcd618596be30fe5, 0x000000000000060b}};
+ return x * divtest_table[exp].mod_inv <= divtest_table[exp].max_quotient;
+}
+
+// Replaces n by floor(n / pow(5, N)) returning true if and only if n is
+// divisible by pow(5, N).
+// Precondition: n <= 2 * pow(5, N + 1).
+template <int N>
+bool check_divisibility_and_divide_by_pow5(uint32_t& n) FMT_NOEXCEPT {
+ static constexpr struct {
+ uint32_t magic_number;
+ int bits_for_comparison;
+ uint32_t threshold;
+ int shift_amount;
+ } infos[] = {{0xcccd, 16, 0x3333, 18}, {0xa429, 8, 0x0a, 20}};
+ constexpr auto info = infos[N - 1];
+ n *= info.magic_number;
+ const uint32_t comparison_mask = (1u << info.bits_for_comparison) - 1;
+ bool result = (n & comparison_mask) <= info.threshold;
+ n >>= info.shift_amount;
+ return result;
+}
+
+// Computes floor(n / pow(10, N)) for small n and N.
+// Precondition: n <= pow(10, N + 1).
+template <int N> uint32_t small_division_by_pow10(uint32_t n) FMT_NOEXCEPT {
+ static constexpr struct {
+ uint32_t magic_number;
+ int shift_amount;
+ uint32_t divisor_times_10;
+ } infos[] = {{0xcccd, 19, 100}, {0xa3d8, 22, 1000}};
+ constexpr auto info = infos[N - 1];
+ FMT_ASSERT(n <= info.divisor_times_10, "n is too large");
+ return n * info.magic_number >> info.shift_amount;
+}
+
+// Computes floor(n / 10^(kappa + 1)) (float)
+inline uint32_t divide_by_10_to_kappa_plus_1(uint32_t n) FMT_NOEXCEPT {
+ return n / float_info<float>::big_divisor;
+}
+// Computes floor(n / 10^(kappa + 1)) (double)
+inline uint64_t divide_by_10_to_kappa_plus_1(uint64_t n) FMT_NOEXCEPT {
+ return umul128_upper64(n, 0x83126e978d4fdf3c) >> 9;
+}
+
+// Various subroutines using pow10 cache
+template <class T> struct cache_accessor;
+
+template <> struct cache_accessor<float> {
+ using carrier_uint = float_info<float>::carrier_uint;
+ using cache_entry_type = uint64_t;
+
+ static uint64_t get_cached_power(int k) FMT_NOEXCEPT {
+ FMT_ASSERT(k >= float_info<float>::min_k && k <= float_info<float>::max_k,
+ "k is out of range");
+ constexpr const uint64_t pow10_significands[] = {
+ 0x81ceb32c4b43fcf5, 0xa2425ff75e14fc32, 0xcad2f7f5359a3b3f,
+ 0xfd87b5f28300ca0e, 0x9e74d1b791e07e49, 0xc612062576589ddb,
+ 0xf79687aed3eec552, 0x9abe14cd44753b53, 0xc16d9a0095928a28,
+ 0xf1c90080baf72cb2, 0x971da05074da7bef, 0xbce5086492111aeb,
+ 0xec1e4a7db69561a6, 0x9392ee8e921d5d08, 0xb877aa3236a4b44a,
+ 0xe69594bec44de15c, 0x901d7cf73ab0acda, 0xb424dc35095cd810,
+ 0xe12e13424bb40e14, 0x8cbccc096f5088cc, 0xafebff0bcb24aaff,
+ 0xdbe6fecebdedd5bf, 0x89705f4136b4a598, 0xabcc77118461cefd,
+ 0xd6bf94d5e57a42bd, 0x8637bd05af6c69b6, 0xa7c5ac471b478424,
+ 0xd1b71758e219652c, 0x83126e978d4fdf3c, 0xa3d70a3d70a3d70b,
+ 0xcccccccccccccccd, 0x8000000000000000, 0xa000000000000000,
+ 0xc800000000000000, 0xfa00000000000000, 0x9c40000000000000,
+ 0xc350000000000000, 0xf424000000000000, 0x9896800000000000,
+ 0xbebc200000000000, 0xee6b280000000000, 0x9502f90000000000,
+ 0xba43b74000000000, 0xe8d4a51000000000, 0x9184e72a00000000,
+ 0xb5e620f480000000, 0xe35fa931a0000000, 0x8e1bc9bf04000000,
+ 0xb1a2bc2ec5000000, 0xde0b6b3a76400000, 0x8ac7230489e80000,
+ 0xad78ebc5ac620000, 0xd8d726b7177a8000, 0x878678326eac9000,
+ 0xa968163f0a57b400, 0xd3c21bcecceda100, 0x84595161401484a0,
+ 0xa56fa5b99019a5c8, 0xcecb8f27f4200f3a, 0x813f3978f8940984,
+ 0xa18f07d736b90be5, 0xc9f2c9cd04674ede, 0xfc6f7c4045812296,
+ 0x9dc5ada82b70b59d, 0xc5371912364ce305, 0xf684df56c3e01bc6,
+ 0x9a130b963a6c115c, 0xc097ce7bc90715b3, 0xf0bdc21abb48db20,
+ 0x96769950b50d88f4, 0xbc143fa4e250eb31, 0xeb194f8e1ae525fd,
+ 0x92efd1b8d0cf37be, 0xb7abc627050305ad, 0xe596b7b0c643c719,
+ 0x8f7e32ce7bea5c6f, 0xb35dbf821ae4f38b, 0xe0352f62a19e306e};
+ return pow10_significands[k - float_info<float>::min_k];
+ }
+
+ static carrier_uint compute_mul(carrier_uint u,
+ const cache_entry_type& cache) FMT_NOEXCEPT {
+ return umul96_upper32(u, cache);
+ }
+
+ static uint32_t compute_delta(const cache_entry_type& cache,
+ int beta_minus_1) FMT_NOEXCEPT {
+ return static_cast<uint32_t>(cache >> (64 - 1 - beta_minus_1));
+ }
+
+ static bool compute_mul_parity(carrier_uint two_f,
+ const cache_entry_type& cache,
+ int beta_minus_1) FMT_NOEXCEPT {
+ FMT_ASSERT(beta_minus_1 >= 1, "");
+ FMT_ASSERT(beta_minus_1 < 64, "");
+
+ return ((umul96_lower64(two_f, cache) >> (64 - beta_minus_1)) & 1) != 0;
+ }
+
+ static carrier_uint compute_left_endpoint_for_shorter_interval_case(
+ const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT {
+ return static_cast<carrier_uint>(
+ (cache - (cache >> (float_info<float>::significand_bits + 2))) >>
+ (64 - float_info<float>::significand_bits - 1 - beta_minus_1));
+ }
+
+ static carrier_uint compute_right_endpoint_for_shorter_interval_case(
+ const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT {
+ return static_cast<carrier_uint>(
+ (cache + (cache >> (float_info<float>::significand_bits + 1))) >>
+ (64 - float_info<float>::significand_bits - 1 - beta_minus_1));
+ }
+
+ static carrier_uint compute_round_up_for_shorter_interval_case(
+ const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT {
+ return (static_cast<carrier_uint>(
+ cache >>
+ (64 - float_info<float>::significand_bits - 2 - beta_minus_1)) +
+ 1) /
+ 2;
+ }
+};
+
+template <> struct cache_accessor<double> {
+ using carrier_uint = float_info<double>::carrier_uint;
+ using cache_entry_type = uint128_wrapper;
+
+ static uint128_wrapper get_cached_power(int k) FMT_NOEXCEPT {
+ FMT_ASSERT(k >= float_info<double>::min_k && k <= float_info<double>::max_k,
+ "k is out of range");
+
+ static constexpr const uint128_wrapper pow10_significands[] = {
+#if FMT_USE_FULL_CACHE_DRAGONBOX
+ {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b},
+ {0x9faacf3df73609b1, 0x77b191618c54e9ad},
+ {0xc795830d75038c1d, 0xd59df5b9ef6a2418},
+ {0xf97ae3d0d2446f25, 0x4b0573286b44ad1e},
+ {0x9becce62836ac577, 0x4ee367f9430aec33},
+ {0xc2e801fb244576d5, 0x229c41f793cda740},
+ {0xf3a20279ed56d48a, 0x6b43527578c11110},
+ {0x9845418c345644d6, 0x830a13896b78aaaa},
+ {0xbe5691ef416bd60c, 0x23cc986bc656d554},
+ {0xedec366b11c6cb8f, 0x2cbfbe86b7ec8aa9},
+ {0x94b3a202eb1c3f39, 0x7bf7d71432f3d6aa},
+ {0xb9e08a83a5e34f07, 0xdaf5ccd93fb0cc54},
+ {0xe858ad248f5c22c9, 0xd1b3400f8f9cff69},
+ {0x91376c36d99995be, 0x23100809b9c21fa2},
+ {0xb58547448ffffb2d, 0xabd40a0c2832a78b},
+ {0xe2e69915b3fff9f9, 0x16c90c8f323f516d},
+ {0x8dd01fad907ffc3b, 0xae3da7d97f6792e4},
+ {0xb1442798f49ffb4a, 0x99cd11cfdf41779d},
+ {0xdd95317f31c7fa1d, 0x40405643d711d584},
+ {0x8a7d3eef7f1cfc52, 0x482835ea666b2573},
+ {0xad1c8eab5ee43b66, 0xda3243650005eed0},
+ {0xd863b256369d4a40, 0x90bed43e40076a83},
+ {0x873e4f75e2224e68, 0x5a7744a6e804a292},
+ {0xa90de3535aaae202, 0x711515d0a205cb37},
+ {0xd3515c2831559a83, 0x0d5a5b44ca873e04},
+ {0x8412d9991ed58091, 0xe858790afe9486c3},
+ {0xa5178fff668ae0b6, 0x626e974dbe39a873},
+ {0xce5d73ff402d98e3, 0xfb0a3d212dc81290},
+ {0x80fa687f881c7f8e, 0x7ce66634bc9d0b9a},
+ {0xa139029f6a239f72, 0x1c1fffc1ebc44e81},
+ {0xc987434744ac874e, 0xa327ffb266b56221},
+ {0xfbe9141915d7a922, 0x4bf1ff9f0062baa9},
+ {0x9d71ac8fada6c9b5, 0x6f773fc3603db4aa},
+ {0xc4ce17b399107c22, 0xcb550fb4384d21d4},
+ {0xf6019da07f549b2b, 0x7e2a53a146606a49},
+ {0x99c102844f94e0fb, 0x2eda7444cbfc426e},
+ {0xc0314325637a1939, 0xfa911155fefb5309},
+ {0xf03d93eebc589f88, 0x793555ab7eba27cb},
+ {0x96267c7535b763b5, 0x4bc1558b2f3458df},
+ {0xbbb01b9283253ca2, 0x9eb1aaedfb016f17},
+ {0xea9c227723ee8bcb, 0x465e15a979c1cadd},
+ {0x92a1958a7675175f, 0x0bfacd89ec191eca},
+ {0xb749faed14125d36, 0xcef980ec671f667c},
+ {0xe51c79a85916f484, 0x82b7e12780e7401b},
+ {0x8f31cc0937ae58d2, 0xd1b2ecb8b0908811},
+ {0xb2fe3f0b8599ef07, 0x861fa7e6dcb4aa16},
+ {0xdfbdcece67006ac9, 0x67a791e093e1d49b},
+ {0x8bd6a141006042bd, 0xe0c8bb2c5c6d24e1},
+ {0xaecc49914078536d, 0x58fae9f773886e19},
+ {0xda7f5bf590966848, 0xaf39a475506a899f},
+ {0x888f99797a5e012d, 0x6d8406c952429604},
+ {0xaab37fd7d8f58178, 0xc8e5087ba6d33b84},
+ {0xd5605fcdcf32e1d6, 0xfb1e4a9a90880a65},
+ {0x855c3be0a17fcd26, 0x5cf2eea09a550680},
+ {0xa6b34ad8c9dfc06f, 0xf42faa48c0ea481f},
+ {0xd0601d8efc57b08b, 0xf13b94daf124da27},
+ {0x823c12795db6ce57, 0x76c53d08d6b70859},
+ {0xa2cb1717b52481ed, 0x54768c4b0c64ca6f},
+ {0xcb7ddcdda26da268, 0xa9942f5dcf7dfd0a},
+ {0xfe5d54150b090b02, 0xd3f93b35435d7c4d},
+ {0x9efa548d26e5a6e1, 0xc47bc5014a1a6db0},
+ {0xc6b8e9b0709f109a, 0x359ab6419ca1091c},
+ {0xf867241c8cc6d4c0, 0xc30163d203c94b63},
+ {0x9b407691d7fc44f8, 0x79e0de63425dcf1e},
+ {0xc21094364dfb5636, 0x985915fc12f542e5},
+ {0xf294b943e17a2bc4, 0x3e6f5b7b17b2939e},
+ {0x979cf3ca6cec5b5a, 0xa705992ceecf9c43},
+ {0xbd8430bd08277231, 0x50c6ff782a838354},
+ {0xece53cec4a314ebd, 0xa4f8bf5635246429},
+ {0x940f4613ae5ed136, 0x871b7795e136be9a},
+ {0xb913179899f68584, 0x28e2557b59846e40},
+ {0xe757dd7ec07426e5, 0x331aeada2fe589d0},
+ {0x9096ea6f3848984f, 0x3ff0d2c85def7622},
+ {0xb4bca50b065abe63, 0x0fed077a756b53aa},
+ {0xe1ebce4dc7f16dfb, 0xd3e8495912c62895},
+ {0x8d3360f09cf6e4bd, 0x64712dd7abbbd95d},
+ {0xb080392cc4349dec, 0xbd8d794d96aacfb4},
+ {0xdca04777f541c567, 0xecf0d7a0fc5583a1},
+ {0x89e42caaf9491b60, 0xf41686c49db57245},
+ {0xac5d37d5b79b6239, 0x311c2875c522ced6},
+ {0xd77485cb25823ac7, 0x7d633293366b828c},
+ {0x86a8d39ef77164bc, 0xae5dff9c02033198},
+ {0xa8530886b54dbdeb, 0xd9f57f830283fdfd},
+ {0xd267caa862a12d66, 0xd072df63c324fd7c},
+ {0x8380dea93da4bc60, 0x4247cb9e59f71e6e},
+ {0xa46116538d0deb78, 0x52d9be85f074e609},
+ {0xcd795be870516656, 0x67902e276c921f8c},
+ {0x806bd9714632dff6, 0x00ba1cd8a3db53b7},
+ {0xa086cfcd97bf97f3, 0x80e8a40eccd228a5},
+ {0xc8a883c0fdaf7df0, 0x6122cd128006b2ce},
+ {0xfad2a4b13d1b5d6c, 0x796b805720085f82},
+ {0x9cc3a6eec6311a63, 0xcbe3303674053bb1},
+ {0xc3f490aa77bd60fc, 0xbedbfc4411068a9d},
+ {0xf4f1b4d515acb93b, 0xee92fb5515482d45},
+ {0x991711052d8bf3c5, 0x751bdd152d4d1c4b},
+ {0xbf5cd54678eef0b6, 0xd262d45a78a0635e},
+ {0xef340a98172aace4, 0x86fb897116c87c35},
+ {0x9580869f0e7aac0e, 0xd45d35e6ae3d4da1},
+ {0xbae0a846d2195712, 0x8974836059cca10a},
+ {0xe998d258869facd7, 0x2bd1a438703fc94c},
+ {0x91ff83775423cc06, 0x7b6306a34627ddd0},
+ {0xb67f6455292cbf08, 0x1a3bc84c17b1d543},
+ {0xe41f3d6a7377eeca, 0x20caba5f1d9e4a94},
+ {0x8e938662882af53e, 0x547eb47b7282ee9d},
+ {0xb23867fb2a35b28d, 0xe99e619a4f23aa44},
+ {0xdec681f9f4c31f31, 0x6405fa00e2ec94d5},
+ {0x8b3c113c38f9f37e, 0xde83bc408dd3dd05},
+ {0xae0b158b4738705e, 0x9624ab50b148d446},
+ {0xd98ddaee19068c76, 0x3badd624dd9b0958},
+ {0x87f8a8d4cfa417c9, 0xe54ca5d70a80e5d7},
+ {0xa9f6d30a038d1dbc, 0x5e9fcf4ccd211f4d},
+ {0xd47487cc8470652b, 0x7647c32000696720},
+ {0x84c8d4dfd2c63f3b, 0x29ecd9f40041e074},
+ {0xa5fb0a17c777cf09, 0xf468107100525891},
+ {0xcf79cc9db955c2cc, 0x7182148d4066eeb5},
+ {0x81ac1fe293d599bf, 0xc6f14cd848405531},
+ {0xa21727db38cb002f, 0xb8ada00e5a506a7d},
+ {0xca9cf1d206fdc03b, 0xa6d90811f0e4851d},
+ {0xfd442e4688bd304a, 0x908f4a166d1da664},
+ {0x9e4a9cec15763e2e, 0x9a598e4e043287ff},
+ {0xc5dd44271ad3cdba, 0x40eff1e1853f29fe},
+ {0xf7549530e188c128, 0xd12bee59e68ef47d},
+ {0x9a94dd3e8cf578b9, 0x82bb74f8301958cf},
+ {0xc13a148e3032d6e7, 0xe36a52363c1faf02},
+ {0xf18899b1bc3f8ca1, 0xdc44e6c3cb279ac2},
+ {0x96f5600f15a7b7e5, 0x29ab103a5ef8c0ba},
+ {0xbcb2b812db11a5de, 0x7415d448f6b6f0e8},
+ {0xebdf661791d60f56, 0x111b495b3464ad22},
+ {0x936b9fcebb25c995, 0xcab10dd900beec35},
+ {0xb84687c269ef3bfb, 0x3d5d514f40eea743},
+ {0xe65829b3046b0afa, 0x0cb4a5a3112a5113},
+ {0x8ff71a0fe2c2e6dc, 0x47f0e785eaba72ac},
+ {0xb3f4e093db73a093, 0x59ed216765690f57},
+ {0xe0f218b8d25088b8, 0x306869c13ec3532d},
+ {0x8c974f7383725573, 0x1e414218c73a13fc},
+ {0xafbd2350644eeacf, 0xe5d1929ef90898fb},
+ {0xdbac6c247d62a583, 0xdf45f746b74abf3a},
+ {0x894bc396ce5da772, 0x6b8bba8c328eb784},
+ {0xab9eb47c81f5114f, 0x066ea92f3f326565},
+ {0xd686619ba27255a2, 0xc80a537b0efefebe},
+ {0x8613fd0145877585, 0xbd06742ce95f5f37},
+ {0xa798fc4196e952e7, 0x2c48113823b73705},
+ {0xd17f3b51fca3a7a0, 0xf75a15862ca504c6},
+ {0x82ef85133de648c4, 0x9a984d73dbe722fc},
+ {0xa3ab66580d5fdaf5, 0xc13e60d0d2e0ebbb},
+ {0xcc963fee10b7d1b3, 0x318df905079926a9},
+ {0xffbbcfe994e5c61f, 0xfdf17746497f7053},
+ {0x9fd561f1fd0f9bd3, 0xfeb6ea8bedefa634},
+ {0xc7caba6e7c5382c8, 0xfe64a52ee96b8fc1},
+ {0xf9bd690a1b68637b, 0x3dfdce7aa3c673b1},
+ {0x9c1661a651213e2d, 0x06bea10ca65c084f},
+ {0xc31bfa0fe5698db8, 0x486e494fcff30a63},
+ {0xf3e2f893dec3f126, 0x5a89dba3c3efccfb},
+ {0x986ddb5c6b3a76b7, 0xf89629465a75e01d},
+ {0xbe89523386091465, 0xf6bbb397f1135824},
+ {0xee2ba6c0678b597f, 0x746aa07ded582e2d},
+ {0x94db483840b717ef, 0xa8c2a44eb4571cdd},
+ {0xba121a4650e4ddeb, 0x92f34d62616ce414},
+ {0xe896a0d7e51e1566, 0x77b020baf9c81d18},
+ {0x915e2486ef32cd60, 0x0ace1474dc1d122f},
+ {0xb5b5ada8aaff80b8, 0x0d819992132456bb},
+ {0xe3231912d5bf60e6, 0x10e1fff697ed6c6a},
+ {0x8df5efabc5979c8f, 0xca8d3ffa1ef463c2},
+ {0xb1736b96b6fd83b3, 0xbd308ff8a6b17cb3},
+ {0xddd0467c64bce4a0, 0xac7cb3f6d05ddbdf},
+ {0x8aa22c0dbef60ee4, 0x6bcdf07a423aa96c},
+ {0xad4ab7112eb3929d, 0x86c16c98d2c953c7},
+ {0xd89d64d57a607744, 0xe871c7bf077ba8b8},
+ {0x87625f056c7c4a8b, 0x11471cd764ad4973},
+ {0xa93af6c6c79b5d2d, 0xd598e40d3dd89bd0},
+ {0xd389b47879823479, 0x4aff1d108d4ec2c4},
+ {0x843610cb4bf160cb, 0xcedf722a585139bb},
+ {0xa54394fe1eedb8fe, 0xc2974eb4ee658829},
+ {0xce947a3da6a9273e, 0x733d226229feea33},
+ {0x811ccc668829b887, 0x0806357d5a3f5260},
+ {0xa163ff802a3426a8, 0xca07c2dcb0cf26f8},
+ {0xc9bcff6034c13052, 0xfc89b393dd02f0b6},
+ {0xfc2c3f3841f17c67, 0xbbac2078d443ace3},
+ {0x9d9ba7832936edc0, 0xd54b944b84aa4c0e},
+ {0xc5029163f384a931, 0x0a9e795e65d4df12},
+ {0xf64335bcf065d37d, 0x4d4617b5ff4a16d6},
+ {0x99ea0196163fa42e, 0x504bced1bf8e4e46},
+ {0xc06481fb9bcf8d39, 0xe45ec2862f71e1d7},
+ {0xf07da27a82c37088, 0x5d767327bb4e5a4d},
+ {0x964e858c91ba2655, 0x3a6a07f8d510f870},
+ {0xbbe226efb628afea, 0x890489f70a55368c},
+ {0xeadab0aba3b2dbe5, 0x2b45ac74ccea842f},
+ {0x92c8ae6b464fc96f, 0x3b0b8bc90012929e},
+ {0xb77ada0617e3bbcb, 0x09ce6ebb40173745},
+ {0xe55990879ddcaabd, 0xcc420a6a101d0516},
+ {0x8f57fa54c2a9eab6, 0x9fa946824a12232e},
+ {0xb32df8e9f3546564, 0x47939822dc96abfa},
+ {0xdff9772470297ebd, 0x59787e2b93bc56f8},
+ {0x8bfbea76c619ef36, 0x57eb4edb3c55b65b},
+ {0xaefae51477a06b03, 0xede622920b6b23f2},
+ {0xdab99e59958885c4, 0xe95fab368e45ecee},
+ {0x88b402f7fd75539b, 0x11dbcb0218ebb415},
+ {0xaae103b5fcd2a881, 0xd652bdc29f26a11a},
+ {0xd59944a37c0752a2, 0x4be76d3346f04960},
+ {0x857fcae62d8493a5, 0x6f70a4400c562ddc},
+ {0xa6dfbd9fb8e5b88e, 0xcb4ccd500f6bb953},
+ {0xd097ad07a71f26b2, 0x7e2000a41346a7a8},
+ {0x825ecc24c873782f, 0x8ed400668c0c28c9},
+ {0xa2f67f2dfa90563b, 0x728900802f0f32fb},
+ {0xcbb41ef979346bca, 0x4f2b40a03ad2ffba},
+ {0xfea126b7d78186bc, 0xe2f610c84987bfa9},
+ {0x9f24b832e6b0f436, 0x0dd9ca7d2df4d7ca},
+ {0xc6ede63fa05d3143, 0x91503d1c79720dbc},
+ {0xf8a95fcf88747d94, 0x75a44c6397ce912b},
+ {0x9b69dbe1b548ce7c, 0xc986afbe3ee11abb},
+ {0xc24452da229b021b, 0xfbe85badce996169},
+ {0xf2d56790ab41c2a2, 0xfae27299423fb9c4},
+ {0x97c560ba6b0919a5, 0xdccd879fc967d41b},
+ {0xbdb6b8e905cb600f, 0x5400e987bbc1c921},
+ {0xed246723473e3813, 0x290123e9aab23b69},
+ {0x9436c0760c86e30b, 0xf9a0b6720aaf6522},
+ {0xb94470938fa89bce, 0xf808e40e8d5b3e6a},
+ {0xe7958cb87392c2c2, 0xb60b1d1230b20e05},
+ {0x90bd77f3483bb9b9, 0xb1c6f22b5e6f48c3},
+ {0xb4ecd5f01a4aa828, 0x1e38aeb6360b1af4},
+ {0xe2280b6c20dd5232, 0x25c6da63c38de1b1},
+ {0x8d590723948a535f, 0x579c487e5a38ad0f},
+ {0xb0af48ec79ace837, 0x2d835a9df0c6d852},
+ {0xdcdb1b2798182244, 0xf8e431456cf88e66},
+ {0x8a08f0f8bf0f156b, 0x1b8e9ecb641b5900},
+ {0xac8b2d36eed2dac5, 0xe272467e3d222f40},
+ {0xd7adf884aa879177, 0x5b0ed81dcc6abb10},
+ {0x86ccbb52ea94baea, 0x98e947129fc2b4ea},
+ {0xa87fea27a539e9a5, 0x3f2398d747b36225},
+ {0xd29fe4b18e88640e, 0x8eec7f0d19a03aae},
+ {0x83a3eeeef9153e89, 0x1953cf68300424ad},
+ {0xa48ceaaab75a8e2b, 0x5fa8c3423c052dd8},
+ {0xcdb02555653131b6, 0x3792f412cb06794e},
+ {0x808e17555f3ebf11, 0xe2bbd88bbee40bd1},
+ {0xa0b19d2ab70e6ed6, 0x5b6aceaeae9d0ec5},
+ {0xc8de047564d20a8b, 0xf245825a5a445276},
+ {0xfb158592be068d2e, 0xeed6e2f0f0d56713},
+ {0x9ced737bb6c4183d, 0x55464dd69685606c},
+ {0xc428d05aa4751e4c, 0xaa97e14c3c26b887},
+ {0xf53304714d9265df, 0xd53dd99f4b3066a9},
+ {0x993fe2c6d07b7fab, 0xe546a8038efe402a},
+ {0xbf8fdb78849a5f96, 0xde98520472bdd034},
+ {0xef73d256a5c0f77c, 0x963e66858f6d4441},
+ {0x95a8637627989aad, 0xdde7001379a44aa9},
+ {0xbb127c53b17ec159, 0x5560c018580d5d53},
+ {0xe9d71b689dde71af, 0xaab8f01e6e10b4a7},
+ {0x9226712162ab070d, 0xcab3961304ca70e9},
+ {0xb6b00d69bb55c8d1, 0x3d607b97c5fd0d23},
+ {0xe45c10c42a2b3b05, 0x8cb89a7db77c506b},
+ {0x8eb98a7a9a5b04e3, 0x77f3608e92adb243},
+ {0xb267ed1940f1c61c, 0x55f038b237591ed4},
+ {0xdf01e85f912e37a3, 0x6b6c46dec52f6689},
+ {0x8b61313bbabce2c6, 0x2323ac4b3b3da016},
+ {0xae397d8aa96c1b77, 0xabec975e0a0d081b},
+ {0xd9c7dced53c72255, 0x96e7bd358c904a22},
+ {0x881cea14545c7575, 0x7e50d64177da2e55},
+ {0xaa242499697392d2, 0xdde50bd1d5d0b9ea},
+ {0xd4ad2dbfc3d07787, 0x955e4ec64b44e865},
+ {0x84ec3c97da624ab4, 0xbd5af13bef0b113f},
+ {0xa6274bbdd0fadd61, 0xecb1ad8aeacdd58f},
+ {0xcfb11ead453994ba, 0x67de18eda5814af3},
+ {0x81ceb32c4b43fcf4, 0x80eacf948770ced8},
+ {0xa2425ff75e14fc31, 0xa1258379a94d028e},
+ {0xcad2f7f5359a3b3e, 0x096ee45813a04331},
+ {0xfd87b5f28300ca0d, 0x8bca9d6e188853fd},
+ {0x9e74d1b791e07e48, 0x775ea264cf55347e},
+ {0xc612062576589dda, 0x95364afe032a819e},
+ {0xf79687aed3eec551, 0x3a83ddbd83f52205},
+ {0x9abe14cd44753b52, 0xc4926a9672793543},
+ {0xc16d9a0095928a27, 0x75b7053c0f178294},
+ {0xf1c90080baf72cb1, 0x5324c68b12dd6339},
+ {0x971da05074da7bee, 0xd3f6fc16ebca5e04},
+ {0xbce5086492111aea, 0x88f4bb1ca6bcf585},
+ {0xec1e4a7db69561a5, 0x2b31e9e3d06c32e6},
+ {0x9392ee8e921d5d07, 0x3aff322e62439fd0},
+ {0xb877aa3236a4b449, 0x09befeb9fad487c3},
+ {0xe69594bec44de15b, 0x4c2ebe687989a9b4},
+ {0x901d7cf73ab0acd9, 0x0f9d37014bf60a11},
+ {0xb424dc35095cd80f, 0x538484c19ef38c95},
+ {0xe12e13424bb40e13, 0x2865a5f206b06fba},
+ {0x8cbccc096f5088cb, 0xf93f87b7442e45d4},
+ {0xafebff0bcb24aafe, 0xf78f69a51539d749},
+ {0xdbe6fecebdedd5be, 0xb573440e5a884d1c},
+ {0x89705f4136b4a597, 0x31680a88f8953031},
+ {0xabcc77118461cefc, 0xfdc20d2b36ba7c3e},
+ {0xd6bf94d5e57a42bc, 0x3d32907604691b4d},
+ {0x8637bd05af6c69b5, 0xa63f9a49c2c1b110},
+ {0xa7c5ac471b478423, 0x0fcf80dc33721d54},
+ {0xd1b71758e219652b, 0xd3c36113404ea4a9},
+ {0x83126e978d4fdf3b, 0x645a1cac083126ea},
+ {0xa3d70a3d70a3d70a, 0x3d70a3d70a3d70a4},
+ {0xcccccccccccccccc, 0xcccccccccccccccd},
+ {0x8000000000000000, 0x0000000000000000},
+ {0xa000000000000000, 0x0000000000000000},
+ {0xc800000000000000, 0x0000000000000000},
+ {0xfa00000000000000, 0x0000000000000000},
+ {0x9c40000000000000, 0x0000000000000000},
+ {0xc350000000000000, 0x0000000000000000},
+ {0xf424000000000000, 0x0000000000000000},
+ {0x9896800000000000, 0x0000000000000000},
+ {0xbebc200000000000, 0x0000000000000000},
+ {0xee6b280000000000, 0x0000000000000000},
+ {0x9502f90000000000, 0x0000000000000000},
+ {0xba43b74000000000, 0x0000000000000000},
+ {0xe8d4a51000000000, 0x0000000000000000},
+ {0x9184e72a00000000, 0x0000000000000000},
+ {0xb5e620f480000000, 0x0000000000000000},
+ {0xe35fa931a0000000, 0x0000000000000000},
+ {0x8e1bc9bf04000000, 0x0000000000000000},
+ {0xb1a2bc2ec5000000, 0x0000000000000000},
+ {0xde0b6b3a76400000, 0x0000000000000000},
+ {0x8ac7230489e80000, 0x0000000000000000},
+ {0xad78ebc5ac620000, 0x0000000000000000},
+ {0xd8d726b7177a8000, 0x0000000000000000},
+ {0x878678326eac9000, 0x0000000000000000},
+ {0xa968163f0a57b400, 0x0000000000000000},
+ {0xd3c21bcecceda100, 0x0000000000000000},
+ {0x84595161401484a0, 0x0000000000000000},
+ {0xa56fa5b99019a5c8, 0x0000000000000000},
+ {0xcecb8f27f4200f3a, 0x0000000000000000},
+ {0x813f3978f8940984, 0x4000000000000000},
+ {0xa18f07d736b90be5, 0x5000000000000000},
+ {0xc9f2c9cd04674ede, 0xa400000000000000},
+ {0xfc6f7c4045812296, 0x4d00000000000000},
+ {0x9dc5ada82b70b59d, 0xf020000000000000},
+ {0xc5371912364ce305, 0x6c28000000000000},
+ {0xf684df56c3e01bc6, 0xc732000000000000},
+ {0x9a130b963a6c115c, 0x3c7f400000000000},
+ {0xc097ce7bc90715b3, 0x4b9f100000000000},
+ {0xf0bdc21abb48db20, 0x1e86d40000000000},
+ {0x96769950b50d88f4, 0x1314448000000000},
+ {0xbc143fa4e250eb31, 0x17d955a000000000},
+ {0xeb194f8e1ae525fd, 0x5dcfab0800000000},
+ {0x92efd1b8d0cf37be, 0x5aa1cae500000000},
+ {0xb7abc627050305ad, 0xf14a3d9e40000000},
+ {0xe596b7b0c643c719, 0x6d9ccd05d0000000},
+ {0x8f7e32ce7bea5c6f, 0xe4820023a2000000},
+ {0xb35dbf821ae4f38b, 0xdda2802c8a800000},
+ {0xe0352f62a19e306e, 0xd50b2037ad200000},
+ {0x8c213d9da502de45, 0x4526f422cc340000},
+ {0xaf298d050e4395d6, 0x9670b12b7f410000},
+ {0xdaf3f04651d47b4c, 0x3c0cdd765f114000},
+ {0x88d8762bf324cd0f, 0xa5880a69fb6ac800},
+ {0xab0e93b6efee0053, 0x8eea0d047a457a00},
+ {0xd5d238a4abe98068, 0x72a4904598d6d880},
+ {0x85a36366eb71f041, 0x47a6da2b7f864750},
+ {0xa70c3c40a64e6c51, 0x999090b65f67d924},
+ {0xd0cf4b50cfe20765, 0xfff4b4e3f741cf6d},
+ {0x82818f1281ed449f, 0xbff8f10e7a8921a4},
+ {0xa321f2d7226895c7, 0xaff72d52192b6a0d},
+ {0xcbea6f8ceb02bb39, 0x9bf4f8a69f764490},
+ {0xfee50b7025c36a08, 0x02f236d04753d5b4},
+ {0x9f4f2726179a2245, 0x01d762422c946590},
+ {0xc722f0ef9d80aad6, 0x424d3ad2b7b97ef5},
+ {0xf8ebad2b84e0d58b, 0xd2e0898765a7deb2},
+ {0x9b934c3b330c8577, 0x63cc55f49f88eb2f},
+ {0xc2781f49ffcfa6d5, 0x3cbf6b71c76b25fb},
+ {0xf316271c7fc3908a, 0x8bef464e3945ef7a},
+ {0x97edd871cfda3a56, 0x97758bf0e3cbb5ac},
+ {0xbde94e8e43d0c8ec, 0x3d52eeed1cbea317},
+ {0xed63a231d4c4fb27, 0x4ca7aaa863ee4bdd},
+ {0x945e455f24fb1cf8, 0x8fe8caa93e74ef6a},
+ {0xb975d6b6ee39e436, 0xb3e2fd538e122b44},
+ {0xe7d34c64a9c85d44, 0x60dbbca87196b616},
+ {0x90e40fbeea1d3a4a, 0xbc8955e946fe31cd},
+ {0xb51d13aea4a488dd, 0x6babab6398bdbe41},
+ {0xe264589a4dcdab14, 0xc696963c7eed2dd1},
+ {0x8d7eb76070a08aec, 0xfc1e1de5cf543ca2},
+ {0xb0de65388cc8ada8, 0x3b25a55f43294bcb},
+ {0xdd15fe86affad912, 0x49ef0eb713f39ebe},
+ {0x8a2dbf142dfcc7ab, 0x6e3569326c784337},
+ {0xacb92ed9397bf996, 0x49c2c37f07965404},
+ {0xd7e77a8f87daf7fb, 0xdc33745ec97be906},
+ {0x86f0ac99b4e8dafd, 0x69a028bb3ded71a3},
+ {0xa8acd7c0222311bc, 0xc40832ea0d68ce0c},
+ {0xd2d80db02aabd62b, 0xf50a3fa490c30190},
+ {0x83c7088e1aab65db, 0x792667c6da79e0fa},
+ {0xa4b8cab1a1563f52, 0x577001b891185938},
+ {0xcde6fd5e09abcf26, 0xed4c0226b55e6f86},
+ {0x80b05e5ac60b6178, 0x544f8158315b05b4},
+ {0xa0dc75f1778e39d6, 0x696361ae3db1c721},
+ {0xc913936dd571c84c, 0x03bc3a19cd1e38e9},
+ {0xfb5878494ace3a5f, 0x04ab48a04065c723},
+ {0x9d174b2dcec0e47b, 0x62eb0d64283f9c76},
+ {0xc45d1df942711d9a, 0x3ba5d0bd324f8394},
+ {0xf5746577930d6500, 0xca8f44ec7ee36479},
+ {0x9968bf6abbe85f20, 0x7e998b13cf4e1ecb},
+ {0xbfc2ef456ae276e8, 0x9e3fedd8c321a67e},
+ {0xefb3ab16c59b14a2, 0xc5cfe94ef3ea101e},
+ {0x95d04aee3b80ece5, 0xbba1f1d158724a12},
+ {0xbb445da9ca61281f, 0x2a8a6e45ae8edc97},
+ {0xea1575143cf97226, 0xf52d09d71a3293bd},
+ {0x924d692ca61be758, 0x593c2626705f9c56},
+ {0xb6e0c377cfa2e12e, 0x6f8b2fb00c77836c},
+ {0xe498f455c38b997a, 0x0b6dfb9c0f956447},
+ {0x8edf98b59a373fec, 0x4724bd4189bd5eac},
+ {0xb2977ee300c50fe7, 0x58edec91ec2cb657},
+ {0xdf3d5e9bc0f653e1, 0x2f2967b66737e3ed},
+ {0x8b865b215899f46c, 0xbd79e0d20082ee74},
+ {0xae67f1e9aec07187, 0xecd8590680a3aa11},
+ {0xda01ee641a708de9, 0xe80e6f4820cc9495},
+ {0x884134fe908658b2, 0x3109058d147fdcdd},
+ {0xaa51823e34a7eede, 0xbd4b46f0599fd415},
+ {0xd4e5e2cdc1d1ea96, 0x6c9e18ac7007c91a},
+ {0x850fadc09923329e, 0x03e2cf6bc604ddb0},
+ {0xa6539930bf6bff45, 0x84db8346b786151c},
+ {0xcfe87f7cef46ff16, 0xe612641865679a63},
+ {0x81f14fae158c5f6e, 0x4fcb7e8f3f60c07e},
+ {0xa26da3999aef7749, 0xe3be5e330f38f09d},
+ {0xcb090c8001ab551c, 0x5cadf5bfd3072cc5},
+ {0xfdcb4fa002162a63, 0x73d9732fc7c8f7f6},
+ {0x9e9f11c4014dda7e, 0x2867e7fddcdd9afa},
+ {0xc646d63501a1511d, 0xb281e1fd541501b8},
+ {0xf7d88bc24209a565, 0x1f225a7ca91a4226},
+ {0x9ae757596946075f, 0x3375788de9b06958},
+ {0xc1a12d2fc3978937, 0x0052d6b1641c83ae},
+ {0xf209787bb47d6b84, 0xc0678c5dbd23a49a},
+ {0x9745eb4d50ce6332, 0xf840b7ba963646e0},
+ {0xbd176620a501fbff, 0xb650e5a93bc3d898},
+ {0xec5d3fa8ce427aff, 0xa3e51f138ab4cebe},
+ {0x93ba47c980e98cdf, 0xc66f336c36b10137},
+ {0xb8a8d9bbe123f017, 0xb80b0047445d4184},
+ {0xe6d3102ad96cec1d, 0xa60dc059157491e5},
+ {0x9043ea1ac7e41392, 0x87c89837ad68db2f},
+ {0xb454e4a179dd1877, 0x29babe4598c311fb},
+ {0xe16a1dc9d8545e94, 0xf4296dd6fef3d67a},
+ {0x8ce2529e2734bb1d, 0x1899e4a65f58660c},
+ {0xb01ae745b101e9e4, 0x5ec05dcff72e7f8f},
+ {0xdc21a1171d42645d, 0x76707543f4fa1f73},
+ {0x899504ae72497eba, 0x6a06494a791c53a8},
+ {0xabfa45da0edbde69, 0x0487db9d17636892},
+ {0xd6f8d7509292d603, 0x45a9d2845d3c42b6},
+ {0x865b86925b9bc5c2, 0x0b8a2392ba45a9b2},
+ {0xa7f26836f282b732, 0x8e6cac7768d7141e},
+ {0xd1ef0244af2364ff, 0x3207d795430cd926},
+ {0x8335616aed761f1f, 0x7f44e6bd49e807b8},
+ {0xa402b9c5a8d3a6e7, 0x5f16206c9c6209a6},
+ {0xcd036837130890a1, 0x36dba887c37a8c0f},
+ {0x802221226be55a64, 0xc2494954da2c9789},
+ {0xa02aa96b06deb0fd, 0xf2db9baa10b7bd6c},
+ {0xc83553c5c8965d3d, 0x6f92829494e5acc7},
+ {0xfa42a8b73abbf48c, 0xcb772339ba1f17f9},
+ {0x9c69a97284b578d7, 0xff2a760414536efb},
+ {0xc38413cf25e2d70d, 0xfef5138519684aba},
+ {0xf46518c2ef5b8cd1, 0x7eb258665fc25d69},
+ {0x98bf2f79d5993802, 0xef2f773ffbd97a61},
+ {0xbeeefb584aff8603, 0xaafb550ffacfd8fa},
+ {0xeeaaba2e5dbf6784, 0x95ba2a53f983cf38},
+ {0x952ab45cfa97a0b2, 0xdd945a747bf26183},
+ {0xba756174393d88df, 0x94f971119aeef9e4},
+ {0xe912b9d1478ceb17, 0x7a37cd5601aab85d},
+ {0x91abb422ccb812ee, 0xac62e055c10ab33a},
+ {0xb616a12b7fe617aa, 0x577b986b314d6009},
+ {0xe39c49765fdf9d94, 0xed5a7e85fda0b80b},
+ {0x8e41ade9fbebc27d, 0x14588f13be847307},
+ {0xb1d219647ae6b31c, 0x596eb2d8ae258fc8},
+ {0xde469fbd99a05fe3, 0x6fca5f8ed9aef3bb},
+ {0x8aec23d680043bee, 0x25de7bb9480d5854},
+ {0xada72ccc20054ae9, 0xaf561aa79a10ae6a},
+ {0xd910f7ff28069da4, 0x1b2ba1518094da04},
+ {0x87aa9aff79042286, 0x90fb44d2f05d0842},
+ {0xa99541bf57452b28, 0x353a1607ac744a53},
+ {0xd3fa922f2d1675f2, 0x42889b8997915ce8},
+ {0x847c9b5d7c2e09b7, 0x69956135febada11},
+ {0xa59bc234db398c25, 0x43fab9837e699095},
+ {0xcf02b2c21207ef2e, 0x94f967e45e03f4bb},
+ {0x8161afb94b44f57d, 0x1d1be0eebac278f5},
+ {0xa1ba1ba79e1632dc, 0x6462d92a69731732},
+ {0xca28a291859bbf93, 0x7d7b8f7503cfdcfe},
+ {0xfcb2cb35e702af78, 0x5cda735244c3d43e},
+ {0x9defbf01b061adab, 0x3a0888136afa64a7},
+ {0xc56baec21c7a1916, 0x088aaa1845b8fdd0},
+ {0xf6c69a72a3989f5b, 0x8aad549e57273d45},
+ {0x9a3c2087a63f6399, 0x36ac54e2f678864b},
+ {0xc0cb28a98fcf3c7f, 0x84576a1bb416a7dd},
+ {0xf0fdf2d3f3c30b9f, 0x656d44a2a11c51d5},
+ {0x969eb7c47859e743, 0x9f644ae5a4b1b325},
+ {0xbc4665b596706114, 0x873d5d9f0dde1fee},
+ {0xeb57ff22fc0c7959, 0xa90cb506d155a7ea},
+ {0x9316ff75dd87cbd8, 0x09a7f12442d588f2},
+ {0xb7dcbf5354e9bece, 0x0c11ed6d538aeb2f},
+ {0xe5d3ef282a242e81, 0x8f1668c8a86da5fa},
+ {0x8fa475791a569d10, 0xf96e017d694487bc},
+ {0xb38d92d760ec4455, 0x37c981dcc395a9ac},
+ {0xe070f78d3927556a, 0x85bbe253f47b1417},
+ {0x8c469ab843b89562, 0x93956d7478ccec8e},
+ {0xaf58416654a6babb, 0x387ac8d1970027b2},
+ {0xdb2e51bfe9d0696a, 0x06997b05fcc0319e},
+ {0x88fcf317f22241e2, 0x441fece3bdf81f03},
+ {0xab3c2fddeeaad25a, 0xd527e81cad7626c3},
+ {0xd60b3bd56a5586f1, 0x8a71e223d8d3b074},
+ {0x85c7056562757456, 0xf6872d5667844e49},
+ {0xa738c6bebb12d16c, 0xb428f8ac016561db},
+ {0xd106f86e69d785c7, 0xe13336d701beba52},
+ {0x82a45b450226b39c, 0xecc0024661173473},
+ {0xa34d721642b06084, 0x27f002d7f95d0190},
+ {0xcc20ce9bd35c78a5, 0x31ec038df7b441f4},
+ {0xff290242c83396ce, 0x7e67047175a15271},
+ {0x9f79a169bd203e41, 0x0f0062c6e984d386},
+ {0xc75809c42c684dd1, 0x52c07b78a3e60868},
+ {0xf92e0c3537826145, 0xa7709a56ccdf8a82},
+ {0x9bbcc7a142b17ccb, 0x88a66076400bb691},
+ {0xc2abf989935ddbfe, 0x6acff893d00ea435},
+ {0xf356f7ebf83552fe, 0x0583f6b8c4124d43},
+ {0x98165af37b2153de, 0xc3727a337a8b704a},
+ {0xbe1bf1b059e9a8d6, 0x744f18c0592e4c5c},
+ {0xeda2ee1c7064130c, 0x1162def06f79df73},
+ {0x9485d4d1c63e8be7, 0x8addcb5645ac2ba8},
+ {0xb9a74a0637ce2ee1, 0x6d953e2bd7173692},
+ {0xe8111c87c5c1ba99, 0xc8fa8db6ccdd0437},
+ {0x910ab1d4db9914a0, 0x1d9c9892400a22a2},
+ {0xb54d5e4a127f59c8, 0x2503beb6d00cab4b},
+ {0xe2a0b5dc971f303a, 0x2e44ae64840fd61d},
+ {0x8da471a9de737e24, 0x5ceaecfed289e5d2},
+ {0xb10d8e1456105dad, 0x7425a83e872c5f47},
+ {0xdd50f1996b947518, 0xd12f124e28f77719},
+ {0x8a5296ffe33cc92f, 0x82bd6b70d99aaa6f},
+ {0xace73cbfdc0bfb7b, 0x636cc64d1001550b},
+ {0xd8210befd30efa5a, 0x3c47f7e05401aa4e},
+ {0x8714a775e3e95c78, 0x65acfaec34810a71},
+ {0xa8d9d1535ce3b396, 0x7f1839a741a14d0d},
+ {0xd31045a8341ca07c, 0x1ede48111209a050},
+ {0x83ea2b892091e44d, 0x934aed0aab460432},
+ {0xa4e4b66b68b65d60, 0xf81da84d5617853f},
+ {0xce1de40642e3f4b9, 0x36251260ab9d668e},
+ {0x80d2ae83e9ce78f3, 0xc1d72b7c6b426019},
+ {0xa1075a24e4421730, 0xb24cf65b8612f81f},
+ {0xc94930ae1d529cfc, 0xdee033f26797b627},
+ {0xfb9b7cd9a4a7443c, 0x169840ef017da3b1},
+ {0x9d412e0806e88aa5, 0x8e1f289560ee864e},
+ {0xc491798a08a2ad4e, 0xf1a6f2bab92a27e2},
+ {0xf5b5d7ec8acb58a2, 0xae10af696774b1db},
+ {0x9991a6f3d6bf1765, 0xacca6da1e0a8ef29},
+ {0xbff610b0cc6edd3f, 0x17fd090a58d32af3},
+ {0xeff394dcff8a948e, 0xddfc4b4cef07f5b0},
+ {0x95f83d0a1fb69cd9, 0x4abdaf101564f98e},
+ {0xbb764c4ca7a4440f, 0x9d6d1ad41abe37f1},
+ {0xea53df5fd18d5513, 0x84c86189216dc5ed},
+ {0x92746b9be2f8552c, 0x32fd3cf5b4e49bb4},
+ {0xb7118682dbb66a77, 0x3fbc8c33221dc2a1},
+ {0xe4d5e82392a40515, 0x0fabaf3feaa5334a},
+ {0x8f05b1163ba6832d, 0x29cb4d87f2a7400e},
+ {0xb2c71d5bca9023f8, 0x743e20e9ef511012},
+ {0xdf78e4b2bd342cf6, 0x914da9246b255416},
+ {0x8bab8eefb6409c1a, 0x1ad089b6c2f7548e},
+ {0xae9672aba3d0c320, 0xa184ac2473b529b1},
+ {0xda3c0f568cc4f3e8, 0xc9e5d72d90a2741e},
+ {0x8865899617fb1871, 0x7e2fa67c7a658892},
+ {0xaa7eebfb9df9de8d, 0xddbb901b98feeab7},
+ {0xd51ea6fa85785631, 0x552a74227f3ea565},
+ {0x8533285c936b35de, 0xd53a88958f87275f},
+ {0xa67ff273b8460356, 0x8a892abaf368f137},
+ {0xd01fef10a657842c, 0x2d2b7569b0432d85},
+ {0x8213f56a67f6b29b, 0x9c3b29620e29fc73},
+ {0xa298f2c501f45f42, 0x8349f3ba91b47b8f},
+ {0xcb3f2f7642717713, 0x241c70a936219a73},
+ {0xfe0efb53d30dd4d7, 0xed238cd383aa0110},
+ {0x9ec95d1463e8a506, 0xf4363804324a40aa},
+ {0xc67bb4597ce2ce48, 0xb143c6053edcd0d5},
+ {0xf81aa16fdc1b81da, 0xdd94b7868e94050a},
+ {0x9b10a4e5e9913128, 0xca7cf2b4191c8326},
+ {0xc1d4ce1f63f57d72, 0xfd1c2f611f63a3f0},
+ {0xf24a01a73cf2dccf, 0xbc633b39673c8cec},
+ {0x976e41088617ca01, 0xd5be0503e085d813},
+ {0xbd49d14aa79dbc82, 0x4b2d8644d8a74e18},
+ {0xec9c459d51852ba2, 0xddf8e7d60ed1219e},
+ {0x93e1ab8252f33b45, 0xcabb90e5c942b503},
+ {0xb8da1662e7b00a17, 0x3d6a751f3b936243},
+ {0xe7109bfba19c0c9d, 0x0cc512670a783ad4},
+ {0x906a617d450187e2, 0x27fb2b80668b24c5},
+ {0xb484f9dc9641e9da, 0xb1f9f660802dedf6},
+ {0xe1a63853bbd26451, 0x5e7873f8a0396973},
+ {0x8d07e33455637eb2, 0xdb0b487b6423e1e8},
+ {0xb049dc016abc5e5f, 0x91ce1a9a3d2cda62},
+ {0xdc5c5301c56b75f7, 0x7641a140cc7810fb},
+ {0x89b9b3e11b6329ba, 0xa9e904c87fcb0a9d},
+ {0xac2820d9623bf429, 0x546345fa9fbdcd44},
+ {0xd732290fbacaf133, 0xa97c177947ad4095},
+ {0x867f59a9d4bed6c0, 0x49ed8eabcccc485d},
+ {0xa81f301449ee8c70, 0x5c68f256bfff5a74},
+ {0xd226fc195c6a2f8c, 0x73832eec6fff3111},
+ {0x83585d8fd9c25db7, 0xc831fd53c5ff7eab},
+ {0xa42e74f3d032f525, 0xba3e7ca8b77f5e55},
+ {0xcd3a1230c43fb26f, 0x28ce1bd2e55f35eb},
+ {0x80444b5e7aa7cf85, 0x7980d163cf5b81b3},
+ {0xa0555e361951c366, 0xd7e105bcc332621f},
+ {0xc86ab5c39fa63440, 0x8dd9472bf3fefaa7},
+ {0xfa856334878fc150, 0xb14f98f6f0feb951},
+ {0x9c935e00d4b9d8d2, 0x6ed1bf9a569f33d3},
+ {0xc3b8358109e84f07, 0x0a862f80ec4700c8},
+ {0xf4a642e14c6262c8, 0xcd27bb612758c0fa},
+ {0x98e7e9cccfbd7dbd, 0x8038d51cb897789c},
+ {0xbf21e44003acdd2c, 0xe0470a63e6bd56c3},
+ {0xeeea5d5004981478, 0x1858ccfce06cac74},
+ {0x95527a5202df0ccb, 0x0f37801e0c43ebc8},
+ {0xbaa718e68396cffd, 0xd30560258f54e6ba},
+ {0xe950df20247c83fd, 0x47c6b82ef32a2069},
+ {0x91d28b7416cdd27e, 0x4cdc331d57fa5441},
+ {0xb6472e511c81471d, 0xe0133fe4adf8e952},
+ {0xe3d8f9e563a198e5, 0x58180fddd97723a6},
+ {0x8e679c2f5e44ff8f, 0x570f09eaa7ea7648},
+ {0xb201833b35d63f73, 0x2cd2cc6551e513da},
+ {0xde81e40a034bcf4f, 0xf8077f7ea65e58d1},
+ {0x8b112e86420f6191, 0xfb04afaf27faf782},
+ {0xadd57a27d29339f6, 0x79c5db9af1f9b563},
+ {0xd94ad8b1c7380874, 0x18375281ae7822bc},
+ {0x87cec76f1c830548, 0x8f2293910d0b15b5},
+ {0xa9c2794ae3a3c69a, 0xb2eb3875504ddb22},
+ {0xd433179d9c8cb841, 0x5fa60692a46151eb},
+ {0x849feec281d7f328, 0xdbc7c41ba6bcd333},
+ {0xa5c7ea73224deff3, 0x12b9b522906c0800},
+ {0xcf39e50feae16bef, 0xd768226b34870a00},
+ {0x81842f29f2cce375, 0xe6a1158300d46640},
+ {0xa1e53af46f801c53, 0x60495ae3c1097fd0},
+ {0xca5e89b18b602368, 0x385bb19cb14bdfc4},
+ {0xfcf62c1dee382c42, 0x46729e03dd9ed7b5},
+ {0x9e19db92b4e31ba9, 0x6c07a2c26a8346d1},
+ {0xc5a05277621be293, 0xc7098b7305241885},
+ { 0xf70867153aa2db38,
+ 0xb8cbee4fc66d1ea7 }
+#else
+ {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b},
+ {0xce5d73ff402d98e3, 0xfb0a3d212dc81290},
+ {0xa6b34ad8c9dfc06f, 0xf42faa48c0ea481f},
+ {0x86a8d39ef77164bc, 0xae5dff9c02033198},
+ {0xd98ddaee19068c76, 0x3badd624dd9b0958},
+ {0xafbd2350644eeacf, 0xe5d1929ef90898fb},
+ {0x8df5efabc5979c8f, 0xca8d3ffa1ef463c2},
+ {0xe55990879ddcaabd, 0xcc420a6a101d0516},
+ {0xb94470938fa89bce, 0xf808e40e8d5b3e6a},
+ {0x95a8637627989aad, 0xdde7001379a44aa9},
+ {0xf1c90080baf72cb1, 0x5324c68b12dd6339},
+ {0xc350000000000000, 0x0000000000000000},
+ {0x9dc5ada82b70b59d, 0xf020000000000000},
+ {0xfee50b7025c36a08, 0x02f236d04753d5b4},
+ {0xcde6fd5e09abcf26, 0xed4c0226b55e6f86},
+ {0xa6539930bf6bff45, 0x84db8346b786151c},
+ {0x865b86925b9bc5c2, 0x0b8a2392ba45a9b2},
+ {0xd910f7ff28069da4, 0x1b2ba1518094da04},
+ {0xaf58416654a6babb, 0x387ac8d1970027b2},
+ {0x8da471a9de737e24, 0x5ceaecfed289e5d2},
+ {0xe4d5e82392a40515, 0x0fabaf3feaa5334a},
+ {0xb8da1662e7b00a17, 0x3d6a751f3b936243},
+ { 0x95527a5202df0ccb,
+ 0x0f37801e0c43ebc8 }
+#endif
+ };
+
+#if FMT_USE_FULL_CACHE_DRAGONBOX
+ return pow10_significands[k - float_info<double>::min_k];
+#else
+ static constexpr const uint64_t powers_of_5_64[] = {
+ 0x0000000000000001, 0x0000000000000005, 0x0000000000000019,
+ 0x000000000000007d, 0x0000000000000271, 0x0000000000000c35,
+ 0x0000000000003d09, 0x000000000001312d, 0x000000000005f5e1,
+ 0x00000000001dcd65, 0x00000000009502f9, 0x0000000002e90edd,
+ 0x000000000e8d4a51, 0x0000000048c27395, 0x000000016bcc41e9,
+ 0x000000071afd498d, 0x0000002386f26fc1, 0x000000b1a2bc2ec5,
+ 0x000003782dace9d9, 0x00001158e460913d, 0x000056bc75e2d631,
+ 0x0001b1ae4d6e2ef5, 0x000878678326eac9, 0x002a5a058fc295ed,
+ 0x00d3c21bcecceda1, 0x0422ca8b0a00a425, 0x14adf4b7320334b9};
+
+ static constexpr const uint32_t pow10_recovery_errors[] = {
+ 0x50001400, 0x54044100, 0x54014555, 0x55954415, 0x54115555, 0x00000001,
+ 0x50000000, 0x00104000, 0x54010004, 0x05004001, 0x55555544, 0x41545555,
+ 0x54040551, 0x15445545, 0x51555514, 0x10000015, 0x00101100, 0x01100015,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x04450514, 0x45414110,
+ 0x55555145, 0x50544050, 0x15040155, 0x11054140, 0x50111514, 0x11451454,
+ 0x00400541, 0x00000000, 0x55555450, 0x10056551, 0x10054011, 0x55551014,
+ 0x69514555, 0x05151109, 0x00155555};
+
+ static const int compression_ratio = 27;
+
+ // Compute base index.
+ int cache_index = (k - float_info<double>::min_k) / compression_ratio;
+ int kb = cache_index * compression_ratio + float_info<double>::min_k;
+ int offset = k - kb;
+
+ // Get base cache.
+ uint128_wrapper base_cache = pow10_significands[cache_index];
+ if (offset == 0) return base_cache;
+
+ // Compute the required amount of bit-shift.
+ int alpha = floor_log2_pow10(kb + offset) - floor_log2_pow10(kb) - offset;
+ FMT_ASSERT(alpha > 0 && alpha < 64, "shifting error detected");
+
+ // Try to recover the real cache.
+ uint64_t pow5 = powers_of_5_64[offset];
+ uint128_wrapper recovered_cache = umul128(base_cache.high(), pow5);
+ uint128_wrapper middle_low =
+ umul128(base_cache.low() - (kb < 0 ? 1u : 0u), pow5);
+
+ recovered_cache += middle_low.high();
+
+ uint64_t high_to_middle = recovered_cache.high() << (64 - alpha);
+ uint64_t middle_to_low = recovered_cache.low() << (64 - alpha);
+
+ recovered_cache =
+ uint128_wrapper{(recovered_cache.low() >> alpha) | high_to_middle,
+ ((middle_low.low() >> alpha) | middle_to_low)};
+
+ if (kb < 0) recovered_cache += 1;
+
+ // Get error.
+ int error_idx = (k - float_info<double>::min_k) / 16;
+ uint32_t error = (pow10_recovery_errors[error_idx] >>
+ ((k - float_info<double>::min_k) % 16) * 2) &
+ 0x3;
+
+ // Add the error back.
+ FMT_ASSERT(recovered_cache.low() + error >= recovered_cache.low(), "");
+ return {recovered_cache.high(), recovered_cache.low() + error};
+#endif
+ }
+
+ static carrier_uint compute_mul(carrier_uint u,
+ const cache_entry_type& cache) FMT_NOEXCEPT {
+ return umul192_upper64(u, cache);
+ }
+
+ static uint32_t compute_delta(cache_entry_type const& cache,
+ int beta_minus_1) FMT_NOEXCEPT {
+ return static_cast<uint32_t>(cache.high() >> (64 - 1 - beta_minus_1));
+ }
+
+ static bool compute_mul_parity(carrier_uint two_f,
+ const cache_entry_type& cache,
+ int beta_minus_1) FMT_NOEXCEPT {
+ FMT_ASSERT(beta_minus_1 >= 1, "");
+ FMT_ASSERT(beta_minus_1 < 64, "");
+
+ return ((umul192_middle64(two_f, cache) >> (64 - beta_minus_1)) & 1) != 0;
+ }
+
+ static carrier_uint compute_left_endpoint_for_shorter_interval_case(
+ const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT {
+ return (cache.high() -
+ (cache.high() >> (float_info<double>::significand_bits + 2))) >>
+ (64 - float_info<double>::significand_bits - 1 - beta_minus_1);
+ }
+
+ static carrier_uint compute_right_endpoint_for_shorter_interval_case(
+ const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT {
+ return (cache.high() +
+ (cache.high() >> (float_info<double>::significand_bits + 1))) >>
+ (64 - float_info<double>::significand_bits - 1 - beta_minus_1);
+ }
+
+ static carrier_uint compute_round_up_for_shorter_interval_case(
+ const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT {
+ return ((cache.high() >>
+ (64 - float_info<double>::significand_bits - 2 - beta_minus_1)) +
+ 1) /
+ 2;
+ }
+};
+
+// Various integer checks
+template <class T>
+bool is_left_endpoint_integer_shorter_interval(int exponent) FMT_NOEXCEPT {
+ return exponent >=
+ float_info<
+ T>::case_shorter_interval_left_endpoint_lower_threshold &&
+ exponent <=
+ float_info<T>::case_shorter_interval_left_endpoint_upper_threshold;
+}
+template <class T>
+bool is_endpoint_integer(typename float_info<T>::carrier_uint two_f,
+ int exponent, int minus_k) FMT_NOEXCEPT {
+ if (exponent < float_info<T>::case_fc_pm_half_lower_threshold) return false;
+ // For k >= 0.
+ if (exponent <= float_info<T>::case_fc_pm_half_upper_threshold) return true;
+ // For k < 0.
+ if (exponent > float_info<T>::divisibility_check_by_5_threshold) return false;
+ return divisible_by_power_of_5(two_f, minus_k);
+}
+
+template <class T>
+bool is_center_integer(typename float_info<T>::carrier_uint two_f, int exponent,
+ int minus_k) FMT_NOEXCEPT {
+ // Exponent for 5 is negative.
+ if (exponent > float_info<T>::divisibility_check_by_5_threshold) return false;
+ if (exponent > float_info<T>::case_fc_upper_threshold)
+ return divisible_by_power_of_5(two_f, minus_k);
+ // Both exponents are nonnegative.
+ if (exponent >= float_info<T>::case_fc_lower_threshold) return true;
+ // Exponent for 2 is negative.
+ return divisible_by_power_of_2(two_f, minus_k - exponent + 1);
+}
+
+// Remove trailing zeros from n and return the number of zeros removed (float)
+FMT_INLINE int remove_trailing_zeros(uint32_t& n) FMT_NOEXCEPT {
+#ifdef FMT_BUILTIN_CTZ
+ int t = FMT_BUILTIN_CTZ(n);
+#else
+ int t = ctz(n);
+#endif
+ if (t > float_info<float>::max_trailing_zeros)
+ t = float_info<float>::max_trailing_zeros;
+
+ const uint32_t mod_inv1 = 0xcccccccd;
+ const uint32_t max_quotient1 = 0x33333333;
+ const uint32_t mod_inv2 = 0xc28f5c29;
+ const uint32_t max_quotient2 = 0x0a3d70a3;
+
+ int s = 0;
+ for (; s < t - 1; s += 2) {
+ if (n * mod_inv2 > max_quotient2) break;
+ n *= mod_inv2;
+ }
+ if (s < t && n * mod_inv1 <= max_quotient1) {
+ n *= mod_inv1;
+ ++s;
+ }
+ n >>= s;
+ return s;
+}
+
+// Removes trailing zeros and returns the number of zeros removed (double)
+FMT_INLINE int remove_trailing_zeros(uint64_t& n) FMT_NOEXCEPT {
+#ifdef FMT_BUILTIN_CTZLL
+ int t = FMT_BUILTIN_CTZLL(n);
+#else
+ int t = ctzll(n);
+#endif
+ if (t > float_info<double>::max_trailing_zeros)
+ t = float_info<double>::max_trailing_zeros;
+ // Divide by 10^8 and reduce to 32-bits
+ // Since ret_value.significand <= (2^64 - 1) / 1000 < 10^17,
+ // both of the quotient and the r should fit in 32-bits
+
+ const uint32_t mod_inv1 = 0xcccccccd;
+ const uint32_t max_quotient1 = 0x33333333;
+ const uint64_t mod_inv8 = 0xc767074b22e90e21;
+ const uint64_t max_quotient8 = 0x00002af31dc46118;
+
+ // If the number is divisible by 1'0000'0000, work with the quotient
+ if (t >= 8) {
+ auto quotient_candidate = n * mod_inv8;
+
+ if (quotient_candidate <= max_quotient8) {
+ auto quotient = static_cast<uint32_t>(quotient_candidate >> 8);
+
+ int s = 8;
+ for (; s < t; ++s) {
+ if (quotient * mod_inv1 > max_quotient1) break;
+ quotient *= mod_inv1;
+ }
+ quotient >>= (s - 8);
+ n = quotient;
+ return s;
+ }
+ }
+
+ // Otherwise, work with the remainder
+ auto quotient = static_cast<uint32_t>(n / 100000000);
+ auto remainder = static_cast<uint32_t>(n - 100000000 * quotient);
+
+ if (t == 0 || remainder * mod_inv1 > max_quotient1) {
+ return 0;
+ }
+ remainder *= mod_inv1;
+
+ if (t == 1 || remainder * mod_inv1 > max_quotient1) {
+ n = (remainder >> 1) + quotient * 10000000ull;
+ return 1;
+ }
+ remainder *= mod_inv1;
+
+ if (t == 2 || remainder * mod_inv1 > max_quotient1) {
+ n = (remainder >> 2) + quotient * 1000000ull;
+ return 2;
+ }
+ remainder *= mod_inv1;
+
+ if (t == 3 || remainder * mod_inv1 > max_quotient1) {
+ n = (remainder >> 3) + quotient * 100000ull;
+ return 3;
+ }
+ remainder *= mod_inv1;
+
+ if (t == 4 || remainder * mod_inv1 > max_quotient1) {
+ n = (remainder >> 4) + quotient * 10000ull;
+ return 4;
+ }
+ remainder *= mod_inv1;
+
+ if (t == 5 || remainder * mod_inv1 > max_quotient1) {
+ n = (remainder >> 5) + quotient * 1000ull;
+ return 5;
+ }
+ remainder *= mod_inv1;
+
+ if (t == 6 || remainder * mod_inv1 > max_quotient1) {
+ n = (remainder >> 6) + quotient * 100ull;
+ return 6;
+ }
+ remainder *= mod_inv1;
+
+ n = (remainder >> 7) + quotient * 10ull;
+ return 7;
+}
+
+// The main algorithm for shorter interval case
+template <class T>
+FMT_INLINE decimal_fp<T> shorter_interval_case(int exponent) FMT_NOEXCEPT {
+ decimal_fp<T> ret_value;
+ // Compute k and beta
+ const int minus_k = floor_log10_pow2_minus_log10_4_over_3(exponent);
+ const int beta_minus_1 = exponent + floor_log2_pow10(-minus_k);
+
+ // Compute xi and zi
+ using cache_entry_type = typename cache_accessor<T>::cache_entry_type;
+ const cache_entry_type cache = cache_accessor<T>::get_cached_power(-minus_k);
+
+ auto xi = cache_accessor<T>::compute_left_endpoint_for_shorter_interval_case(
+ cache, beta_minus_1);
+ auto zi = cache_accessor<T>::compute_right_endpoint_for_shorter_interval_case(
+ cache, beta_minus_1);
+
+ // If the left endpoint is not an integer, increase it
+ if (!is_left_endpoint_integer_shorter_interval<T>(exponent)) ++xi;
+
+ // Try bigger divisor
+ ret_value.significand = zi / 10;
+
+ // If succeed, remove trailing zeros if necessary and return
+ if (ret_value.significand * 10 >= xi) {
+ ret_value.exponent = minus_k + 1;
+ ret_value.exponent += remove_trailing_zeros(ret_value.significand);
+ return ret_value;
+ }
+
+ // Otherwise, compute the round-up of y
+ ret_value.significand =
+ cache_accessor<T>::compute_round_up_for_shorter_interval_case(
+ cache, beta_minus_1);
+ ret_value.exponent = minus_k;
+
+ // When tie occurs, choose one of them according to the rule
+ if (exponent >= float_info<T>::shorter_interval_tie_lower_threshold &&
+ exponent <= float_info<T>::shorter_interval_tie_upper_threshold) {
+ ret_value.significand = ret_value.significand % 2 == 0
+ ? ret_value.significand
+ : ret_value.significand - 1;
+ } else if (ret_value.significand < xi) {
+ ++ret_value.significand;
+ }
+ return ret_value;
+}
+
+template <typename T> decimal_fp<T> to_decimal(T x) FMT_NOEXCEPT {
+ // Step 1: integer promotion & Schubfach multiplier calculation.
+
+ using carrier_uint = typename float_info<T>::carrier_uint;
+ using cache_entry_type = typename cache_accessor<T>::cache_entry_type;
+ auto br = bit_cast<carrier_uint>(x);
+
+ // Extract significand bits and exponent bits.
+ const carrier_uint significand_mask =
+ (static_cast<carrier_uint>(1) << float_info<T>::significand_bits) - 1;
+ carrier_uint significand = (br & significand_mask);
+ int exponent = static_cast<int>((br & exponent_mask<T>()) >>
+ float_info<T>::significand_bits);
+
+ if (exponent != 0) { // Check if normal.
+ exponent += float_info<T>::exponent_bias - float_info<T>::significand_bits;
+
+ // Shorter interval case; proceed like Schubfach.
+ if (significand == 0) return shorter_interval_case<T>(exponent);
+
+ significand |=
+ (static_cast<carrier_uint>(1) << float_info<T>::significand_bits);
+ } else {
+ // Subnormal case; the interval is always regular.
+ if (significand == 0) return {0, 0};
+ exponent = float_info<T>::min_exponent - float_info<T>::significand_bits;
+ }
+
+ const bool include_left_endpoint = (significand % 2 == 0);
+ const bool include_right_endpoint = include_left_endpoint;
+
+ // Compute k and beta.
+ const int minus_k = floor_log10_pow2(exponent) - float_info<T>::kappa;
+ const cache_entry_type cache = cache_accessor<T>::get_cached_power(-minus_k);
+ const int beta_minus_1 = exponent + floor_log2_pow10(-minus_k);
+
+ // Compute zi and deltai
+ // 10^kappa <= deltai < 10^(kappa + 1)
+ const uint32_t deltai = cache_accessor<T>::compute_delta(cache, beta_minus_1);
+ const carrier_uint two_fc = significand << 1;
+ const carrier_uint two_fr = two_fc | 1;
+ const carrier_uint zi =
+ cache_accessor<T>::compute_mul(two_fr << beta_minus_1, cache);
+
+ // Step 2: Try larger divisor; remove trailing zeros if necessary
+
+ // Using an upper bound on zi, we might be able to optimize the division
+ // better than the compiler; we are computing zi / big_divisor here
+ decimal_fp<T> ret_value;
+ ret_value.significand = divide_by_10_to_kappa_plus_1(zi);
+ uint32_t r = static_cast<uint32_t>(zi - float_info<T>::big_divisor *
+ ret_value.significand);
+
+ if (r > deltai) {
+ goto small_divisor_case_label;
+ } else if (r < deltai) {
+ // Exclude the right endpoint if necessary
+ if (r == 0 && !include_right_endpoint &&
+ is_endpoint_integer<T>(two_fr, exponent, minus_k)) {
+ --ret_value.significand;
+ r = float_info<T>::big_divisor;
+ goto small_divisor_case_label;
+ }
+ } else {
+ // r == deltai; compare fractional parts
+ // Check conditions in the order different from the paper
+ // to take advantage of short-circuiting
+ const carrier_uint two_fl = two_fc - 1;
+ if ((!include_left_endpoint ||
+ !is_endpoint_integer<T>(two_fl, exponent, minus_k)) &&
+ !cache_accessor<T>::compute_mul_parity(two_fl, cache, beta_minus_1)) {
+ goto small_divisor_case_label;
+ }
+ }
+ ret_value.exponent = minus_k + float_info<T>::kappa + 1;
+
+ // We may need to remove trailing zeros
+ ret_value.exponent += remove_trailing_zeros(ret_value.significand);
+ return ret_value;
+
+ // Step 3: Find the significand with the smaller divisor
+
+small_divisor_case_label:
+ ret_value.significand *= 10;
+ ret_value.exponent = minus_k + float_info<T>::kappa;
+
+ const uint32_t mask = (1u << float_info<T>::kappa) - 1;
+ auto dist = r - (deltai / 2) + (float_info<T>::small_divisor / 2);
+
+ // Is dist divisible by 2^kappa?
+ if ((dist & mask) == 0) {
+ const bool approx_y_parity =
+ ((dist ^ (float_info<T>::small_divisor / 2)) & 1) != 0;
+ dist >>= float_info<T>::kappa;
+
+ // Is dist divisible by 5^kappa?
+ if (check_divisibility_and_divide_by_pow5<float_info<T>::kappa>(dist)) {
+ ret_value.significand += dist;
+
+ // Check z^(f) >= epsilon^(f)
+ // We have either yi == zi - epsiloni or yi == (zi - epsiloni) - 1,
+ // where yi == zi - epsiloni if and only if z^(f) >= epsilon^(f)
+ // Since there are only 2 possibilities, we only need to care about the
+ // parity. Also, zi and r should have the same parity since the divisor
+ // is an even number
+ if (cache_accessor<T>::compute_mul_parity(two_fc, cache, beta_minus_1) !=
+ approx_y_parity) {
+ --ret_value.significand;
+ } else {
+ // If z^(f) >= epsilon^(f), we might have a tie
+ // when z^(f) == epsilon^(f), or equivalently, when y is an integer
+ if (is_center_integer<T>(two_fc, exponent, minus_k)) {
+ ret_value.significand = ret_value.significand % 2 == 0
+ ? ret_value.significand
+ : ret_value.significand - 1;
+ }
+ }
+ }
+ // Is dist not divisible by 5^kappa?
+ else {
+ ret_value.significand += dist;
+ }
+ }
+ // Is dist not divisible by 2^kappa?
+ else {
+ // Since we know dist is small, we might be able to optimize the division
+ // better than the compiler; we are computing dist / small_divisor here
+ ret_value.significand +=
+ small_division_by_pow10<float_info<T>::kappa>(dist);
+ }
+ return ret_value;
+}
+} // namespace dragonbox
+
+// Formats value using a variation of the Fixed-Precision Positive
+// Floating-Point Printout ((FPP)^2) algorithm by Steele & White:
+// https://fmt.dev/papers/p372-steele.pdf.
+template <typename Double>
+void fallback_format(Double d, int num_digits, bool binary32, buffer<char>& buf,
+ int& exp10) {
+ bigint numerator; // 2 * R in (FPP)^2.
+ bigint denominator; // 2 * S in (FPP)^2.
+ // lower and upper are differences between value and corresponding boundaries.
+ bigint lower; // (M^- in (FPP)^2).
+ bigint upper_store; // upper's value if different from lower.
+ bigint* upper = nullptr; // (M^+ in (FPP)^2).
+ fp value;
+ // Shift numerator and denominator by an extra bit or two (if lower boundary
+ // is closer) to make lower and upper integers. This eliminates multiplication
+ // by 2 during later computations.
+ const bool is_predecessor_closer =
+ binary32 ? value.assign(static_cast<float>(d)) : value.assign(d);
+ int shift = is_predecessor_closer ? 2 : 1;
+ uint64_t significand = value.f << shift;
+ if (value.e >= 0) {
+ numerator.assign(significand);
+ numerator <<= value.e;
+ lower.assign(1);
+ lower <<= value.e;
+ if (shift != 1) {
+ upper_store.assign(1);
+ upper_store <<= value.e + 1;
+ upper = &upper_store;
+ }
+ denominator.assign_pow10(exp10);
+ denominator <<= shift;
+ } else if (exp10 < 0) {
+ numerator.assign_pow10(-exp10);
+ lower.assign(numerator);
+ if (shift != 1) {
+ upper_store.assign(numerator);
+ upper_store <<= 1;
+ upper = &upper_store;
+ }
+ numerator *= significand;
+ denominator.assign(1);
+ denominator <<= shift - value.e;
+ } else {
+ numerator.assign(significand);
+ denominator.assign_pow10(exp10);
+ denominator <<= shift - value.e;
+ lower.assign(1);
+ if (shift != 1) {
+ upper_store.assign(1ULL << 1);
+ upper = &upper_store;
+ }
+ }
+ // Invariant: value == (numerator / denominator) * pow(10, exp10).
+ if (num_digits < 0) {
+ // Generate the shortest representation.
+ if (!upper) upper = &lower;
+ bool even = (value.f & 1) == 0;
+ num_digits = 0;
+ char* data = buf.data();
+ for (;;) {
+ int digit = numerator.divmod_assign(denominator);
+ bool low = compare(numerator, lower) - even < 0; // numerator <[=] lower.
+ // numerator + upper >[=] pow10:
+ bool high = add_compare(numerator, *upper, denominator) + even > 0;
+ data[num_digits++] = static_cast<char>('0' + digit);
+ if (low || high) {
+ if (!low) {
+ ++data[num_digits - 1];
+ } else if (high) {
+ int result = add_compare(numerator, numerator, denominator);
+ // Round half to even.
+ if (result > 0 || (result == 0 && (digit % 2) != 0))
+ ++data[num_digits - 1];
+ }
+ buf.try_resize(to_unsigned(num_digits));
+ exp10 -= num_digits - 1;
+ return;
+ }
+ numerator *= 10;
+ lower *= 10;
+ if (upper != &lower) *upper *= 10;
+ }
+ }
+ // Generate the given number of digits.
+ exp10 -= num_digits - 1;
+ if (num_digits == 0) {
+ buf.try_resize(1);
+ denominator *= 10;
+ buf[0] = add_compare(numerator, numerator, denominator) > 0 ? '1' : '0';
+ return;
+ }
+ buf.try_resize(to_unsigned(num_digits));
+ for (int i = 0; i < num_digits - 1; ++i) {
+ int digit = numerator.divmod_assign(denominator);
+ buf[i] = static_cast<char>('0' + digit);
+ numerator *= 10;
+ }
+ int digit = numerator.divmod_assign(denominator);
+ auto result = add_compare(numerator, numerator, denominator);
+ if (result > 0 || (result == 0 && (digit % 2) != 0)) {
+ if (digit == 9) {
+ const auto overflow = '0' + 10;
+ buf[num_digits - 1] = overflow;
+ // Propagate the carry.
+ for (int i = num_digits - 1; i > 0 && buf[i] == overflow; --i) {
+ buf[i] = '0';
+ ++buf[i - 1];
+ }
+ if (buf[0] == overflow) {
+ buf[0] = '1';
+ ++exp10;
+ }
+ return;
+ }
+ ++digit;
+ }
+ buf[num_digits - 1] = static_cast<char>('0' + digit);
+}
+
+template <typename T>
+int format_float(T value, int precision, float_specs specs, buffer<char>& buf) {
+ static_assert(!std::is_same<T, float>::value, "");
+ FMT_ASSERT(value >= 0, "value is negative");
+
+ const bool fixed = specs.format == float_format::fixed;
+ if (value <= 0) { // <= instead of == to silence a warning.
+ if (precision <= 0 || !fixed) {
+ buf.push_back('0');
+ return 0;
+ }
+ buf.try_resize(to_unsigned(precision));
+ std::uninitialized_fill_n(buf.data(), precision, '0');
+ return -precision;
+ }
+
+ if (!specs.use_grisu) return snprintf_float(value, precision, specs, buf);
+
+ if (precision < 0) {
+ // Use Dragonbox for the shortest format.
+ if (specs.binary32) {
+ auto dec = dragonbox::to_decimal(static_cast<float>(value));
+ write<char>(buffer_appender<char>(buf), dec.significand);
+ return dec.exponent;
+ }
+ auto dec = dragonbox::to_decimal(static_cast<double>(value));
+ write<char>(buffer_appender<char>(buf), dec.significand);
+ return dec.exponent;
+ }
+
+ // Use Grisu + Dragon4 for the given precision:
+ // https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf.
+ int exp = 0;
+ const int min_exp = -60; // alpha in Grisu.
+ int cached_exp10 = 0; // K in Grisu.
+ fp normalized = normalize(fp(value));
+ const auto cached_pow = get_cached_power(
+ min_exp - (normalized.e + fp::significand_size), cached_exp10);
+ normalized = normalized * cached_pow;
+ // Limit precision to the maximum possible number of significant digits in an
+ // IEEE754 double because we don't need to generate zeros.
+ const int max_double_digits = 767;
+ if (precision > max_double_digits) precision = max_double_digits;
+ fixed_handler handler{buf.data(), 0, precision, -cached_exp10, fixed};
+ if (grisu_gen_digits(normalized, 1, exp, handler) == digits::error) {
+ exp += handler.size - cached_exp10 - 1;
+ fallback_format(value, handler.precision, specs.binary32, buf, exp);
+ } else {
+ exp += handler.exp10;
+ buf.try_resize(to_unsigned(handler.size));
+ }
+ if (!fixed && !specs.showpoint) {
+ // Remove trailing zeros.
+ auto num_digits = buf.size();
+ while (num_digits > 0 && buf[num_digits - 1] == '0') {
+ --num_digits;
+ ++exp;
+ }
+ buf.try_resize(num_digits);
+ }
+ return exp;
+} // namespace detail
+
+template <typename T>
+int snprintf_float(T value, int precision, float_specs specs,
+ buffer<char>& buf) {
+ // Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail.
+ FMT_ASSERT(buf.capacity() > buf.size(), "empty buffer");
+ static_assert(!std::is_same<T, float>::value, "");
+
+ // Subtract 1 to account for the difference in precision since we use %e for
+ // both general and exponent format.
+ if (specs.format == float_format::general ||
+ specs.format == float_format::exp)
+ precision = (precision >= 0 ? precision : 6) - 1;
+
+ // Build the format string.
+ enum { max_format_size = 7 }; // The longest format is "%#.*Le".
+ char format[max_format_size];
+ char* format_ptr = format;
+ *format_ptr++ = '%';
+ if (specs.showpoint && specs.format == float_format::hex) *format_ptr++ = '#';
+ if (precision >= 0) {
+ *format_ptr++ = '.';
+ *format_ptr++ = '*';
+ }
+ if (std::is_same<T, long double>()) *format_ptr++ = 'L';
+ *format_ptr++ = specs.format != float_format::hex
+ ? (specs.format == float_format::fixed ? 'f' : 'e')
+ : (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;
+#ifdef FMT_FUZZ
+ if (precision > 100000)
+ throw std::runtime_error(
+ "fuzz mode - avoid large allocation inside snprintf");
+#endif
+ // 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_reserve(size + offset + 1); // Add 1 for the terminating '\0'.
+ continue;
+ }
+ auto is_digit = [](char c) { return c >= '0' && c <= '9'; };
+ if (specs.format == float_format::fixed) {
+ if (precision == 0) {
+ buf.try_resize(size);
+ return 0;
+ }
+ // Find and remove the decimal point.
+ auto end = begin + size, p = end;
+ do {
+ --p;
+ } while (is_digit(*p));
+ int fraction_size = static_cast<int>(end - p - 1);
+ std::memmove(p, p + 1, to_unsigned(fraction_size));
+ buf.try_resize(size - 1);
+ return -fraction_size;
+ }
+ if (specs.format == float_format::hex) {
+ buf.try_resize(size + offset);
+ return 0;
+ }
+ // Find and parse the exponent.
+ auto end = begin + size, exp_pos = end;
+ do {
+ --exp_pos;
+ } while (*exp_pos != 'e');
+ char sign = exp_pos[1];
+ FMT_ASSERT(sign == '+' || sign == '-', "");
+ int exp = 0;
+ auto p = exp_pos + 2; // Skip 'e' and sign.
+ do {
+ FMT_ASSERT(is_digit(*p), "");
+ exp = exp * 10 + (*p++ - '0');
+ } while (p != end);
+ if (sign == '-') exp = -exp;
+ int fraction_size = 0;
+ if (exp_pos != begin + 1) {
+ // Remove trailing zeros.
+ auto fraction_end = exp_pos - 1;
+ while (*fraction_end == '0') --fraction_end;
+ // Move the fractional part left to get rid of the decimal point.
+ fraction_size = static_cast<int>(fraction_end - begin - 1);
+ std::memmove(begin + 1, begin + 2, to_unsigned(fraction_size));
+ }
+ buf.try_resize(to_unsigned(fraction_size) + offset + 1);
+ return exp - fraction_size;
+ }
+}
+} // namespace detail
+
+template <> struct formatter<detail::bigint> {
+ FMT_CONSTEXPR format_parse_context::iterator parse(
+ format_parse_context& ctx) {
+ return ctx.begin();
+ }
+
+ format_context::iterator format(const detail::bigint& n,
+ format_context& ctx) {
+ auto out = ctx.out();
+ bool first = true;
+ for (auto i = n.bigits_.size(); i > 0; --i) {
+ auto value = n.bigits_[i - 1u];
+ if (first) {
+ out = format_to(out, FMT_STRING("{:x}"), value);
+ first = false;
+ continue;
+ }
+ out = format_to(out, FMT_STRING("{:08x}"), value);
+ }
+ if (n.exp_ > 0)
+ out = format_to(out, FMT_STRING("p{}"),
+ n.exp_ * detail::bigint::bigit_bits);
+ return out;
+ }
+};
+
+FMT_FUNC detail::utf8_to_utf16::utf8_to_utf16(string_view s) {
+ for_each_codepoint(s, [this](uint32_t cp, int error) {
+ if (error != 0) FMT_THROW(std::runtime_error("invalid utf8"));
+ if (cp <= 0xFFFF) {
+ buffer_.push_back(static_cast<wchar_t>(cp));
+ } else {
+ cp -= 0x10000;
+ buffer_.push_back(static_cast<wchar_t>(0xD800 + (cp >> 10)));
+ buffer_.push_back(static_cast<wchar_t>(0xDC00 + (cp & 0x3FF)));
+ }
+ });
+ buffer_.push_back(0);
+}
+
+FMT_FUNC void format_system_error(detail::buffer<char>& out, int error_code,
+ const char* message) FMT_NOEXCEPT {
+ FMT_TRY {
+ auto ec = std::error_code(error_code, std::generic_category());
+ write(std::back_inserter(out), std::system_error(ec, message).what());
+ return;
+ }
+ FMT_CATCH(...) {}
+ format_error_code(out, error_code, message);
+}
+
+FMT_FUNC void detail::error_handler::on_error(const char* message) {
+ FMT_THROW(format_error(message));
+}
+
+FMT_FUNC void report_system_error(int error_code,
+ const char* message) FMT_NOEXCEPT {
+ report_error(format_system_error, error_code, message);
+}
+
+FMT_FUNC std::string vformat(string_view fmt, format_args args) {
+ // Don't optimize the "{}" case to keep the binary size small and because it
+ // can be better optimized in fmt::format anyway.
+ auto buffer = memory_buffer();
+ detail::vformat_to(buffer, fmt, args);
+ return to_string(buffer);
+}
+
+#ifdef _WIN32
+namespace detail {
+using dword = conditional_t<sizeof(long) == 4, unsigned long, unsigned>;
+extern "C" __declspec(dllimport) int __stdcall WriteConsoleW( //
+ void*, const void*, dword, dword*, void*);
+} // namespace detail
+#endif
+
+namespace detail {
+FMT_FUNC void print(std::FILE* f, string_view text) {
+#ifdef _WIN32
+ 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;
+ }
+ // Fallback to fwrite on failure. It can happen if the output has been
+ // redirected to NUL.
+ }
+#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()});
+}
+
+#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,
+ 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_END_NAMESPACE
+
+#endif // FMT_FORMAT_INL_H_
diff --git a/wpiutil/src/main/native/fmtlib/include/fmt/format.h b/wpiutil/src/main/native/fmtlib/include/fmt/format.h
new file mode 100644
index 0000000..5398a23
--- /dev/null
+++ b/wpiutil/src/main/native/fmtlib/include/fmt/format.h
@@ -0,0 +1,2830 @@
+/*
+ Formatting library for C++
+
+ Copyright (c) 2012 - present, Victor Zverovich
+
+ 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.
+
+ --- Optional exception to the license ---
+
+ As an exception, if, as a result of your compiling your source code, portions
+ of this Software are embedded into a machine-executable object form of such
+ source code, you may redistribute such embedded portions in such object form
+ without including the above copyright and permission notices.
+ */
+
+#ifndef FMT_FORMAT_H_
+#define FMT_FORMAT_H_
+
+#include <cmath> // std::signbit
+#include <cstdint> // uint32_t
+#include <limits> // std::numeric_limits
+#include <memory> // std::uninitialized_copy
+#include <stdexcept> // std::runtime_error
+#include <system_error> // std::system_error
+#include <utility> // std::swap
+
+#include "core.h"
+
+#ifdef __INTEL_COMPILER
+# define FMT_ICC_VERSION __INTEL_COMPILER
+#elif defined(__ICL)
+# define FMT_ICC_VERSION __ICL
+#else
+# define FMT_ICC_VERSION 0
+#endif
+
+#ifdef __NVCC__
+# define FMT_CUDA_VERSION (__CUDACC_VER_MAJOR__ * 100 + __CUDACC_VER_MINOR__)
+#else
+# define FMT_CUDA_VERSION 0
+#endif
+
+#ifdef __has_builtin
+# define FMT_HAS_BUILTIN(x) __has_builtin(x)
+#else
+# define FMT_HAS_BUILTIN(x) 0
+#endif
+
+#if FMT_GCC_VERSION || FMT_CLANG_VERSION
+# define FMT_NOINLINE __attribute__((noinline))
+#else
+# define FMT_NOINLINE
+#endif
+
+#if FMT_MSC_VER
+# define FMT_MSC_DEFAULT = default
+#else
+# define FMT_MSC_DEFAULT
+#endif
+
+#ifndef FMT_THROW
+# if FMT_EXCEPTIONS
+# if FMT_MSC_VER || FMT_NVCC
+FMT_BEGIN_NAMESPACE
+namespace detail {
+template <typename Exception> inline void do_throw(const Exception& x) {
+ // Silence unreachable code warnings in MSVC and NVCC because these
+ // are nearly impossible to fix in a generic code.
+ volatile bool b = true;
+ if (b) throw x;
+}
+} // namespace detail
+FMT_END_NAMESPACE
+# define FMT_THROW(x) detail::do_throw(x)
+# else
+# define FMT_THROW(x) throw x
+# endif
+# else
+# define FMT_THROW(x) \
+ do { \
+ FMT_ASSERT(false, (x).what()); \
+ } while (false)
+# endif
+#endif
+
+#if FMT_EXCEPTIONS
+# define FMT_TRY try
+# define FMT_CATCH(x) catch (x)
+#else
+# define FMT_TRY if (true)
+# define FMT_CATCH(x) if (false)
+#endif
+
+#ifndef FMT_DEPRECATED
+# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VER >= 1900
+# define FMT_DEPRECATED [[deprecated]]
+# else
+# if (defined(__GNUC__) && !defined(__LCC__)) || defined(__clang__)
+# define FMT_DEPRECATED __attribute__((deprecated))
+# elif FMT_MSC_VER
+# define FMT_DEPRECATED __declspec(deprecated)
+# else
+# define FMT_DEPRECATED /* deprecated */
+# endif
+# endif
+#endif
+
+// Workaround broken [[deprecated]] in the Intel, PGI and NVCC compilers.
+#if FMT_ICC_VERSION || defined(__PGI) || FMT_NVCC
+# define FMT_DEPRECATED_ALIAS
+#else
+# define FMT_DEPRECATED_ALIAS FMT_DEPRECATED
+#endif
+
+#ifndef FMT_USE_USER_DEFINED_LITERALS
+// EDG based compilers (Intel, NVIDIA, Elbrus, etc), GCC and MSVC support UDLs.
+# if (FMT_HAS_FEATURE(cxx_user_literals) || FMT_GCC_VERSION >= 407 || \
+ FMT_MSC_VER >= 1900) && \
+ (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= /* UDL feature */ 480)
+# define FMT_USE_USER_DEFINED_LITERALS 1
+# else
+# define FMT_USE_USER_DEFINED_LITERALS 0
+# endif
+#endif
+
+// Defining FMT_REDUCE_INT_INSTANTIATIONS to 1, will reduce the number of
+// integer formatter template instantiations to just one by only using the
+// largest integer type. This results in a reduction in binary size but will
+// cause a decrease in integer formatting performance.
+#if !defined(FMT_REDUCE_INT_INSTANTIATIONS)
+# define FMT_REDUCE_INT_INSTANTIATIONS 0
+#endif
+
+// __builtin_clz is broken in clang with Microsoft CodeGen:
+// https://github.com/fmtlib/fmt/issues/519
+#if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_clz)) && !FMT_MSC_VER
+# define FMT_BUILTIN_CLZ(n) __builtin_clz(n)
+#endif
+#if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_clzll)) && !FMT_MSC_VER
+# define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n)
+#endif
+#if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_ctz))
+# define FMT_BUILTIN_CTZ(n) __builtin_ctz(n)
+#endif
+#if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_ctzll))
+# define FMT_BUILTIN_CTZLL(n) __builtin_ctzll(n)
+#endif
+
+#if FMT_MSC_VER
+# include <intrin.h> // _BitScanReverse[64], _BitScanForward[64], _umul128
+#endif
+
+// Some compilers masquerade as both MSVC and GCC-likes or otherwise support
+// __builtin_clz and __builtin_clzll, so only define FMT_BUILTIN_CLZ using the
+// MSVC intrinsics if the clz and clzll builtins are not available.
+#if FMT_MSC_VER && !defined(FMT_BUILTIN_CLZLL) && !defined(FMT_BUILTIN_CTZLL)
+FMT_BEGIN_NAMESPACE
+namespace detail {
+// Avoid Clang with Microsoft CodeGen's -Wunknown-pragmas warning.
+# if !defined(__clang__)
+# pragma managed(push, off)
+# pragma intrinsic(_BitScanForward)
+# pragma intrinsic(_BitScanReverse)
+# if defined(_WIN64)
+# pragma intrinsic(_BitScanForward64)
+# pragma intrinsic(_BitScanReverse64)
+# endif
+# endif
+
+inline auto clz(uint32_t x) -> int {
+ unsigned long r = 0;
+ _BitScanReverse(&r, x);
+ FMT_ASSERT(x != 0, "");
+ // Static analysis complains about using uninitialized data
+ // "r", but the only way that can happen is if "x" is 0,
+ // which the callers guarantee to not happen.
+ FMT_MSC_WARNING(suppress : 6102)
+ return 31 ^ static_cast<int>(r);
+}
+# define FMT_BUILTIN_CLZ(n) detail::clz(n)
+
+inline auto clzll(uint64_t x) -> int {
+ unsigned long r = 0;
+# ifdef _WIN64
+ _BitScanReverse64(&r, x);
+# else
+ // Scan the high 32 bits.
+ if (_BitScanReverse(&r, static_cast<uint32_t>(x >> 32))) return 63 ^ (r + 32);
+ // Scan the low 32 bits.
+ _BitScanReverse(&r, static_cast<uint32_t>(x));
+# endif
+ FMT_ASSERT(x != 0, "");
+ FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning.
+ return 63 ^ static_cast<int>(r);
+}
+# define FMT_BUILTIN_CLZLL(n) detail::clzll(n)
+
+inline auto ctz(uint32_t x) -> int {
+ unsigned long r = 0;
+ _BitScanForward(&r, x);
+ FMT_ASSERT(x != 0, "");
+ FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning.
+ return static_cast<int>(r);
+}
+# define FMT_BUILTIN_CTZ(n) detail::ctz(n)
+
+inline auto ctzll(uint64_t x) -> int {
+ unsigned long r = 0;
+ FMT_ASSERT(x != 0, "");
+ FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning.
+# ifdef _WIN64
+ _BitScanForward64(&r, x);
+# else
+ // Scan the low 32 bits.
+ if (_BitScanForward(&r, static_cast<uint32_t>(x))) return static_cast<int>(r);
+ // Scan the high 32 bits.
+ _BitScanForward(&r, static_cast<uint32_t>(x >> 32));
+ r += 32;
+# endif
+ return static_cast<int>(r);
+}
+# define FMT_BUILTIN_CTZLL(n) detail::ctzll(n)
+# if !defined(__clang__)
+# pragma managed(pop)
+# endif
+} // namespace detail
+FMT_END_NAMESPACE
+#endif
+
+FMT_BEGIN_NAMESPACE
+namespace detail {
+
+#if __cplusplus >= 202002L || \
+ (__cplusplus >= 201709L && FMT_GCC_VERSION >= 1002)
+# define FMT_CONSTEXPR20 constexpr
+#else
+# define FMT_CONSTEXPR20
+#endif
+
+// An equivalent of `*reinterpret_cast<Dest*>(&source)` that doesn't have
+// undefined behavior (e.g. due to type aliasing).
+// Example: uint64_t d = bit_cast<uint64_t>(2.718);
+template <typename Dest, typename Source>
+inline auto bit_cast(const Source& source) -> Dest {
+ static_assert(sizeof(Dest) == sizeof(Source), "size mismatch");
+ Dest dest;
+ std::memcpy(&dest, &source, sizeof(dest));
+ return dest;
+}
+
+inline auto is_big_endian() -> bool {
+ const auto u = 1u;
+ struct bytes {
+ char data[sizeof(u)];
+ };
+ return bit_cast<bytes>(u).data[0] == 0;
+}
+
+// A fallback implementation of uintptr_t for systems that lack it.
+struct fallback_uintptr {
+ unsigned char value[sizeof(void*)];
+
+ fallback_uintptr() = default;
+ explicit fallback_uintptr(const void* p) {
+ *this = bit_cast<fallback_uintptr>(p);
+ if (is_big_endian()) {
+ for (size_t i = 0, j = sizeof(void*) - 1; i < j; ++i, --j)
+ std::swap(value[i], value[j]);
+ }
+ }
+};
+#ifdef UINTPTR_MAX
+using uintptr_t = ::uintptr_t;
+inline auto to_uintptr(const void* p) -> uintptr_t {
+ return bit_cast<uintptr_t>(p);
+}
+#else
+using uintptr_t = fallback_uintptr;
+inline auto to_uintptr(const void* p) -> fallback_uintptr {
+ return fallback_uintptr(p);
+}
+#endif
+
+// Returns the largest possible value for type T. Same as
+// std::numeric_limits<T>::max() but shorter and not affected by the max macro.
+template <typename T> constexpr auto max_value() -> T {
+ return (std::numeric_limits<T>::max)();
+}
+template <typename T> constexpr auto num_bits() -> int {
+ return std::numeric_limits<T>::digits;
+}
+// std::numeric_limits<T>::digits may return 0 for 128-bit ints.
+template <> constexpr auto num_bits<int128_t>() -> int { return 128; }
+template <> constexpr auto num_bits<uint128_t>() -> int { return 128; }
+template <> constexpr auto num_bits<fallback_uintptr>() -> int {
+ return static_cast<int>(sizeof(void*) *
+ std::numeric_limits<unsigned char>::digits);
+}
+
+FMT_INLINE void assume(bool condition) {
+ (void)condition;
+#if FMT_HAS_BUILTIN(__builtin_assume)
+ __builtin_assume(condition);
+#endif
+}
+
+// An approximation of iterator_t for pre-C++20 systems.
+template <typename T>
+using iterator_t = decltype(std::begin(std::declval<T&>()));
+template <typename T> using sentinel_t = decltype(std::end(std::declval<T&>()));
+
+// A workaround for std::string not having mutable data() until C++17.
+template <typename Char>
+inline auto get_data(std::basic_string<Char>& s) -> Char* {
+ return &s[0];
+}
+template <typename Container>
+inline auto get_data(Container& c) -> typename Container::value_type* {
+ 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> 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> inline 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)>
+#if FMT_CLANG_VERSION >= 307 && !FMT_ICC_VERSION
+__attribute__((no_sanitize("undefined")))
+#endif
+inline auto
+reserve(std::back_insert_iterator<Container> it, size_t n)
+ -> checked_ptr<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);
+}
+
+template <typename T>
+inline auto reserve(buffer_appender<T> it, size_t n) -> buffer_appender<T> {
+ buffer<T>& buf = get_container(it);
+ buf.try_reserve(buf.size() + n);
+ return it;
+}
+
+template <typename Iterator>
+constexpr auto reserve(Iterator& it, size_t) -> Iterator& {
+ return it;
+}
+
+template <typename OutputIt>
+using reserve_iterator =
+ remove_reference_t<decltype(reserve(std::declval<OutputIt&>(), 0))>;
+
+template <typename T, typename OutputIt>
+constexpr auto to_pointer(OutputIt, size_t) -> T* {
+ return nullptr;
+}
+template <typename T> auto to_pointer(buffer_appender<T> it, size_t n) -> T* {
+ buffer<T>& buf = get_container(it);
+ auto size = buf.size();
+ if (buf.capacity() < size + n) return nullptr;
+ buf.try_resize(size + n);
+ return buf.data() + size;
+}
+
+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>)
+ -> std::back_insert_iterator<Container> {
+ return it;
+}
+
+template <typename Iterator>
+constexpr auto base_iterator(Iterator, Iterator it) -> Iterator {
+ return it;
+}
+
+// <algorithm> is spectacularly slow to compile in C++20 so use a simple fill_n
+// instead (#1998).
+template <typename OutputIt, typename Size, typename T>
+FMT_CONSTEXPR auto fill_n(OutputIt out, Size count, const T& value)
+ -> OutputIt {
+ for (Size i = 0; i < count; ++i) *out++ = value;
+ return out;
+}
+template <typename T, typename Size>
+FMT_CONSTEXPR20 auto fill_n(T* out, Size count, char value) -> T* {
+ if (is_constant_evaluated()) {
+ return fill_n<T*, Size, T>(out, count, value);
+ }
+ std::memset(out, value, to_unsigned(count));
+ return out + count;
+}
+
+#ifdef __cpp_char8_t
+using char8_type = char8_t;
+#else
+enum char8_type : unsigned char {};
+#endif
+
+template <typename OutChar, typename InputIt, typename OutputIt>
+FMT_CONSTEXPR FMT_NOINLINE auto copy_str_noinline(InputIt begin, InputIt end,
+ OutputIt out) -> OutputIt {
+ return copy_str<OutChar>(begin, end, out);
+}
+
+// A public domain branchless UTF-8 decoder by Christopher Wellons:
+// https://github.com/skeeto/branchless-utf8
+/* Decode the next character, c, from s, reporting errors in e.
+ *
+ * Since this is a branchless decoder, four bytes will be read from the
+ * buffer regardless of the actual length of the next character. This
+ * means the buffer _must_ have at least three bytes of zero padding
+ * following the end of the data stream.
+ *
+ * Errors are reported in e, which will be non-zero if the parsed
+ * character was somehow invalid: invalid byte sequence, non-canonical
+ * encoding, or a surrogate half.
+ *
+ * The function returns a pointer to the next character. When an error
+ * occurs, this pointer will be a guess that depends on the particular
+ * error, but it will always advance at least one byte.
+ */
+FMT_CONSTEXPR inline auto utf8_decode(const char* s, uint32_t* c, int* e)
+ -> const char* {
+ constexpr const int masks[] = {0x00, 0x7f, 0x1f, 0x0f, 0x07};
+ constexpr const uint32_t mins[] = {4194304, 0, 128, 2048, 65536};
+ constexpr const int shiftc[] = {0, 18, 12, 6, 0};
+ constexpr const int shifte[] = {0, 6, 4, 2, 0};
+
+ int len = code_point_length(s);
+ const char* next = s + len;
+
+ // Assume a four-byte character and load four bytes. Unused bits are
+ // shifted out.
+ *c = uint32_t(s[0] & masks[len]) << 18;
+ *c |= uint32_t(s[1] & 0x3f) << 12;
+ *c |= uint32_t(s[2] & 0x3f) << 6;
+ *c |= uint32_t(s[3] & 0x3f) << 0;
+ *c >>= shiftc[len];
+
+ // Accumulate the various error conditions.
+ using uchar = unsigned char;
+ *e = (*c < mins[len]) << 6; // non-canonical encoding
+ *e |= ((*c >> 11) == 0x1b) << 7; // surrogate half?
+ *e |= (*c > 0x10FFFF) << 8; // out of range?
+ *e |= (uchar(s[1]) & 0xc0) >> 2;
+ *e |= (uchar(s[2]) & 0xc0) >> 4;
+ *e |= uchar(s[3]) >> 6;
+ *e ^= 0x2a; // top two bits of each tail byte correct?
+ *e >>= shifte[len];
+
+ return next;
+}
+
+template <typename F>
+FMT_CONSTEXPR void for_each_codepoint(string_view s, F f) {
+ auto decode = [f](const char* p) {
+ auto cp = uint32_t();
+ auto error = 0;
+ p = utf8_decode(p, &cp, &error);
+ f(cp, error);
+ return p;
+ };
+ auto p = s.data();
+ const size_t block_size = 4; // utf8_decode always reads blocks of 4 chars.
+ if (s.size() >= block_size) {
+ for (auto end = p + s.size() - block_size + 1; p < end;) p = decode(p);
+ }
+ if (auto num_chars_left = s.data() + s.size() - p) {
+ char buf[2 * block_size - 1] = {};
+ copy_str<char>(p, p + num_chars_left, buf);
+ p = buf;
+ do {
+ p = decode(p);
+ } while (p - buf < num_chars_left);
+ }
+}
+
+template <typename Char>
+inline auto compute_width(basic_string_view<Char> s) -> size_t {
+ return s.size();
+}
+
+// Computes approximate display width of a UTF-8 string.
+FMT_CONSTEXPR inline size_t compute_width(string_view s) {
+ size_t num_code_points = 0;
+ // It is not a lambda for compatibility with C++14.
+ struct count_code_points {
+ size_t* count;
+ FMT_CONSTEXPR void operator()(uint32_t cp, int error) const {
+ *count += detail::to_unsigned(
+ 1 +
+ (error == 0 && cp >= 0x1100 &&
+ (cp <= 0x115f || // Hangul Jamo init. consonants
+ cp == 0x2329 || // LEFT-POINTING ANGLE BRACKET
+ cp == 0x232a || // RIGHT-POINTING ANGLE BRACKET
+ // CJK ... Yi except IDEOGRAPHIC HALF FILL SPACE:
+ (cp >= 0x2e80 && cp <= 0xa4cf && cp != 0x303f) ||
+ (cp >= 0xac00 && cp <= 0xd7a3) || // Hangul Syllables
+ (cp >= 0xf900 && cp <= 0xfaff) || // CJK Compatibility Ideographs
+ (cp >= 0xfe10 && cp <= 0xfe19) || // Vertical Forms
+ (cp >= 0xfe30 && cp <= 0xfe6f) || // CJK Compatibility Forms
+ (cp >= 0xff00 && cp <= 0xff60) || // Fullwidth Forms
+ (cp >= 0xffe0 && cp <= 0xffe6) || // Fullwidth Forms
+ (cp >= 0x20000 && cp <= 0x2fffd) || // CJK
+ (cp >= 0x30000 && cp <= 0x3fffd) ||
+ // Miscellaneous Symbols and Pictographs + Emoticons:
+ (cp >= 0x1f300 && cp <= 0x1f64f) ||
+ // Supplemental Symbols and Pictographs:
+ (cp >= 0x1f900 && cp <= 0x1f9ff))));
+ }
+ };
+ for_each_codepoint(s, count_code_points{&num_code_points});
+ return num_code_points;
+}
+
+inline auto compute_width(basic_string_view<char8_type> s) -> size_t {
+ return compute_width(basic_string_view<char>(
+ reinterpret_cast<const char*>(s.data()), s.size()));
+}
+
+template <typename Char>
+inline auto code_point_index(basic_string_view<Char> s, size_t n) -> size_t {
+ size_t size = s.size();
+ return n < size ? n : size;
+}
+
+// Calculates the index of the nth code point in a UTF-8 string.
+inline auto code_point_index(basic_string_view<char8_type> s, size_t n)
+ -> size_t {
+ const char8_type* data = s.data();
+ size_t num_code_points = 0;
+ for (size_t i = 0, size = s.size(); i != size; ++i) {
+ if ((data[i] & 0xc0) != 0x80 && ++num_code_points > n) return i;
+ }
+ return s.size();
+}
+
+template <typename T>
+using is_fast_float = bool_constant<std::numeric_limits<T>::is_iec559 &&
+ sizeof(T) <= sizeof(double)>;
+
+#ifndef FMT_USE_FULL_CACHE_DRAGONBOX
+# define FMT_USE_FULL_CACHE_DRAGONBOX 0
+#endif
+
+template <typename T>
+template <typename U>
+void buffer<T>::append(const U* begin, const U* end) {
+ while (begin != end) {
+ auto count = to_unsigned(end - begin);
+ 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));
+ size_ += count;
+ begin += count;
+ }
+}
+
+template <typename T, typename Enable = void>
+struct is_locale : std::false_type {};
+template <typename T>
+struct is_locale<T, void_t<decltype(T::classic())>> : std::true_type {};
+} // namespace detail
+
+FMT_MODULE_EXPORT_BEGIN
+
+// The number of characters to store in the basic_memory_buffer object itself
+// to avoid dynamic memory allocation.
+enum { inline_buffer_size = 500 };
+
+/**
+ \rst
+ A dynamically growing memory buffer for trivially copyable/constructible types
+ with the first ``SIZE`` elements stored in the object itself.
+
+ You can use the ``memory_buffer`` type alias for ``char`` instead.
+
+ **Example**::
+
+ fmt::memory_buffer out;
+ format_to(out, "The answer is {}.", 42);
+
+ This will append the following output to the ``out`` object:
+
+ .. code-block:: none
+
+ The answer is 42.
+
+ The output can be converted to an ``std::string`` with ``to_string(out)``.
+ \endrst
+ */
+template <typename T, size_t SIZE = inline_buffer_size,
+ typename Allocator = std::allocator<T>>
+class basic_memory_buffer final : public detail::buffer<T> {
+ private:
+ T store_[SIZE];
+
+ // Don't inherit from Allocator avoid generating type_info for it.
+ Allocator alloc_;
+
+ // Deallocate memory allocated by the buffer.
+ void deallocate() {
+ T* data = this->data();
+ if (data != store_) alloc_.deallocate(data, this->capacity());
+ }
+
+ protected:
+ void grow(size_t size) final FMT_OVERRIDE;
+
+ public:
+ using value_type = T;
+ using const_reference = const T&;
+
+ explicit basic_memory_buffer(const Allocator& alloc = Allocator())
+ : alloc_(alloc) {
+ this->set(store_, SIZE);
+ }
+ ~basic_memory_buffer() { deallocate(); }
+
+ private:
+ // Move data from other to this buffer.
+ void move(basic_memory_buffer& other) {
+ alloc_ = std::move(other.alloc_);
+ T* data = other.data();
+ size_t size = other.size(), capacity = other.capacity();
+ if (data == other.store_) {
+ this->set(store_, capacity);
+ std::uninitialized_copy(other.store_, other.store_ + size,
+ detail::make_checked(store_, capacity));
+ } else {
+ this->set(data, capacity);
+ // Set pointer to the inline array so that delete is not called
+ // when deallocating.
+ other.set(other.store_, 0);
+ }
+ this->resize(size);
+ }
+
+ public:
+ /**
+ \rst
+ Constructs a :class:`fmt::basic_memory_buffer` object moving the content
+ of the other object to it.
+ \endrst
+ */
+ basic_memory_buffer(basic_memory_buffer&& other) FMT_NOEXCEPT { move(other); }
+
+ /**
+ \rst
+ Moves the content of the other ``basic_memory_buffer`` object to this one.
+ \endrst
+ */
+ auto operator=(basic_memory_buffer&& other) FMT_NOEXCEPT
+ -> basic_memory_buffer& {
+ FMT_ASSERT(this != &other, "");
+ deallocate();
+ move(other);
+ return *this;
+ }
+
+ // Returns a copy of the allocator associated with this buffer.
+ auto get_allocator() const -> Allocator { return alloc_; }
+
+ /**
+ Resizes the buffer to contain *count* elements. If T is a POD type new
+ elements may not be initialized.
+ */
+ void resize(size_t count) { this->try_resize(count); }
+
+ /** Increases the buffer capacity to *new_capacity*. */
+ void reserve(size_t new_capacity) { this->try_reserve(new_capacity); }
+
+ // Directly append data into the buffer
+ using detail::buffer<T>::append;
+ template <typename ContiguousRange>
+ void append(const ContiguousRange& range) {
+ append(range.data(), range.data() + range.size());
+ }
+};
+
+template <typename T, size_t SIZE, typename Allocator>
+void basic_memory_buffer<T, SIZE, Allocator>::grow(size_t size) {
+#ifdef FMT_FUZZ
+ if (size > 5000) throw std::runtime_error("fuzz mode - won't grow that much");
+#endif
+ 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 {
+};
+
+namespace detail {
+FMT_API void print(std::FILE*, string_view);
+}
+
+/** A formatting error such as invalid format string. */
+FMT_CLASS_API
+class FMT_API 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() FMT_NOEXCEPT FMT_OVERRIDE FMT_MSC_DEFAULT;
+};
+
+/**
+ \rst
+ Constructs a `~fmt::format_arg_store` object that contains references
+ to arguments and can be implicitly converted to `~fmt::format_args`.
+ If ``fmt`` is a compile-time string then `make_args_checked` checks
+ its validity at compile time.
+ \endrst
+ */
+template <typename... Args, typename S, typename Char = char_t<S>>
+FMT_INLINE auto make_args_checked(const S& fmt,
+ const remove_reference_t<Args>&... args)
+ -> format_arg_store<buffer_context<Char>, remove_reference_t<Args>...> {
+ static_assert(
+ detail::count<(
+ std::is_base_of<detail::view, remove_reference_t<Args>>::value &&
+ std::is_reference<Args>::value)...>() == 0,
+ "passing views as lvalues is disallowed");
+ detail::check_format_string<Args...>(fmt);
+ return {args...};
+}
+
+// compile-time support
+namespace detail_exported {
+#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
+template <typename Char, size_t N> struct fixed_string {
+ constexpr fixed_string(const Char (&str)[N]) {
+ detail::copy_str<Char, const Char*, Char*>(static_cast<const Char*>(str),
+ str + N, data);
+ }
+ Char data[N]{};
+};
+#endif
+
+// Converts a compile-time string to basic_string_view.
+template <typename Char, size_t N>
+constexpr auto compile_string_to_view(const Char (&s)[N])
+ -> basic_string_view<Char> {
+ // Remove trailing NUL character if needed. Won't be present if this is used
+ // with a raw character array (i.e. not defined as a string).
+ return {s, N - (std::char_traits<Char>::to_int_type(s[N - 1]) == 0 ? 1 : 0)};
+}
+template <typename Char>
+constexpr auto compile_string_to_view(detail::std_string_view<Char> s)
+ -> basic_string_view<Char> {
+ return {s.data(), s.size()};
+}
+} // namespace detail_exported
+
+FMT_BEGIN_DETAIL_NAMESPACE
+
+inline void throw_format_error(const char* message) {
+ FMT_THROW(format_error(message));
+}
+
+template <typename T> struct is_integral : std::is_integral<T> {};
+template <> struct is_integral<int128_t> : 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_t>::value>;
+
+// Returns true if value is negative, false otherwise.
+// Same as `value < 0` but doesn't produce warnings if T is an unsigned type.
+template <typename T, FMT_ENABLE_IF(is_signed<T>::value)>
+FMT_CONSTEXPR auto is_negative(T value) -> bool {
+ return value < 0;
+}
+template <typename T, FMT_ENABLE_IF(!is_signed<T>::value)>
+FMT_CONSTEXPR auto is_negative(T) -> bool {
+ return false;
+}
+
+template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
+FMT_CONSTEXPR auto is_supported_floating_point(T) -> uint16_t {
+ return (std::is_same<T, float>::value && FMT_USE_FLOAT) ||
+ (std::is_same<T, double>::value && FMT_USE_DOUBLE) ||
+ (std::is_same<T, long double>::value && FMT_USE_LONG_DOUBLE);
+}
+
+// Smallest of uint32_t, uint64_t, uint128_t that is large enough to
+// represent all values of an integral type T.
+template <typename T>
+using uint32_or_64_or_128_t =
+ conditional_t<num_bits<T>() <= 32 && !FMT_REDUCE_INT_INSTANTIATIONS,
+ uint32_t,
+ conditional_t<num_bits<T>() <= 64, uint64_t, uint128_t>>;
+template <typename T>
+using uint64_or_128_t = conditional_t<num_bits<T>() <= 64, uint64_t, uint128_t>;
+
+#define FMT_POWERS_OF_10(factor) \
+ factor * 10, (factor)*100, (factor)*1000, (factor)*10000, (factor)*100000, \
+ (factor)*1000000, (factor)*10000000, (factor)*100000000, \
+ (factor)*1000000000
+
+// Static data is placed in this class template for the header-only config.
+template <typename T = void> struct basic_data {
+ // log10(2) = 0x0.4d104d427de7fbcc...
+ static const uint64_t log10_2_significand = 0x4d104d427de7fbcc;
+
+ // GCC generates slightly better code for pairs than chars.
+ FMT_API static constexpr const char 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'}};
+
+ FMT_API static constexpr const char hex_digits[] = "0123456789abcdef";
+ FMT_API static constexpr const char signs[4] = {0, '-', '+', ' '};
+ FMT_API static constexpr const unsigned prefixes[4] = {0, 0, 0x1000000u | '+',
+ 0x1000000u | ' '};
+ FMT_API static constexpr const char left_padding_shifts[5] = {31, 31, 0, 1,
+ 0};
+ FMT_API static constexpr const char right_padding_shifts[5] = {0, 31, 0, 1,
+ 0};
+};
+
+#ifdef FMT_SHARED
+// Required for -flto, -fivisibility=hidden and -shared to work
+extern template struct basic_data<void>;
+#endif
+
+// This is a struct rather than an alias to avoid shadowing warnings in gcc.
+struct data : basic_data<> {};
+
+template <typename T> FMT_CONSTEXPR auto count_digits_fallback(T n) -> int {
+ int count = 1;
+ for (;;) {
+ // Integer division is slow so do it for a group of four digits instead
+ // of for every digit. The idea comes from the talk by Alexandrescu
+ // "Three Optimization Tips for C++". See speed-test for a comparison.
+ if (n < 10) return count;
+ if (n < 100) return count + 1;
+ if (n < 1000) return count + 2;
+ if (n < 10000) return count + 3;
+ n /= 10000u;
+ count += 4;
+ }
+}
+#if FMT_USE_INT128
+FMT_CONSTEXPR inline auto count_digits(uint128_t n) -> int {
+ return count_digits_fallback(n);
+}
+#endif
+
+// Returns the number of decimal digits in n. Leading zeros are not counted
+// except for n == 0 in which case count_digits returns 1.
+FMT_CONSTEXPR20 inline auto count_digits(uint64_t n) -> int {
+#ifdef FMT_BUILTIN_CLZLL
+ if (!is_constant_evaluated()) {
+ // https://github.com/fmtlib/format-benchmark/blob/master/digits10
+ // Maps bsr(n) to ceil(log10(pow(2, bsr(n) + 1) - 1)).
+ constexpr uint16_t bsr2log10[] = {
+ 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5,
+ 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10,
+ 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15, 15,
+ 15, 16, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 19, 20};
+ auto t = bsr2log10[FMT_BUILTIN_CLZLL(n | 1) ^ 63];
+ constexpr const uint64_t zero_or_powers_of_10[] = {
+ 0, 0, FMT_POWERS_OF_10(1U), FMT_POWERS_OF_10(1000000000ULL),
+ 10000000000000000000ULL};
+ return t - (n < zero_or_powers_of_10[t]);
+ }
+#endif
+ return count_digits_fallback(n);
+}
+
+// Counts the number of digits in n. BITS = log2(radix).
+template <int BITS, typename UInt>
+FMT_CONSTEXPR auto count_digits(UInt n) -> int {
+#ifdef FMT_BUILTIN_CLZ
+ if (num_bits<UInt>() == 32)
+ return (FMT_BUILTIN_CLZ(static_cast<uint32_t>(n) | 1) ^ 31) / BITS + 1;
+#endif
+ int num_digits = 0;
+ do {
+ ++num_digits;
+ } while ((n >>= BITS) != 0);
+ return num_digits;
+}
+
+template <> auto count_digits<4>(detail::fallback_uintptr n) -> int;
+
+// It is a separate function rather than a part of count_digits to workaround
+// the lack of static constexpr in constexpr functions.
+FMT_INLINE uint64_t count_digits_inc(int n) {
+ // 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)
+ 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
+ FMT_INC(100), FMT_INC(100), FMT_INC(100), // 512
+ FMT_INC(1000), FMT_INC(1000), FMT_INC(1000), // 4096
+ FMT_INC(10000), FMT_INC(10000), FMT_INC(10000), // 32k
+ FMT_INC(100000), FMT_INC(100000), FMT_INC(100000), // 256k
+ FMT_INC(1000000), FMT_INC(1000000), FMT_INC(1000000), // 2048k
+ FMT_INC(10000000), FMT_INC(10000000), FMT_INC(10000000), // 16M
+ FMT_INC(100000000), FMT_INC(100000000), FMT_INC(100000000), // 128M
+ FMT_INC(1000000000), FMT_INC(1000000000), FMT_INC(1000000000), // 1024M
+ FMT_INC(1000000000), FMT_INC(1000000000) // 4B
+ };
+ return table[n];
+}
+
+// Optional version of count_digits for better performance on 32-bit platforms.
+FMT_CONSTEXPR20 inline auto count_digits(uint32_t n) -> int {
+#ifdef FMT_BUILTIN_CLZ
+ if (!is_constant_evaluated()) {
+ auto inc = count_digits_inc(FMT_BUILTIN_CLZ(n | 1) ^ 31);
+ return static_cast<int>((n + inc) >> 32);
+ }
+#endif
+ return count_digits_fallback(n);
+}
+
+template <typename Int> constexpr auto digits10() FMT_NOEXCEPT -> int {
+ return std::numeric_limits<Int>::digits10;
+}
+template <> constexpr auto digits10<int128_t>() FMT_NOEXCEPT -> int {
+ return 38;
+}
+template <> constexpr auto digits10<uint128_t>() FMT_NOEXCEPT -> int {
+ return 38;
+}
+
+template <typename Char> struct thousands_sep_result {
+ std::string grouping;
+ Char thousands_sep;
+};
+
+template <typename Char>
+FMT_API auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result<Char>;
+template <typename Char>
+inline auto thousands_sep(locale_ref loc) -> thousands_sep_result<Char> {
+ auto result = thousands_sep_impl<char>(loc);
+ return {result.grouping, Char(result.thousands_sep)};
+}
+template <>
+inline auto thousands_sep(locale_ref loc) -> thousands_sep_result<wchar_t> {
+ return thousands_sep_impl<wchar_t>(loc);
+}
+
+template <typename Char>
+FMT_API auto decimal_point_impl(locale_ref loc) -> Char;
+template <typename Char> inline auto decimal_point(locale_ref loc) -> Char {
+ return Char(decimal_point_impl<char>(loc));
+}
+template <> inline auto decimal_point(locale_ref loc) -> wchar_t {
+ return decimal_point_impl<wchar_t>(loc);
+}
+
+// Compares two characters for equality.
+template <typename Char> auto equal2(const Char* lhs, const char* rhs) -> bool {
+ return lhs[0] == Char(rhs[0]) && lhs[1] == Char(rhs[1]);
+}
+inline auto equal2(const char* lhs, const char* rhs) -> bool {
+ return memcmp(lhs, rhs, 2) == 0;
+}
+
+// Copies two characters from src to dst.
+template <typename Char> void copy2(Char* dst, const char* src) {
+ *dst++ = static_cast<Char>(*src++);
+ *dst = static_cast<Char>(*src);
+}
+FMT_INLINE void copy2(char* dst, const char* src) { memcpy(dst, src, 2); }
+
+template <typename Iterator> struct format_decimal_result {
+ Iterator begin;
+ Iterator end;
+};
+
+// Formats a decimal unsigned integer value writing into out pointing to a
+// buffer of specified size. The caller must ensure that the buffer is large
+// enough.
+template <typename Char, typename UInt>
+FMT_CONSTEXPR20 auto format_decimal(Char* out, UInt value, int size)
+ -> format_decimal_result<Char*> {
+ FMT_ASSERT(size >= count_digits(value), "invalid digit count");
+ out += size;
+ Char* end = out;
+ if (is_constant_evaluated()) {
+ while (value >= 10) {
+ *--out = static_cast<Char>('0' + value % 10);
+ value /= 10;
+ }
+ *--out = static_cast<Char>('0' + value);
+ return {out, end};
+ }
+ while (value >= 100) {
+ // Integer division is slow so do it for a group of two digits instead
+ // of for every digit. The idea comes from the talk by Alexandrescu
+ // "Three Optimization Tips for C++". See speed-test for a comparison.
+ out -= 2;
+ copy2(out, data::digits[value % 100]);
+ value /= 100;
+ }
+ if (value < 10) {
+ *--out = static_cast<Char>('0' + value);
+ return {out, end};
+ }
+ out -= 2;
+ copy2(out, data::digits[value]);
+ return {out, end};
+}
+
+template <typename Char, typename UInt, typename Iterator,
+ FMT_ENABLE_IF(!std::is_pointer<remove_cvref_t<Iterator>>::value)>
+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];
+ auto end = format_decimal(buffer, value, size).end;
+ return {out, detail::copy_str_noinline<Char>(buffer, end, out)};
+}
+
+template <unsigned BASE_BITS, typename Char, typename UInt>
+FMT_CONSTEXPR auto format_uint(Char* buffer, UInt value, int num_digits,
+ bool upper = false) -> Char* {
+ buffer += num_digits;
+ Char* end = buffer;
+ do {
+ const char* digits = upper ? "0123456789ABCDEF" : data::hex_digits;
+ unsigned digit = (value & ((1 << BASE_BITS) - 1));
+ *--buffer = static_cast<Char>(BASE_BITS < 4 ? static_cast<char>('0' + digit)
+ : digits[digit]);
+ } while ((value >>= BASE_BITS) != 0);
+ return end;
+}
+
+template <unsigned BASE_BITS, typename Char>
+auto format_uint(Char* buffer, detail::fallback_uintptr n, int num_digits,
+ bool = false) -> Char* {
+ auto char_digits = std::numeric_limits<unsigned char>::digits / 4;
+ int start = (num_digits + char_digits - 1) / char_digits - 1;
+ if (int start_digits = num_digits % char_digits) {
+ unsigned value = n.value[start--];
+ buffer = format_uint<BASE_BITS>(buffer, value, start_digits);
+ }
+ for (; start >= 0; --start) {
+ unsigned value = n.value[start];
+ buffer += char_digits;
+ auto p = buffer;
+ for (int i = 0; i < char_digits; ++i) {
+ unsigned digit = (value & ((1 << BASE_BITS) - 1));
+ *--p = static_cast<Char>(data::hex_digits[digit]);
+ value >>= BASE_BITS;
+ }
+ }
+ return buffer;
+}
+
+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 {
+ if (auto ptr = to_pointer<Char>(out, to_unsigned(num_digits))) {
+ format_uint<BASE_BITS>(ptr, value, num_digits, upper);
+ return out;
+ }
+ // Buffer should be large enough to hold all digits (digits / BASE_BITS + 1).
+ char buffer[num_bits<UInt>() / BASE_BITS + 1];
+ format_uint<BASE_BITS>(buffer, value, num_digits, upper);
+ return detail::copy_str_noinline<Char>(buffer, buffer + num_digits, out);
+}
+
+// A converter from UTF-8 to UTF-16.
+class utf8_to_utf16 {
+ private:
+ basic_memory_buffer<wchar_t> buffer_;
+
+ public:
+ FMT_API explicit utf8_to_utf16(string_view s);
+ operator basic_string_view<wchar_t>() const { return {&buffer_[0], size()}; }
+ auto size() const -> size_t { return buffer_.size() - 1; }
+ auto c_str() const -> const wchar_t* { return &buffer_[0]; }
+ auto str() const -> std::wstring { return {&buffer_[0], size()}; }
+};
+
+namespace dragonbox {
+
+// Type-specific information that Dragonbox uses.
+template <class T> struct float_info;
+
+template <> struct float_info<float> {
+ using carrier_uint = uint32_t;
+ static const int significand_bits = 23;
+ static const int exponent_bits = 8;
+ static const int min_exponent = -126;
+ static const int max_exponent = 127;
+ static const int exponent_bias = -127;
+ static const int decimal_digits = 9;
+ static const int kappa = 1;
+ static const int big_divisor = 100;
+ static const int small_divisor = 10;
+ static const int min_k = -31;
+ static const int max_k = 46;
+ static const int cache_bits = 64;
+ static const int divisibility_check_by_5_threshold = 39;
+ static const int case_fc_pm_half_lower_threshold = -1;
+ static const int case_fc_pm_half_upper_threshold = 6;
+ static const int case_fc_lower_threshold = -2;
+ static const int case_fc_upper_threshold = 6;
+ static const int case_shorter_interval_left_endpoint_lower_threshold = 2;
+ static const int case_shorter_interval_left_endpoint_upper_threshold = 3;
+ static const int shorter_interval_tie_lower_threshold = -35;
+ static const int shorter_interval_tie_upper_threshold = -35;
+ static const int max_trailing_zeros = 7;
+};
+
+template <> struct float_info<double> {
+ using carrier_uint = uint64_t;
+ static const int significand_bits = 52;
+ static const int exponent_bits = 11;
+ static const int min_exponent = -1022;
+ static const int max_exponent = 1023;
+ static const int exponent_bias = -1023;
+ static const int decimal_digits = 17;
+ static const int kappa = 2;
+ 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 cache_bits = 128;
+ static const int divisibility_check_by_5_threshold = 86;
+ static const int case_fc_pm_half_lower_threshold = -2;
+ static const int case_fc_pm_half_upper_threshold = 9;
+ static const int case_fc_lower_threshold = -4;
+ static const int case_fc_upper_threshold = 9;
+ static const int case_shorter_interval_left_endpoint_lower_threshold = 2;
+ static const int case_shorter_interval_left_endpoint_upper_threshold = 3;
+ static const int shorter_interval_tie_lower_threshold = -77;
+ static const int shorter_interval_tie_upper_threshold = -77;
+ static const int max_trailing_zeros = 16;
+};
+
+template <typename T> struct decimal_fp {
+ using significand_type = typename float_info<T>::carrier_uint;
+ significand_type significand;
+ int exponent;
+};
+
+template <typename T>
+FMT_API auto to_decimal(T x) FMT_NOEXCEPT -> decimal_fp<T>;
+} // namespace dragonbox
+
+template <typename T>
+constexpr auto exponent_mask() ->
+ typename dragonbox::float_info<T>::carrier_uint {
+ using uint = typename dragonbox::float_info<T>::carrier_uint;
+ return ((uint(1) << dragonbox::float_info<T>::exponent_bits) - 1)
+ << dragonbox::float_info<T>::significand_bits;
+}
+
+// Writes the exponent exp in the form "[+-]d{2,3}" to buffer.
+template <typename Char, typename It>
+auto write_exponent(int exp, It it) -> It {
+ FMT_ASSERT(-10000 < exp && exp < 10000, "exponent out of range");
+ if (exp < 0) {
+ *it++ = static_cast<Char>('-');
+ exp = -exp;
+ } else {
+ *it++ = static_cast<Char>('+');
+ }
+ if (exp >= 100) {
+ const char* top = data::digits[exp / 100];
+ if (exp >= 1000) *it++ = static_cast<Char>(top[0]);
+ *it++ = static_cast<Char>(top[1]);
+ exp %= 100;
+ }
+ const char* d = data::digits[exp];
+ *it++ = static_cast<Char>(d[0]);
+ *it++ = static_cast<Char>(d[1]);
+ return it;
+}
+
+template <typename T>
+auto format_float(T value, int precision, float_specs specs, buffer<char>& buf)
+ -> int;
+
+// Formats a floating-point number with snprintf.
+template <typename T>
+auto snprintf_float(T value, int precision, float_specs specs,
+ buffer<char>& buf) -> int;
+
+template <typename T> auto promote_float(T value) -> T { return value; }
+inline auto promote_float(float value) -> double {
+ return static_cast<double>(value);
+}
+
+template <typename OutputIt, typename Char>
+FMT_NOINLINE FMT_CONSTEXPR auto fill(OutputIt it, size_t n,
+ const fill_t<Char>& fill) -> OutputIt {
+ auto fill_size = fill.size();
+ if (fill_size == 1) return detail::fill_n(it, n, fill[0]);
+ auto data = fill.data();
+ for (size_t i = 0; i < n; ++i)
+ it = copy_str<Char>(data, data + fill_size, it);
+ return it;
+}
+
+// Writes the output of f, padded according to format specifications in specs.
+// size: output size in code units.
+// 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,
+ size_t size, size_t width, F&& f) -> OutputIt {
+ static_assert(align == align::left || align == align::right, "");
+ unsigned spec_width = to_unsigned(specs.width);
+ size_t padding = spec_width > width ? spec_width - width : 0;
+ auto* shifts = align == align::left ? data::left_padding_shifts
+ : data::right_padding_shifts;
+ size_t left_padding = padding >> shifts[specs.align];
+ size_t right_padding = padding - left_padding;
+ auto it = reserve(out, size + padding * specs.fill.size());
+ if (left_padding != 0) it = fill(it, left_padding, specs.fill);
+ it = f(it);
+ if (right_padding != 0) it = fill(it, right_padding, specs.fill);
+ return base_iterator(out, it);
+}
+
+template <align::type align = align::left, typename OutputIt, typename Char,
+ typename F>
+constexpr auto write_padded(OutputIt out, const basic_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 {
+ return write_padded<align>(
+ out, specs, bytes.size(), [bytes](reserve_iterator<OutputIt> it) {
+ const char* data = bytes.data();
+ return copy_str<Char>(data, data + bytes.size(), it);
+ });
+}
+
+template <typename Char, typename OutputIt, typename UIntPtr>
+auto write_ptr(OutputIt out, UIntPtr value,
+ const basic_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) {
+ *it++ = static_cast<Char>('0');
+ *it++ = static_cast<Char>('x');
+ return format_uint<4, Char>(it, value, num_digits);
+ };
+ return specs ? write_padded<align::right>(out, *specs, size, write)
+ : base_iterator(out, write(reserve(out, size)));
+}
+
+template <typename Char, typename OutputIt>
+FMT_CONSTEXPR auto write_char(OutputIt out, Char value,
+ const basic_format_specs<Char>& specs)
+ -> OutputIt {
+ return write_padded(out, specs, 1, [=](reserve_iterator<OutputIt> it) {
+ *it++ = value;
+ return it;
+ });
+}
+template <typename Char, typename OutputIt>
+FMT_CONSTEXPR auto write(OutputIt out, Char value,
+ const basic_format_specs<Char>& specs,
+ locale_ref loc = {}) -> OutputIt {
+ return check_char_specs(specs)
+ ? write_char(out, value, specs)
+ : write(out, static_cast<int>(value), specs, loc);
+}
+
+// Data for write_int that doesn't depend on output iterator type. It is used to
+// avoid template code bloat.
+template <typename Char> struct write_int_data {
+ size_t size;
+ size_t padding;
+
+ FMT_CONSTEXPR write_int_data(int num_digits, unsigned prefix,
+ const basic_format_specs<Char>& specs)
+ : size((prefix >> 24) + to_unsigned(num_digits)), padding(0) {
+ if (specs.align == align::numeric) {
+ auto width = to_unsigned(specs.width);
+ if (width > size) {
+ padding = width - size;
+ size = width;
+ }
+ } else if (specs.precision > num_digits) {
+ size = (prefix >> 24) + to_unsigned(specs.precision);
+ padding = to_unsigned(specs.precision - num_digits);
+ }
+ }
+};
+
+// Writes an integer in the format
+// <left-padding><prefix><numeric-padding><digits><right-padding>
+// where <digits> are written by write_digits(it).
+// prefix contains chars in three lower bytes and the size in the fourth byte.
+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,
+ W write_digits) -> OutputIt {
+ // Slightly faster check for specs.width == 0 && specs.precision == -1.
+ if ((specs.width | (specs.precision + 1)) == 0) {
+ auto it = reserve(out, to_unsigned(num_digits) + (prefix >> 24));
+ if (prefix != 0) {
+ for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8)
+ *it++ = static_cast<Char>(p & 0xff);
+ }
+ return base_iterator(out, write_digits(it));
+ }
+ auto data = write_int_data<Char>(num_digits, prefix, specs);
+ return write_padded<align::right>(
+ out, specs, data.size, [=](reserve_iterator<OutputIt> it) {
+ for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8)
+ *it++ = static_cast<Char>(p & 0xff);
+ it = detail::fill_n(it, data.padding, static_cast<Char>('0'));
+ return write_digits(it);
+ });
+}
+
+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 {
+ static_assert(std::is_same<uint64_or_128_t<UInt>, UInt>::value, "");
+ const auto sep_size = 1;
+ auto ts = thousands_sep<Char>(loc);
+ if (!ts.thousands_sep) return false;
+ int num_digits = count_digits(value);
+ int size = num_digits, n = num_digits;
+ const std::string& groups = ts.grouping;
+ std::string::const_iterator group = groups.cbegin();
+ while (group != groups.cend() && n > *group && *group > 0 &&
+ *group != max_value<char>()) {
+ size += sep_size;
+ n -= *group;
+ ++group;
+ }
+ if (group == groups.cend()) size += sep_size * ((n - 1) / groups.back());
+ char digits[40];
+ format_decimal(digits, value, num_digits);
+ basic_memory_buffer<Char> buffer;
+ if (prefix != 0) ++size;
+ const auto usize = to_unsigned(size);
+ buffer.resize(usize);
+ basic_string_view<Char> s(&ts.thousands_sep, sep_size);
+ // Index of a decimal digit with the least significant digit having index 0.
+ int digit_index = 0;
+ group = groups.cbegin();
+ auto p = buffer.data() + size - 1;
+ for (int i = num_digits - 1; i > 0; --i) {
+ *p-- = static_cast<Char>(digits[i]);
+ if (*group <= 0 || ++digit_index % *group != 0 ||
+ *group == max_value<char>())
+ continue;
+ if (group + 1 != groups.cend()) {
+ digit_index = 0;
+ ++group;
+ }
+ std::uninitialized_copy(s.data(), s.data() + s.size(),
+ make_checked(p, s.size()));
+ p -= s.size();
+ }
+ *p-- = static_cast<Char>(*digits);
+ if (prefix != 0) *p = static_cast<Char>(prefix);
+ auto data = buffer.data();
+ out = write_padded<align::right>(
+ out, specs, usize, usize, [=](reserve_iterator<OutputIt> it) {
+ return copy_str<Char>(data, data + size, it);
+ });
+ return true;
+}
+
+FMT_CONSTEXPR inline void prefix_append(unsigned& prefix, unsigned value) {
+ prefix |= prefix != 0 ? value << 8 : value;
+ prefix += (1u + (value > 0xff ? 1 : 0)) << 24;
+}
+
+template <typename UInt> struct write_int_arg {
+ UInt abs_value;
+ unsigned prefix;
+};
+
+template <typename T>
+FMT_CONSTEXPR auto make_write_int_arg(T value, sign_t sign)
+ -> write_int_arg<uint32_or_64_or_128_t<T>> {
+ auto prefix = 0u;
+ auto abs_value = static_cast<uint32_or_64_or_128_t<T>>(value);
+ if (is_negative(value)) {
+ prefix = 0x01000000 | '-';
+ abs_value = 0 - abs_value;
+ } else {
+ prefix = data::prefixes[sign];
+ }
+ return {abs_value, prefix};
+}
+
+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 {
+ static_assert(std::is_same<T, uint32_or_64_or_128_t<T>>::value, "");
+ auto abs_value = arg.abs_value;
+ auto prefix = arg.prefix;
+ auto utype = static_cast<unsigned>(specs.type);
+ switch (specs.type) {
+ case 0:
+ case 'd': {
+ 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) {
+ return format_decimal<Char>(it, abs_value, num_digits).end;
+ });
+ }
+ case 'x':
+ case 'X': {
+ if (specs.alt) prefix_append(prefix, (utype << 8) | '0');
+ bool upper = specs.type != 'x';
+ int num_digits = count_digits<4>(abs_value);
+ return write_int(
+ out, num_digits, prefix, specs, [=](reserve_iterator<OutputIt> it) {
+ return format_uint<4, Char>(it, abs_value, num_digits, upper);
+ });
+ }
+ case 'b':
+ case 'B': {
+ if (specs.alt) prefix_append(prefix, (utype << 8) | '0');
+ int num_digits = count_digits<1>(abs_value);
+ return write_int(out, num_digits, prefix, specs,
+ [=](reserve_iterator<OutputIt> it) {
+ return format_uint<1, Char>(it, abs_value, num_digits);
+ });
+ }
+ case 'o': {
+ int num_digits = count_digits<3>(abs_value);
+ if (specs.alt && specs.precision <= num_digits && abs_value != 0) {
+ // Octal prefix '0' is counted as a digit, so only add it if precision
+ // is not greater than the number of digits.
+ prefix_append(prefix, '0');
+ }
+ return write_int(out, num_digits, prefix, specs,
+ [=](reserve_iterator<OutputIt> it) {
+ return format_uint<3, Char>(it, abs_value, num_digits);
+ });
+ }
+ case 'c':
+ return write_char(out, static_cast<Char>(abs_value), specs);
+ default:
+ FMT_THROW(format_error("invalid type specifier"));
+ }
+ return out;
+}
+template <typename Char, typename OutputIt, typename T,
+ FMT_ENABLE_IF(is_integral<T>::value &&
+ !std::is_same<T, bool>::value &&
+ std::is_same<OutputIt, buffer_appender<Char>>::value)>
+FMT_CONSTEXPR auto write(OutputIt out, T value,
+ const basic_format_specs<Char>& specs, locale_ref loc)
+ -> OutputIt {
+ return write_int(out, make_write_int_arg(value, specs.sign), specs, loc);
+}
+// An inlined version of write used in format string compilation.
+template <typename Char, typename OutputIt, typename T,
+ FMT_ENABLE_IF(is_integral<T>::value &&
+ !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,
+ locale_ref loc) -> OutputIt {
+ return write_int(out, make_write_int_arg(value, specs.sign), specs, loc);
+}
+
+template <typename Char, typename OutputIt>
+FMT_CONSTEXPR auto write(OutputIt out, basic_string_view<Char> s,
+ const basic_format_specs<Char>& specs) -> OutputIt {
+ auto data = s.data();
+ auto size = s.size();
+ if (specs.precision >= 0 && to_unsigned(specs.precision) < size)
+ size = code_point_index(s, to_unsigned(specs.precision));
+ auto width =
+ specs.width != 0 ? compute_width(basic_string_view<Char>(data, size)) : 0;
+ return write_padded(out, specs, size, width,
+ [=](reserve_iterator<OutputIt> it) {
+ return copy_str<Char>(data, data + size, it);
+ });
+}
+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)
+ -> 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)
+ -> OutputIt {
+ return check_cstring_type_spec(specs.type)
+ ? write(out, basic_string_view<Char>(s), specs, {})
+ : write_ptr<Char>(out, to_uintptr(s), &specs);
+}
+
+template <typename Char, typename OutputIt>
+auto write_nonfinite(OutputIt out, bool isinf, basic_format_specs<Char> specs,
+ const float_specs& fspecs) -> OutputIt {
+ auto str =
+ isinf ? (fspecs.upper ? "INF" : "inf") : (fspecs.upper ? "NAN" : "nan");
+ constexpr size_t str_size = 3;
+ auto sign = fspecs.sign;
+ auto size = str_size + (sign ? 1 : 0);
+ // Replace '0'-padding with space for non-finite values.
+ const bool is_zero_fill =
+ specs.fill.size() == 1 && *specs.fill.data() == static_cast<Char>('0');
+ if (is_zero_fill) specs.fill[0] = static_cast<Char>(' ');
+ return write_padded(out, specs, size, [=](reserve_iterator<OutputIt> it) {
+ if (sign) *it++ = static_cast<Char>(data::signs[sign]);
+ return copy_str<Char>(str, str + str_size, it);
+ });
+}
+
+// A decimal floating-point number significand * pow(10, exp).
+struct big_decimal_fp {
+ const char* significand;
+ int significand_size;
+ int exponent;
+};
+
+inline auto get_significand_size(const big_decimal_fp& fp) -> int {
+ return fp.significand_size;
+}
+template <typename T>
+inline auto get_significand_size(const dragonbox::decimal_fp<T>& fp) -> int {
+ return count_digits(fp.significand);
+}
+
+template <typename Char, typename OutputIt>
+inline auto write_significand(OutputIt out, const char* significand,
+ int& significand_size) -> OutputIt {
+ return copy_str<Char>(significand, significand + significand_size, out);
+}
+template <typename Char, typename OutputIt, typename UInt>
+inline auto write_significand(OutputIt out, UInt significand,
+ int significand_size) -> OutputIt {
+ return format_decimal<Char>(out, significand, significand_size).end;
+}
+
+template <typename Char, typename UInt,
+ FMT_ENABLE_IF(std::is_integral<UInt>::value)>
+inline auto write_significand(Char* out, UInt significand, int significand_size,
+ int integral_size, Char decimal_point) -> Char* {
+ if (!decimal_point)
+ return format_decimal(out, significand, significand_size).end;
+ auto end = format_decimal(out + 1, significand, significand_size).end;
+ if (integral_size == 1) {
+ out[0] = out[1];
+ } else {
+ std::uninitialized_copy_n(out + 1, integral_size,
+ make_checked(out, to_unsigned(integral_size)));
+ }
+ out[integral_size] = decimal_point;
+ return end;
+}
+
+template <typename OutputIt, typename UInt, typename Char,
+ FMT_ENABLE_IF(!std::is_pointer<remove_cvref_t<OutputIt>>::value)>
+inline auto write_significand(OutputIt out, UInt significand,
+ int significand_size, int integral_size,
+ Char decimal_point) -> OutputIt {
+ // Buffer is large enough to hold digits (digits10 + 1) and a decimal point.
+ Char buffer[digits10<UInt>() + 2];
+ auto end = write_significand(buffer, significand, significand_size,
+ integral_size, decimal_point);
+ return detail::copy_str_noinline<Char>(buffer, end, out);
+}
+
+template <typename OutputIt, typename Char>
+inline auto write_significand(OutputIt out, const char* significand,
+ int significand_size, int integral_size,
+ Char decimal_point) -> OutputIt {
+ out = detail::copy_str_noinline<Char>(significand,
+ significand + integral_size, out);
+ if (!decimal_point) return out;
+ *out++ = decimal_point;
+ return detail::copy_str_noinline<Char>(significand + integral_size,
+ significand + significand_size, out);
+}
+
+template <typename OutputIt, typename DecimalFP, typename Char>
+auto write_float(OutputIt out, const DecimalFP& fp,
+ const basic_format_specs<Char>& specs, float_specs fspecs,
+ Char decimal_point) -> OutputIt {
+ auto significand = fp.significand;
+ int significand_size = get_significand_size(fp);
+ static const Char zero = static_cast<Char>('0');
+ auto sign = fspecs.sign;
+ size_t size = to_unsigned(significand_size) + (sign ? 1 : 0);
+ using iterator = reserve_iterator<OutputIt>;
+
+ int output_exp = fp.exponent + significand_size - 1;
+ auto use_exp_format = [=]() {
+ if (fspecs.format == float_format::exp) return true;
+ if (fspecs.format != float_format::general) return false;
+ // Use the fixed notation if the exponent is in [exp_lower, exp_upper),
+ // e.g. 0.0001 instead of 1e-04. Otherwise use the exponent notation.
+ const int exp_lower = -4, exp_upper = 16;
+ return output_exp < exp_lower ||
+ output_exp >= (fspecs.precision > 0 ? fspecs.precision : exp_upper);
+ };
+ if (use_exp_format()) {
+ int num_zeros = 0;
+ if (fspecs.showpoint) {
+ num_zeros = fspecs.precision - significand_size;
+ if (num_zeros < 0) num_zeros = 0;
+ size += to_unsigned(num_zeros);
+ } else if (significand_size == 1) {
+ decimal_point = Char();
+ }
+ auto abs_output_exp = output_exp >= 0 ? output_exp : -output_exp;
+ int exp_digits = 2;
+ if (abs_output_exp >= 100) exp_digits = abs_output_exp >= 1000 ? 4 : 3;
+
+ size += to_unsigned((decimal_point ? 1 : 0) + 2 + exp_digits);
+ char exp_char = fspecs.upper ? 'E' : 'e';
+ auto write = [=](iterator it) {
+ if (sign) *it++ = static_cast<Char>(data::signs[sign]);
+ // Insert a decimal point after the first digit and add an exponent.
+ it = write_significand(it, significand, significand_size, 1,
+ decimal_point);
+ if (num_zeros > 0) it = detail::fill_n(it, num_zeros, zero);
+ *it++ = static_cast<Char>(exp_char);
+ return write_exponent<Char>(output_exp, it);
+ };
+ return specs.width > 0 ? write_padded<align::right>(out, specs, size, write)
+ : base_iterator(out, write(reserve(out, size)));
+ }
+
+ int exp = fp.exponent + significand_size;
+ if (fp.exponent >= 0) {
+ // 1234e5 -> 123400000[.0+]
+ size += to_unsigned(fp.exponent);
+ int num_zeros = fspecs.precision - exp;
+#ifdef FMT_FUZZ
+ if (num_zeros > 5000)
+ throw std::runtime_error("fuzz mode - avoiding excessive cpu use");
+#endif
+ if (fspecs.showpoint) {
+ if (num_zeros <= 0 && fspecs.format != float_format::fixed) num_zeros = 1;
+ if (num_zeros > 0) size += to_unsigned(num_zeros) + 1;
+ }
+ return write_padded<align::right>(out, specs, size, [&](iterator it) {
+ if (sign) *it++ = static_cast<Char>(data::signs[sign]);
+ it = write_significand<Char>(it, significand, significand_size);
+ it = detail::fill_n(it, fp.exponent, zero);
+ if (!fspecs.showpoint) return it;
+ *it++ = decimal_point;
+ return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it;
+ });
+ } else if (exp > 0) {
+ // 1234e-2 -> 12.34[0+]
+ int num_zeros = fspecs.showpoint ? fspecs.precision - significand_size : 0;
+ size += 1 + to_unsigned(num_zeros > 0 ? num_zeros : 0);
+ return write_padded<align::right>(out, specs, size, [&](iterator it) {
+ if (sign) *it++ = static_cast<Char>(data::signs[sign]);
+ it = write_significand(it, significand, significand_size, exp,
+ decimal_point);
+ return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it;
+ });
+ }
+ // 1234e-6 -> 0.001234
+ int num_zeros = -exp;
+ if (significand_size == 0 && fspecs.precision >= 0 &&
+ fspecs.precision < num_zeros) {
+ num_zeros = fspecs.precision;
+ }
+ bool pointy = num_zeros != 0 || significand_size != 0 || fspecs.showpoint;
+ size += 1 + (pointy ? 1 : 0) + to_unsigned(num_zeros);
+ return write_padded<align::right>(out, specs, size, [&](iterator it) {
+ if (sign) *it++ = static_cast<Char>(data::signs[sign]);
+ *it++ = zero;
+ if (!pointy) return it;
+ *it++ = decimal_point;
+ it = detail::fill_n(it, num_zeros, zero);
+ return write_significand<Char>(it, significand, significand_size);
+ });
+}
+
+template <typename Char, typename OutputIt, typename T,
+ FMT_ENABLE_IF(std::is_floating_point<T>::value)>
+auto write(OutputIt out, T value, basic_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 (std::signbit(value)) { // value < 0 is false for NaN so use signbit.
+ fspecs.sign = sign::minus;
+ value = -value;
+ } else if (fspecs.sign == sign::minus) {
+ fspecs.sign = sign::none;
+ }
+
+ if (!std::isfinite(value))
+ return write_nonfinite(out, std::isinf(value), specs, fspecs);
+
+ if (specs.align == align::numeric && fspecs.sign) {
+ auto it = reserve(out, 1);
+ *it++ = static_cast<Char>(data::signs[fspecs.sign]);
+ out = base_iterator(out, it);
+ fspecs.sign = sign::none;
+ if (specs.width != 0) --specs.width;
+ }
+
+ memory_buffer buffer;
+ if (fspecs.format == float_format::hex) {
+ if (fspecs.sign) buffer.push_back(data::signs[fspecs.sign]);
+ snprintf_float(promote_float(value), specs.precision, fspecs, buffer);
+ return write_bytes<align::right>(out, {buffer.data(), buffer.size()},
+ specs);
+ }
+ int precision = specs.precision >= 0 || !specs.type ? specs.precision : 6;
+ if (fspecs.format == float_format::exp) {
+ if (precision == max_value<int>())
+ FMT_THROW(format_error("number is too big"));
+ else
+ ++precision;
+ }
+ if (const_check(std::is_same<T, float>())) fspecs.binary32 = true;
+ fspecs.use_grisu = is_fast_float<T>();
+ int exp = format_float(promote_float(value), precision, fspecs, buffer);
+ fspecs.precision = precision;
+ Char point =
+ fspecs.locale ? decimal_point<Char>(loc) : static_cast<Char>('.');
+ auto fp = big_decimal_fp{buffer.data(), static_cast<int>(buffer.size()), exp};
+ return write_float(out, fp, specs, fspecs, point);
+}
+
+template <typename Char, typename OutputIt, typename T,
+ FMT_ENABLE_IF(is_fast_float<T>::value)>
+auto write(OutputIt out, T value) -> OutputIt {
+ if (const_check(!is_supported_floating_point(value))) return out;
+
+ using floaty = conditional_t<std::is_same<T, long double>::value, double, T>;
+ using uint = typename dragonbox::float_info<floaty>::carrier_uint;
+ auto bits = bit_cast<uint>(value);
+
+ auto fspecs = float_specs();
+ auto sign_bit = bits & (uint(1) << (num_bits<uint>() - 1));
+ if (sign_bit != 0) {
+ fspecs.sign = sign::minus;
+ value = -value;
+ }
+
+ static const auto specs = basic_format_specs<Char>();
+ uint mask = exponent_mask<floaty>();
+ if ((bits & mask) == mask)
+ return write_nonfinite(out, std::isinf(value), specs, fspecs);
+
+ auto dec = dragonbox::to_decimal(static_cast<floaty>(value));
+ return write_float(out, dec, specs, fspecs, static_cast<Char>('.'));
+}
+
+template <typename Char, typename OutputIt, typename T,
+ FMT_ENABLE_IF(std::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>());
+}
+
+template <typename Char, typename OutputIt>
+auto write(OutputIt out, monostate, basic_format_specs<Char> = {},
+ locale_ref = {}) -> OutputIt {
+ FMT_ASSERT(false, "");
+ return out;
+}
+
+template <typename Char, typename OutputIt>
+FMT_CONSTEXPR auto write(OutputIt out, basic_string_view<Char> value)
+ -> OutputIt {
+ auto it = reserve(out, value.size());
+ it = copy_str_noinline<Char>(value.begin(), value.end(), it);
+ return base_iterator(out, it);
+}
+
+template <typename Char, typename OutputIt, typename T,
+ FMT_ENABLE_IF(is_string<T>::value)>
+constexpr auto write(OutputIt out, const T& value) -> OutputIt {
+ return write<Char>(out, to_string_view(value));
+}
+
+template <typename Char, typename OutputIt, typename T,
+ FMT_ENABLE_IF(is_integral<T>::value &&
+ !std::is_same<T, bool>::value &&
+ !std::is_same<T, Char>::value)>
+FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt {
+ auto abs_value = static_cast<uint32_or_64_or_128_t<T>>(value);
+ bool negative = is_negative(value);
+ // Don't do -abs_value since it trips unsigned-integer-overflow sanitizer.
+ if (negative) abs_value = ~abs_value + 1;
+ int num_digits = count_digits(abs_value);
+ auto size = (negative ? 1 : 0) + static_cast<size_t>(num_digits);
+ auto it = reserve(out, size);
+ if (auto ptr = to_pointer<Char>(it, size)) {
+ if (negative) *ptr++ = static_cast<Char>('-');
+ format_decimal<Char>(ptr, abs_value, num_digits);
+ return out;
+ }
+ if (negative) *it++ = static_cast<Char>('-');
+ it = format_decimal<Char>(it, abs_value, num_digits).end;
+ return base_iterator(out, it);
+}
+
+// FMT_ENABLE_IF() condition separated to workaround MSVC bug
+template <
+ typename Char, typename OutputIt, typename T,
+ bool check =
+ std::is_enum<T>::value && !std::is_same<T, Char>::value &&
+ mapped_type_constant<T, basic_format_context<OutputIt, Char>>::value !=
+ type::custom_type,
+ FMT_ENABLE_IF(check)>
+FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt {
+ return write<Char>(
+ out, static_cast<typename std::underlying_type<T>::type>(value));
+}
+
+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 {
+ return specs.type && specs.type != 's'
+ ? write(out, value ? 1 : 0, specs, {})
+ : write_bytes(out, value ? "true" : "false", specs);
+}
+
+template <typename Char, typename OutputIt>
+FMT_CONSTEXPR auto write(OutputIt out, Char value) -> OutputIt {
+ auto it = reserve(out, 1);
+ *it++ = value;
+ return base_iterator(out, it);
+}
+
+template <typename Char, typename OutputIt>
+FMT_CONSTEXPR_CHAR_TRAITS auto write(OutputIt out, const Char* value)
+ -> OutputIt {
+ if (!value) {
+ FMT_THROW(format_error("string pointer is null"));
+ } else {
+ auto length = std::char_traits<Char>::length(value);
+ out = write(out, basic_string_view<Char>(value, length));
+ }
+ 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());
+ return write_ptr<Char>(out, to_uintptr(value), &specs);
+}
+
+template <typename Char, typename OutputIt, typename T>
+FMT_CONSTEXPR auto write(OutputIt out, const T& value) ->
+ typename std::enable_if<
+ mapped_type_constant<T, basic_format_context<OutputIt, Char>>::value ==
+ type::custom_type,
+ OutputIt>::type {
+ using context_type = basic_format_context<OutputIt, Char>;
+ using formatter_type =
+ conditional_t<has_formatter<T, context_type>::value,
+ typename context_type::template formatter_type<T>,
+ fallback_formatter<T, Char>>;
+ context_type ctx(out, {}, {});
+ return formatter_type().format(value, ctx);
+}
+
+// An argument visitor that formats the argument and writes it via the output
+// iterator. It's a class and not a generic lambda for compatibility with C++11.
+template <typename Char> struct default_arg_formatter {
+ using iterator = buffer_appender<Char>;
+ using context = buffer_context<Char>;
+
+ iterator out;
+ basic_format_args<context> args;
+ locale_ref loc;
+
+ template <typename T> auto operator()(T value) -> iterator {
+ return write<Char>(out, value);
+ }
+ auto operator()(typename basic_format_arg<context>::handle h) -> iterator {
+ basic_format_parse_context<Char> parse_ctx({});
+ context format_ctx(out, args, loc);
+ h.format(parse_ctx, format_ctx);
+ return format_ctx.out();
+ }
+};
+
+template <typename Char> struct arg_formatter {
+ using iterator = buffer_appender<Char>;
+ using context = buffer_context<Char>;
+
+ iterator out;
+ const basic_format_specs<Char>& specs;
+ locale_ref locale;
+
+ template <typename T>
+ FMT_CONSTEXPR FMT_INLINE auto operator()(T value) -> iterator {
+ return detail::write(out, value, specs, locale);
+ }
+ auto operator()(typename basic_format_arg<context>::handle) -> iterator {
+ // User-defined types are handled separately because they require access
+ // to the parse context.
+ return out;
+ }
+};
+
+template <typename Char> struct custom_formatter {
+ basic_format_parse_context<Char>& parse_ctx;
+ buffer_context<Char>& ctx;
+
+ void operator()(
+ typename basic_format_arg<buffer_context<Char>>::handle h) const {
+ h.format(parse_ctx, ctx);
+ }
+ 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) {}
+
+ template <typename T, FMT_ENABLE_IF(is_integer<T>::value)>
+ FMT_CONSTEXPR auto operator()(T value) -> unsigned long long {
+ if (is_negative(value)) handler_.on_error("negative width");
+ return static_cast<unsigned long long>(value);
+ }
+
+ template <typename T, FMT_ENABLE_IF(!is_integer<T>::value)>
+ FMT_CONSTEXPR auto operator()(T) -> unsigned long long {
+ handler_.on_error("width is not integer");
+ return 0;
+ }
+
+ private:
+ ErrorHandler& handler_;
+};
+
+template <typename ErrorHandler> class precision_checker {
+ public:
+ explicit FMT_CONSTEXPR precision_checker(ErrorHandler& eh) : handler_(eh) {}
+
+ template <typename T, FMT_ENABLE_IF(is_integer<T>::value)>
+ FMT_CONSTEXPR auto operator()(T value) -> unsigned long long {
+ if (is_negative(value)) handler_.on_error("negative precision");
+ return static_cast<unsigned long long>(value);
+ }
+
+ template <typename T, FMT_ENABLE_IF(!is_integer<T>::value)>
+ FMT_CONSTEXPR auto operator()(T) -> unsigned long long {
+ handler_.on_error("precision is not integer");
+ return 0;
+ }
+
+ private:
+ ErrorHandler& handler_;
+};
+
+template <template <typename> class Handler, typename FormatArg,
+ typename ErrorHandler>
+FMT_CONSTEXPR auto get_dynamic_spec(FormatArg arg, ErrorHandler eh) -> int {
+ unsigned long long value = visit_format_arg(Handler<ErrorHandler>(eh), arg);
+ if (value > to_unsigned(max_value<int>())) eh.on_error("number is too big");
+ return static_cast<int>(value);
+}
+
+template <typename Context, typename ID>
+FMT_CONSTEXPR auto get_arg(Context& ctx, ID id) ->
+ typename Context::format_arg {
+ 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,
+ Context& ctx) {
+ switch (ref.kind) {
+ case arg_id_kind::none:
+ break;
+ case arg_id_kind::index:
+ value = detail::get_dynamic_spec<Handler>(ctx.arg(ref.val.index),
+ ctx.error_handler());
+ break;
+ case arg_id_kind::name:
+ value = detail::get_dynamic_spec<Handler>(ctx.arg(ref.val.name),
+ ctx.error_handler());
+ break;
+ }
+}
+
+#define FMT_STRING_IMPL(s, base, explicit) \
+ [] { \
+ /* 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 { \
+ using char_type = fmt::remove_cvref_t<decltype(s[0])>; \
+ FMT_MAYBE_UNUSED FMT_CONSTEXPR explicit \
+ operator fmt::basic_string_view<char_type>() const { \
+ return fmt::detail_exported::compile_string_to_view<char_type>(s); \
+ } \
+ }; \
+ return FMT_COMPILE_STRING(); \
+ }()
+
+/**
+ \rst
+ Constructs a compile-time format string from a string literal *s*.
+
+ **Example**::
+
+ // A compile-time error because 'd' is an invalid specifier for strings.
+ std::string s = fmt::format(FMT_STRING("{:d}"), "foo");
+ \endrst
+ */
+#define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::compile_string, )
+
+#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_args_checked<T...>(str, args...));
+ }
+};
+
+# if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
+template <typename T, typename Char, size_t N,
+ fmt::detail_exported::fixed_string<Char, N> Str>
+struct statically_named_arg : view {
+ static constexpr auto name = Str.data;
+
+ const T& value;
+ statically_named_arg(const T& v) : value(v) {}
+};
+
+template <typename T, typename Char, size_t N,
+ fmt::detail_exported::fixed_string<Char, N> Str>
+struct is_named_arg<statically_named_arg<T, Char, N, Str>> : std::true_type {};
+
+template <typename T, typename Char, size_t N,
+ fmt::detail_exported::fixed_string<Char, N> Str>
+struct is_statically_named_arg<statically_named_arg<T, Char, N, Str>>
+ : std::true_type {};
+
+template <typename Char, size_t N,
+ fmt::detail_exported::fixed_string<Char, N> Str>
+struct udl_arg {
+ template <typename T> auto operator=(T&& value) const {
+ return statically_named_arg<T, Char, N, Str>(std::forward<T>(value));
+ }
+};
+# else
+template <typename Char> struct udl_arg {
+ const Char* str;
+
+ template <typename T> auto operator=(T&& value) const -> named_arg<Char, T> {
+ return {str, std::forward<T>(value)};
+ }
+};
+# endif
+#endif // FMT_USE_USER_DEFINED_LITERALS
+
+template <typename Locale, typename Char>
+auto vformat(const Locale& loc, 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, detail::locale_ref(loc));
+ return {buffer.data(), buffer.size()};
+}
+
+using format_func = void (*)(detail::buffer<char>&, int, const char*);
+
+FMT_API void format_error_code(buffer<char>& out, int error_code,
+ string_view message) FMT_NOEXCEPT;
+
+FMT_API void report_error(format_func func, int error_code,
+ const char* message) FMT_NOEXCEPT;
+FMT_END_DETAIL_NAMESPACE
+
+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...)``.
+ *error_code* is a system error code as given by ``errno``.
+
+ **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
+*/
+template <typename... T>
+auto system_error(int error_code, format_string<T...> fmt, T&&... args)
+ -> std::system_error {
+ return vsystem_error(error_code, fmt, fmt::make_format_args(args...));
+}
+
+/**
+ \rst
+ Formats an error message for an error returned by an operating system or a
+ language runtime, for example a file opening error, and writes it to *out*.
+ The format is the same as the one used by ``std::system_error(ec, message)``
+ where ``ec`` is ``std::error_code(error_code, std::generic_category()})``.
+ It is implementation-defined but normally looks like:
+
+ .. parsed-literal::
+ *<message>*: *<system-message>*
+
+ where *<message>* is the passed message and *<system-message>* is the system
+ message corresponding to the error code.
+ *error_code* is a system error code as given by ``errno``.
+ \endrst
+ */
+FMT_API void format_system_error(detail::buffer<char>& out, int error_code,
+ const char* message) FMT_NOEXCEPT;
+
+// Reports a system error without throwing an exception.
+// Can be used to report errors from destructors.
+FMT_API void report_system_error(int error_code,
+ const char* message) FMT_NOEXCEPT;
+
+/** Fast integer formatter. */
+class format_int {
+ private:
+ // Buffer should be large enough to hold all digits (digits10 + 1),
+ // a sign and a null character.
+ enum { buffer_size = std::numeric_limits<unsigned long long>::digits10 + 3 };
+ mutable char buffer_[buffer_size];
+ char* str_;
+
+ template <typename UInt> auto format_unsigned(UInt value) -> char* {
+ auto n = static_cast<detail::uint32_or_64_or_128_t<UInt>>(value);
+ return detail::format_decimal(buffer_, n, buffer_size - 1).begin;
+ }
+
+ template <typename Int> auto format_signed(Int value) -> char* {
+ auto abs_value = static_cast<detail::uint32_or_64_or_128_t<Int>>(value);
+ bool negative = value < 0;
+ if (negative) abs_value = 0 - abs_value;
+ auto begin = format_unsigned(abs_value);
+ if (negative) *--begin = '-';
+ return begin;
+ }
+
+ public:
+ explicit format_int(int value) : str_(format_signed(value)) {}
+ explicit format_int(long value) : str_(format_signed(value)) {}
+ explicit format_int(long long value) : str_(format_signed(value)) {}
+ explicit format_int(unsigned value) : str_(format_unsigned(value)) {}
+ explicit format_int(unsigned long value) : str_(format_unsigned(value)) {}
+ explicit format_int(unsigned long long value)
+ : str_(format_unsigned(value)) {}
+
+ /** Returns the number of characters written to the output buffer. */
+ auto size() const -> size_t {
+ return detail::to_unsigned(buffer_ - str_ + buffer_size - 1);
+ }
+
+ /**
+ Returns a pointer to the output buffer content. No terminating null
+ character is appended.
+ */
+ auto data() const -> const char* { return str_; }
+
+ /**
+ Returns a pointer to the output buffer content with terminating null
+ character appended.
+ */
+ auto c_str() const -> const char* {
+ buffer_[buffer_size - 1] = '\0';
+ return str_;
+ }
+
+ /**
+ \rst
+ Returns the content of the output buffer as an ``std::string``.
+ \endrst
+ */
+ auto str() const -> std::string { return std::string(str_, size()); }
+};
+
+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());
+}
+
+#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 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);
+ }
+};
+
+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());
+ }
+};
+
+/**
+ \rst
+ Converts ``p`` to ``const void*`` for pointer formatting.
+
+ **Example**::
+
+ auto s = fmt::format("{}", fmt::ptr(p));
+ \endrst
+ */
+template <typename T> auto ptr(T p) -> const void* {
+ 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* {
+ return p.get();
+}
+template <typename T> auto ptr(const std::shared_ptr<T>& p) -> const void* {
+ return p.get();
+}
+
+class bytes {
+ private:
+ string_view data_;
+ friend struct formatter<bytes>;
+
+ public:
+ explicit bytes(string_view data) : data_(data) {}
+};
+
+template <> struct formatter<bytes> {
+ private:
+ detail::dynamic_format_specs<char> 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;
+ }
+
+ template <typename FormatContext>
+ auto format(bytes b, FormatContext& ctx) -> decltype(ctx.out()) {
+ 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_bytes(ctx.out(), b.data_, specs_);
+ }
+};
+
+template <typename It, typename Sentinel, typename Char = char>
+struct join_view : detail::view {
+ It begin;
+ Sentinel end;
+ basic_string_view<Char> sep;
+
+ join_view(It b, Sentinel e, basic_string_view<Char> s)
+ : begin(b), end(e), sep(s) {}
+};
+
+template <typename It, typename Sentinel, typename Char>
+using arg_join FMT_DEPRECATED_ALIAS = join_view<It, Sentinel, Char>;
+
+template <typename It, typename Sentinel, typename Char>
+struct formatter<join_view<It, Sentinel, Char>, Char> {
+ private:
+ using value_type = typename std::iterator_traits<It>::value_type;
+ 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_;
+
+ public:
+ template <typename ParseContext>
+ FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
+ return value_formatter_.parse(ctx);
+ }
+
+ template <typename FormatContext>
+ auto format(const join_view<It, Sentinel, Char>& value, FormatContext& ctx)
+ -> decltype(ctx.out()) {
+ auto it = value.begin;
+ auto out = ctx.out();
+ if (it != value.end) {
+ out = value_formatter_.format(map(*it++), ctx);
+ 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);
+ }
+ }
+ return out;
+ }
+};
+
+/**
+ Returns an object that formats the iterator range `[begin, end)` with
+ elements separated by `sep`.
+ */
+template <typename It, typename Sentinel>
+auto join(It begin, Sentinel end, string_view sep) -> join_view<It, Sentinel> {
+ return {begin, end, sep};
+}
+
+/**
+ \rst
+ Returns an object that formats `range` with elements separated by `sep`.
+
+ **Example**::
+
+ std::vector<int> v = {1, 2, 3};
+ fmt::print("{}", fmt::join(v, ", "));
+ // Output: "1, 2, 3"
+
+ ``fmt::join`` applies passed format specifiers to the range elements::
+
+ fmt::print("{:02}", fmt::join(v, ", "));
+ // Output: "01, 02, 03"
+ \endrst
+ */
+template <typename Range>
+auto join(Range&& range, string_view sep)
+ -> join_view<detail::iterator_t<Range>, detail::sentinel_t<Range>> {
+ return join(std::begin(range), std::end(range), sep);
+}
+
+/**
+ \rst
+ Converts *value* to ``std::string`` using the default format for type *T*.
+
+ **Example**::
+
+ #include <fmt/format.h>
+
+ std::string answer = fmt::to_string(42);
+ \endrst
+ */
+template <typename T, FMT_ENABLE_IF(!std::is_integral<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;
+}
+
+template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
+inline auto to_string(T value) -> std::string {
+ // The buffer should be large enough to store the number including the sign
+ // or "false" for bool.
+ constexpr int max_size = detail::digits10<T>() + 2;
+ char buffer[max_size > 5 ? static_cast<unsigned>(max_size) : 5];
+ char* begin = buffer;
+ return std::string(begin, detail::write<char>(begin, value));
+}
+
+template <typename Char, size_t SIZE>
+auto to_string(const basic_memory_buffer<Char, SIZE>& buf)
+ -> std::basic_string<Char> {
+ auto size = buf.size();
+ detail::assume(size < std::basic_string<Char>().max_size());
+ return std::basic_string<Char>(buf.data(), size);
+}
+
+FMT_BEGIN_DETAIL_NAMESPACE
+
+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;
+ auto out = buffer_appender<Char>(buf);
+ if (fmt.size() == 2 && equal2(fmt.data(), "{}")) {
+ auto arg = args.get(0);
+ if (!arg) error_handler().on_error("argument not found");
+ visit_format_arg(default_arg_formatter<Char>{out, args, loc}, arg);
+ return;
+ }
+
+ struct format_handler : error_handler {
+ basic_format_parse_context<Char> parse_context;
+ buffer_context<Char> context;
+
+ format_handler(buffer_appender<Char> out, basic_string_view<Char> str,
+ basic_format_args<buffer_context<Char>> args, locale_ref loc)
+ : parse_context(str), context(out, args, loc) {}
+
+ void on_text(const Char* begin, const Char* end) {
+ auto text = basic_string_view<Char>(begin, to_unsigned(end - begin));
+ context.advance_to(write<Char>(context.out(), text));
+ }
+
+ FMT_CONSTEXPR auto on_arg_id() -> int {
+ return parse_context.next_arg_id();
+ }
+ FMT_CONSTEXPR auto on_arg_id(int id) -> int {
+ return parse_context.check_arg_id(id), id;
+ }
+ FMT_CONSTEXPR auto on_arg_id(basic_string_view<Char> id) -> int {
+ int arg_id = context.arg_id(id);
+ if (arg_id < 0) on_error("argument not found");
+ return arg_id;
+ }
+
+ FMT_INLINE void on_replacement_field(int id, const Char*) {
+ auto arg = get_arg(context, id);
+ context.advance_to(visit_format_arg(
+ default_arg_formatter<Char>{context.out(), context.args(),
+ context.locale()},
+ arg));
+ }
+
+ auto on_format_specs(int id, const Char* begin, const Char* end)
+ -> 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()));
+ 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);
+ if (begin == end || *begin != '}')
+ on_error("missing '}' in format string");
+ auto f = arg_formatter<Char>{context.out(), specs, context.locale()};
+ context.advance_to(visit_format_arg(f, arg));
+ return begin;
+ }
+ };
+ detail::parse_format_string<false>(fmt, format_handler(out, fmt, args, loc));
+}
+
+#ifndef FMT_HEADER_ONLY
+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)
+ -> thousands_sep_result<wchar_t>;
+extern template FMT_API auto decimal_point_impl(locale_ref) -> char;
+extern template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t;
+extern template auto format_float<double>(double value, int precision,
+ float_specs specs, buffer<char>& buf)
+ -> int;
+extern template auto format_float<long double>(long double value, int precision,
+ float_specs specs,
+ buffer<char>& buf) -> int;
+void snprintf_float(float, int, float_specs, buffer<char>&) = delete;
+extern template auto snprintf_float<double>(double value, int precision,
+ float_specs specs,
+ buffer<char>& buf) -> int;
+extern template auto snprintf_float<long double>(long double value,
+ int precision,
+ float_specs specs,
+ buffer<char>& buf) -> int;
+#endif // FMT_HEADER_ONLY
+
+FMT_END_DETAIL_NAMESPACE
+
+#if FMT_USE_USER_DEFINED_LITERALS
+inline namespace literals {
+/**
+ \rst
+ User-defined literal equivalent of :func:`fmt::arg`.
+
+ **Example**::
+
+ using namespace fmt::literals;
+ fmt::print("Elapsed time: {s:.2f} seconds", "s"_a=1.23);
+ \endrst
+ */
+# if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
+template <detail_exported::fixed_string Str>
+constexpr auto operator""_a()
+ -> detail::udl_arg<remove_cvref_t<decltype(Str.data[0])>,
+ sizeof(Str.data) / sizeof(decltype(Str.data[0])), Str> {
+ return {};
+}
+# else
+constexpr auto operator"" _a(const char* s, size_t) -> detail::udl_arg<char> {
+ return {s};
+}
+# endif
+
+/**
+ \rst
+ User-defined literal equivalent of :func:`fmt::format`.
+
+ **Example**::
+
+ using namespace fmt::literals;
+ std::string message = "The answer is {}"_format(42);
+ \endrst
+ */
+constexpr auto operator"" _format(const char* s, size_t n)
+ -> detail::udl_formatter<char> {
+ return {{s, n}};
+}
+} // namespace literals
+#endif // FMT_USE_USER_DEFINED_LITERALS
+
+template <typename Locale, FMT_ENABLE_IF(detail::is_locale<Locale>::value)>
+inline auto vformat(const Locale& loc, string_view fmt, format_args args)
+ -> std::string {
+ return detail::vformat(loc, fmt, args);
+}
+
+template <typename Locale, typename... T,
+ 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...));
+}
+
+template <typename... T, size_t SIZE, typename Allocator>
+FMT_DEPRECATED auto format_to(basic_memory_buffer<char, SIZE, Allocator>& buf,
+ format_string<T...> fmt, T&&... args)
+ -> appender {
+ detail::vformat_to(buf, string_view(fmt), fmt::make_format_args(args...));
+ return appender(buf);
+}
+
+template <typename OutputIt, typename Locale,
+ FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value&&
+ detail::is_locale<Locale>::value)>
+auto vformat_to(OutputIt out, const Locale& loc, string_view fmt,
+ format_args args) -> OutputIt {
+ 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);
+}
+
+template <typename OutputIt, typename Locale, typename... T,
+ FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value&&
+ detail::is_locale<Locale>::value)>
+FMT_INLINE auto format_to(OutputIt out, const Locale& loc,
+ format_string<T...> fmt, T&&... args) -> OutputIt {
+ return vformat_to(out, loc, fmt, fmt::make_format_args(args...));
+}
+
+FMT_MODULE_EXPORT_END
+FMT_END_NAMESPACE
+
+#ifdef FMT_DEPRECATED_INCLUDE_XCHAR
+# include "xchar.h"
+#endif
+
+#ifdef FMT_HEADER_ONLY
+# define FMT_FUNC inline
+# include "format-inl.h"
+#else
+# define FMT_FUNC
+#endif
+
+#endif // FMT_FORMAT_H_
diff --git a/wpiutil/src/main/native/fmtlib/include/fmt/locale.h b/wpiutil/src/main/native/fmtlib/include/fmt/locale.h
new file mode 100644
index 0000000..7571b52
--- /dev/null
+++ b/wpiutil/src/main/native/fmtlib/include/fmt/locale.h
@@ -0,0 +1,2 @@
+#include "xchar.h"
+#warning fmt/locale.h is deprecated, include fmt/format.h or fmt/xchar.h instead
diff --git a/wpiutil/src/main/native/fmtlib/include/fmt/os.h b/wpiutil/src/main/native/fmtlib/include/fmt/os.h
new file mode 100644
index 0000000..f6c0f32
--- /dev/null
+++ b/wpiutil/src/main/native/fmtlib/include/fmt/os.h
@@ -0,0 +1,515 @@
+// Formatting library for C++ - optional OS-specific functionality
+//
+// Copyright (c) 2012 - present, Victor Zverovich
+// All rights reserved.
+//
+// For the license information refer to format.h.
+
+#ifndef FMT_OS_H_
+#define FMT_OS_H_
+
+#include <cerrno>
+#include <clocale> // locale_t
+#include <cstddef>
+#include <cstdio>
+#include <cstdlib> // strtod_l
+#include <system_error> // std::system_error
+
+#if defined __APPLE__ || defined(__FreeBSD__)
+# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
+#endif
+
+#include "format.h"
+
+// UWP doesn't provide _pipe.
+#if FMT_HAS_INCLUDE("winapifamily.h")
+# include <winapifamily.h>
+#endif
+#if (FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__) || \
+ defined(__linux__)) && \
+ (!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
+# include <fcntl.h> // for O_RDONLY
+# define FMT_USE_FCNTL 1
+#else
+# define FMT_USE_FCNTL 0
+#endif
+
+#ifndef FMT_POSIX
+# if defined(_WIN32) && !defined(__MINGW32__)
+// Fix warnings about deprecated symbols.
+# define FMT_POSIX(call) _##call
+# else
+# define FMT_POSIX(call) call
+# endif
+#endif
+
+// Calls to system functions are wrapped in FMT_SYSTEM for testability.
+#ifdef FMT_SYSTEM
+# define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
+#else
+# define FMT_SYSTEM(call) ::call
+# ifdef _WIN32
+// Fix warnings about deprecated symbols.
+# define FMT_POSIX_CALL(call) ::_##call
+# else
+# define FMT_POSIX_CALL(call) ::call
+# endif
+#endif
+
+// Retries the expression while it evaluates to error_result and errno
+// equals to EINTR.
+#ifndef _WIN32
+# define FMT_RETRY_VAL(result, expression, error_result) \
+ do { \
+ (result) = (expression); \
+ } while ((result) == (error_result) && errno == EINTR)
+#else
+# define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
+#endif
+
+#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
+
+FMT_BEGIN_NAMESPACE
+FMT_MODULE_EXPORT_BEGIN
+
+/**
+ \rst
+ A reference to a null-terminated string. It can be constructed from a C
+ string or ``std::string``.
+
+ You can use one of the following type aliases for common character types:
+
+ +---------------+-----------------------------+
+ | Type | Definition |
+ +===============+=============================+
+ | cstring_view | basic_cstring_view<char> |
+ +---------------+-----------------------------+
+ | wcstring_view | basic_cstring_view<wchar_t> |
+ +---------------+-----------------------------+
+
+ This class is most useful as a parameter type to allow passing
+ different types of strings to a function, for example::
+
+ template <typename... Args>
+ std::string format(cstring_view format_str, const Args & ... args);
+
+ format("{}", 42);
+ format(std::string("{}"), 42);
+ \endrst
+ */
+template <typename Char> class basic_cstring_view {
+ private:
+ const Char* data_;
+
+ public:
+ /** Constructs a string reference object from a C string. */
+ basic_cstring_view(const Char* s) : data_(s) {}
+
+ /**
+ \rst
+ Constructs a string reference from an ``std::string`` object.
+ \endrst
+ */
+ basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {}
+
+ /** Returns the pointer to a C string. */
+ const Char* c_str() const { return data_; }
+};
+
+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() FMT_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);
+};
+
+FMT_API void format_windows_error(buffer<char>& out, int error_code,
+ const char* message) FMT_NOEXCEPT;
+FMT_END_DETAIL_NAMESPACE
+
+FMT_API std::system_error vwindows_error(int error_code, string_view format_str,
+ format_args args);
+
+/**
+ \rst
+ Constructs a :class:`std::system_error` object with the description
+ of the form
+
+ .. parsed-literal::
+ *<message>*: *<system-message>*
+
+ where *<message>* is the formatted message and *<system-message>* is the
+ system message corresponding to the error code.
+ *error_code* is a Windows error code as given by ``GetLastError``.
+ If *error_code* is not a valid error code such as -1, the system message
+ will look like "error -1".
+
+ **Example**::
+
+ // This throws a system_error with the description
+ // cannot open file 'madeup': The system cannot find the file specified.
+ // or similar (system message may vary).
+ const char *filename = "madeup";
+ LPOFSTRUCT of = LPOFSTRUCT();
+ HFILE file = OpenFile(filename, &of, OF_READ);
+ if (file == HFILE_ERROR) {
+ throw fmt::windows_error(GetLastError(),
+ "cannot open file '{}'", filename);
+ }
+ \endrst
+*/
+template <typename... Args>
+std::system_error windows_error(int error_code, string_view message,
+ const Args&... args) {
+ return vwindows_error(error_code, message, fmt::make_format_args(args...));
+}
+
+// Reports a Windows error without throwing an exception.
+// Can be used to report errors from destructors.
+FMT_API void report_windows_error(int error_code,
+ const char* message) FMT_NOEXCEPT;
+#else
+inline const std::error_category& system_category() FMT_NOEXCEPT {
+ return std::system_category();
+}
+#endif // _WIN32
+
+// std::system is not available on some platforms such as iOS (#2248).
+#ifdef __OSX__
+template <typename S, typename... Args, typename Char = char_t<S>>
+void say(const S& format_str, Args&&... args) {
+ std::system(format("say \"{}\"", format(format_str, args...)).c_str());
+}
+#endif
+
+// A buffered file.
+class buffered_file {
+ private:
+ FILE* file_;
+
+ friend class file;
+
+ explicit buffered_file(FILE* f) : file_(f) {}
+
+ public:
+ buffered_file(const buffered_file&) = delete;
+ void operator=(const buffered_file&) = delete;
+
+ // Constructs a buffered_file object which doesn't represent any file.
+ buffered_file() FMT_NOEXCEPT : file_(nullptr) {}
+
+ // Destroys the object closing the file it represents if any.
+ FMT_API ~buffered_file() FMT_NOEXCEPT;
+
+ public:
+ buffered_file(buffered_file&& other) FMT_NOEXCEPT : file_(other.file_) {
+ other.file_ = nullptr;
+ }
+
+ buffered_file& operator=(buffered_file&& other) {
+ close();
+ file_ = other.file_;
+ other.file_ = nullptr;
+ return *this;
+ }
+
+ // Opens a file.
+ FMT_API buffered_file(cstring_view filename, cstring_view mode);
+
+ // Closes the file.
+ FMT_API void close();
+
+ // Returns the pointer to a FILE object representing this file.
+ FILE* get() const FMT_NOEXCEPT { return file_; }
+
+ // We place parentheses around fileno to workaround a bug in some versions
+ // of MinGW that define fileno as a macro.
+ FMT_API int(fileno)() const;
+
+ void vprint(string_view format_str, format_args args) {
+ fmt::vprint(file_, format_str, args);
+ }
+
+ template <typename... Args>
+ inline void print(string_view format_str, const Args&... args) {
+ vprint(format_str, fmt::make_format_args(args...));
+ }
+};
+
+#if FMT_USE_FCNTL
+// A file. Closed file is represented by a file object with descriptor -1.
+// Methods that are not declared with FMT_NOEXCEPT may throw
+// fmt::system_error in case of failure. Note that some errors such as
+// closing the file multiple times will cause a crash on Windows rather
+// than an exception. You can get standard behavior by overriding the
+// invalid parameter handler with _set_invalid_parameter_handler.
+class file {
+ private:
+ int fd_; // File descriptor.
+
+ // Constructs a file object with a given descriptor.
+ explicit file(int fd) : fd_(fd) {}
+
+ public:
+ // Possible values for the oflag argument to the constructor.
+ enum {
+ RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
+ WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
+ RDWR = FMT_POSIX(O_RDWR), // Open for reading and writing.
+ CREATE = FMT_POSIX(O_CREAT), // Create if the file doesn't exist.
+ APPEND = FMT_POSIX(O_APPEND), // Open in append mode.
+ TRUNC = FMT_POSIX(O_TRUNC) // Truncate the content of the file.
+ };
+
+ // Constructs a file object which doesn't represent any file.
+ file() FMT_NOEXCEPT : fd_(-1) {}
+
+ // Opens a file and constructs a file object representing this file.
+ FMT_API file(cstring_view path, int oflag);
+
+ public:
+ file(const file&) = delete;
+ void operator=(const file&) = delete;
+
+ file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; }
+
+ // Move assignment is not noexcept because close may throw.
+ file& operator=(file&& other) {
+ close();
+ fd_ = other.fd_;
+ other.fd_ = -1;
+ return *this;
+ }
+
+ // Destroys the object closing the file it represents if any.
+ FMT_API ~file() FMT_NOEXCEPT;
+
+ // Returns the file descriptor.
+ int descriptor() const FMT_NOEXCEPT { return fd_; }
+
+ // Closes the file.
+ FMT_API void close();
+
+ // Returns the file size. The size has signed type for consistency with
+ // stat::st_size.
+ FMT_API long long size() const;
+
+ // Attempts to read count bytes from the file into the specified buffer.
+ FMT_API size_t read(void* buffer, size_t count);
+
+ // Attempts to write count bytes from the specified buffer to the file.
+ FMT_API size_t write(const void* buffer, size_t count);
+
+ // Duplicates a file descriptor with the dup function and returns
+ // the duplicate as a file object.
+ FMT_API static file dup(int fd);
+
+ // Makes fd be the copy of this file descriptor, closing fd first if
+ // necessary.
+ FMT_API void dup2(int fd);
+
+ // Makes fd be the copy of this file descriptor, closing fd first if
+ // necessary.
+ FMT_API void dup2(int fd, std::error_code& ec) FMT_NOEXCEPT;
+
+ // Creates a pipe setting up read_end and write_end file objects for reading
+ // and writing respectively.
+ FMT_API static void pipe(file& read_end, file& write_end);
+
+ // Creates a buffered_file object associated with this file and detaches
+ // this file object from the file.
+ FMT_API buffered_file fdopen(const char* mode);
+};
+
+// Returns the memory page size.
+long getpagesize();
+
+FMT_BEGIN_DETAIL_NAMESPACE
+
+struct buffer_size {
+ buffer_size() = default;
+ size_t value = 0;
+ buffer_size operator=(size_t val) const {
+ auto bs = buffer_size();
+ bs.value = val;
+ return bs;
+ }
+};
+
+struct ostream_params {
+ int oflag = file::WRONLY | file::CREATE | file::TRUNC;
+ size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768;
+
+ ostream_params() {}
+
+ template <typename... T>
+ ostream_params(T... params, int new_oflag) : ostream_params(params...) {
+ oflag = new_oflag;
+ }
+
+ template <typename... T>
+ ostream_params(T... params, detail::buffer_size bs)
+ : ostream_params(params...) {
+ this->buffer_size = bs.value;
+ }
+};
+
+FMT_END_DETAIL_NAMESPACE
+
+constexpr detail::buffer_size buffer_size;
+
+/** A fast output stream which is not thread-safe. */
+class FMT_API ostream final : private detail::buffer<char> {
+ private:
+ file file_;
+
+ void flush() {
+ if (size() == 0) return;
+ file_.write(data(), size());
+ clear();
+ }
+
+ void grow(size_t) override;
+
+ ostream(cstring_view path, const detail::ostream_params& params)
+ : file_(path, params.oflag) {
+ set(new char[params.buffer_size], params.buffer_size);
+ }
+
+ 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();
+ }
+
+ template <typename... T>
+ friend ostream output_file(cstring_view path, T... params);
+
+ void close() {
+ flush();
+ file_.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,
+ fmt::make_format_args(args...));
+ }
+};
+
+/**
+ \rst
+ Opens a file for writing. Supported parameters passed in *params*:
+
+ * ``<integer>``: Flags passed to `open
+ <https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html>`_
+ (``file::WRONLY | file::CREATE`` by default)
+ * ``buffer_size=<integer>``: Output buffer size
+
+ **Example**::
+
+ auto out = fmt::output_file("guide.txt");
+ out.print("Don't {}", "Panic");
+ \endrst
+ */
+template <typename... T>
+inline ostream output_file(cstring_view path, T... params) {
+ return {path, detail::ostream_params(params...)};
+}
+#endif // FMT_USE_FCNTL
+
+#ifdef FMT_LOCALE
+// A "C" numeric locale.
+class locale {
+ private:
+# ifdef _WIN32
+ using locale_t = _locale_t;
+
+ static void freelocale(locale_t loc) { _free_locale(loc); }
+
+ static double strtod_l(const char* nptr, char** endptr, _locale_t loc) {
+ return _strtod_l(nptr, endptr, loc);
+ }
+# endif
+
+ locale_t locale_;
+
+ public:
+ using type = locale_t;
+ locale(const locale&) = delete;
+ void operator=(const locale&) = delete;
+
+ locale() {
+# ifndef _WIN32
+ locale_ = FMT_SYSTEM(newlocale(LC_NUMERIC_MASK, "C", nullptr));
+# else
+ locale_ = _create_locale(LC_NUMERIC, "C");
+# endif
+ if (!locale_) FMT_THROW(system_error(errno, "cannot create locale"));
+ }
+ ~locale() { freelocale(locale_); }
+
+ type get() const { return locale_; }
+
+ // Converts string to floating-point number and advances str past the end
+ // of the parsed input.
+ double strtod(const char*& str) const {
+ char* end = nullptr;
+ double result = strtod_l(str, &end, locale_);
+ str = end;
+ return result;
+ }
+};
+using Locale FMT_DEPRECATED_ALIAS = locale;
+#endif // FMT_LOCALE
+FMT_MODULE_EXPORT_END
+FMT_END_NAMESPACE
+
+#endif // FMT_OS_H_
diff --git a/wpiutil/src/main/native/fmtlib/include/fmt/ostream.h b/wpiutil/src/main/native/fmtlib/include/fmt/ostream.h
new file mode 100644
index 0000000..d66248a
--- /dev/null
+++ b/wpiutil/src/main/native/fmtlib/include/fmt/ostream.h
@@ -0,0 +1,181 @@
+// Formatting library for C++ - std::ostream support
+//
+// Copyright (c) 2012 - present, Victor Zverovich
+// All rights reserved.
+//
+// For the license information refer to format.h.
+
+#ifndef FMT_OSTREAM_H_
+#define FMT_OSTREAM_H_
+
+#include <ostream>
+
+#include "format.h"
+
+FMT_BEGIN_NAMESPACE
+
+template <typename Char> class basic_printf_parse_context;
+template <typename OutputIt, typename Char> class basic_printf_context;
+
+namespace detail {
+
+template <class Char> class formatbuf : public std::basic_streambuf<Char> {
+ private:
+ using int_type = typename std::basic_streambuf<Char>::int_type;
+ using traits_type = typename std::basic_streambuf<Char>::traits_type;
+
+ buffer<Char>& buffer_;
+
+ public:
+ formatbuf(buffer<Char>& buf) : buffer_(buf) {}
+
+ protected:
+ // The put-area is actually always empty. This makes the implementation
+ // simpler and has the advantage that the streambuf and the buffer are always
+ // in sync and sputc never writes into uninitialized memory. The obvious
+ // disadvantage is that each call to sputc always results in a (virtual) call
+ // to overflow. There is no disadvantage here for sputn since this always
+ // results in a call to xsputn.
+
+ int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE {
+ if (!traits_type::eq_int_type(ch, traits_type::eof()))
+ buffer_.push_back(static_cast<Char>(ch));
+ return ch;
+ }
+
+ std::streamsize xsputn(const Char* s, std::streamsize count) FMT_OVERRIDE {
+ buffer_.append(s, s + count);
+ return count;
+ }
+};
+
+struct converter {
+ template <typename T, FMT_ENABLE_IF(is_integral<T>::value)> converter(T);
+};
+
+template <typename Char> struct test_stream : std::basic_ostream<Char> {
+ private:
+ void_t<> operator<<(converter);
+};
+
+// Hide insertion operators for built-in types.
+template <typename Char, typename Traits>
+void_t<> operator<<(std::basic_ostream<Char, Traits>&, Char);
+template <typename Char, typename Traits>
+void_t<> operator<<(std::basic_ostream<Char, Traits>&, char);
+template <typename Traits>
+void_t<> operator<<(std::basic_ostream<char, Traits>&, char);
+template <typename Traits>
+void_t<> operator<<(std::basic_ostream<char, Traits>&, signed char);
+template <typename Traits>
+void_t<> operator<<(std::basic_ostream<char, Traits>&, unsigned char);
+
+// Checks if T has a user-defined operator<< (e.g. not a member of
+// std::ostream).
+template <typename T, typename Char> class is_streamable {
+ private:
+ template <typename U>
+ static bool_constant<!std::is_same<decltype(std::declval<test_stream<Char>&>()
+ << std::declval<U>()),
+ void_t<>>::value>
+ test(int);
+
+ template <typename> static std::false_type test(...);
+
+ using result = decltype(test<T>(0));
+
+ public:
+ is_streamable() = default;
+
+ static const bool value = result::value;
+};
+
+// Write the content of buf to os.
+template <typename Char>
+void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
+ const Char* buf_data = buf.data();
+ using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
+ unsigned_streamsize size = buf.size();
+ unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>());
+ do {
+ unsigned_streamsize n = size <= max_size ? size : max_size;
+ os.write(buf_data, static_cast<std::streamsize>(n));
+ buf_data += n;
+ size -= n;
+ } while (size != 0);
+}
+
+template <typename Char, typename T>
+void format_value(buffer<Char>& buf, const T& value,
+ locale_ref loc = locale_ref()) {
+ formatbuf<Char> format_buf(buf);
+ std::basic_ostream<Char> output(&format_buf);
+#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
+ if (loc) output.imbue(loc.get<std::locale>());
+#endif
+ output << value;
+ output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
+ buf.try_resize(buf.size());
+}
+
+// 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>>
+ : private formatter<basic_string_view<Char>, Char> {
+ FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
+ -> decltype(ctx.begin()) {
+ return formatter<basic_string_view<Char>, Char>::parse(ctx);
+ }
+ template <typename ParseCtx,
+ FMT_ENABLE_IF(std::is_same<
+ ParseCtx, basic_printf_parse_context<Char>>::value)>
+ auto parse(ParseCtx& ctx) -> decltype(ctx.begin()) {
+ return ctx.begin();
+ }
+
+ template <typename OutputIt>
+ auto format(const T& value, basic_format_context<OutputIt, Char>& ctx)
+ -> OutputIt {
+ basic_memory_buffer<Char> buffer;
+ format_value(buffer, value, ctx.locale());
+ basic_string_view<Char> str(buffer.data(), buffer.size());
+ return formatter<basic_string_view<Char>, Char>::format(str, ctx);
+ }
+ template <typename OutputIt>
+ auto format(const T& value, basic_printf_context<OutputIt, Char>& ctx)
+ -> OutputIt {
+ basic_memory_buffer<Char> buffer;
+ format_value(buffer, value, ctx.locale());
+ return std::copy(buffer.begin(), buffer.end(), ctx.out());
+ }
+};
+} // namespace detail
+
+FMT_MODULE_EXPORT
+template <typename Char>
+void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
+ basic_format_args<buffer_context<type_identity_t<Char>>> args) {
+ basic_memory_buffer<Char> buffer;
+ detail::vformat_to(buffer, format_str, args);
+ detail::write_buffer(os, buffer);
+}
+
+/**
+ \rst
+ Prints formatted data to the stream *os*.
+
+ **Example**::
+
+ fmt::print(cerr, "Don't {}!", "panic");
+ \endrst
+ */
+FMT_MODULE_EXPORT
+template <typename S, typename... Args,
+ typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
+void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) {
+ vprint(os, to_string_view(format_str),
+ fmt::make_args_checked<Args...>(format_str, args...));
+}
+FMT_END_NAMESPACE
+
+#endif // FMT_OSTREAM_H_
diff --git a/wpiutil/src/main/native/fmtlib/include/fmt/printf.h b/wpiutil/src/main/native/fmtlib/include/fmt/printf.h
new file mode 100644
index 0000000..3a3cd15
--- /dev/null
+++ b/wpiutil/src/main/native/fmtlib/include/fmt/printf.h
@@ -0,0 +1,652 @@
+// Formatting library for C++ - legacy printf implementation
+//
+// Copyright (c) 2012 - 2016, Victor Zverovich
+// All rights reserved.
+//
+// For the license information refer to format.h.
+
+#ifndef FMT_PRINTF_H_
+#define FMT_PRINTF_H_
+
+#include <algorithm> // std::max
+#include <limits> // std::numeric_limits
+#include <ostream>
+
+#include "format.h"
+
+FMT_BEGIN_NAMESPACE
+FMT_MODULE_EXPORT_BEGIN
+
+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 {
+ private:
+ OutputIt 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>;
+ template <typename T> using formatter_type = printf_formatter<T>;
+
+ /**
+ \rst
+ Constructs a ``printf_context`` object. References to the arguments are
+ stored in the context object so make sure they have appropriate lifetimes.
+ \endrst
+ */
+ basic_printf_context(OutputIt out,
+ basic_format_args<basic_printf_context> args)
+ : out_(out), args_(args) {}
+
+ OutputIt out() { return out_; }
+ void advance_to(OutputIt it) { out_ = it; }
+
+ detail::locale_ref locale() { return {}; }
+
+ format_arg arg(int id) const { return args_.get(id); }
+
+ FMT_CONSTEXPR void on_error(const char* message) {
+ detail::error_handler().on_error(message);
+ }
+};
+
+FMT_BEGIN_DETAIL_NAMESPACE
+
+// 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) {
+ unsigned max = max_value<int>();
+ return value <= max;
+ }
+ static bool fits_in_int(bool) { return true; }
+};
+
+template <> struct int_checker<true> {
+ template <typename T> static bool fits_in_int(T value) {
+ return value >= (std::numeric_limits<int>::min)() &&
+ value <= max_value<int>();
+ }
+ static bool fits_in_int(int) { return true; }
+};
+
+class printf_precision_handler {
+ public:
+ template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
+ int operator()(T value) {
+ if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
+ FMT_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"));
+ return 0;
+ }
+};
+
+// An argument visitor that returns true iff arg is a zero integer.
+class is_zero_int {
+ public:
+ template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
+ bool operator()(T value) {
+ return value == 0;
+ }
+
+ template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
+ bool operator()(T) {
+ return false;
+ }
+};
+
+template <typename T> struct make_unsigned_or_bool : std::make_unsigned<T> {};
+
+template <> struct make_unsigned_or_bool<bool> { using type = bool; };
+
+template <typename T, typename Context> class arg_converter {
+ private:
+ using char_type = typename Context::char_type;
+
+ basic_format_arg<Context>& arg_;
+ char_type type_;
+
+ public:
+ arg_converter(basic_format_arg<Context>& arg, char_type type)
+ : arg_(arg), type_(type) {}
+
+ void operator()(bool value) {
+ if (type_ != 's') operator()<bool>(value);
+ }
+
+ template <typename U, FMT_ENABLE_IF(std::is_integral<U>::value)>
+ void operator()(U value) {
+ bool is_signed = type_ == 'd' || type_ == 'i';
+ using target_type = conditional_t<std::is_same<T, void>::value, U, T>;
+ 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)));
+ } 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)));
+ }
+ } 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));
+ } else {
+ arg_ = detail::make_arg<Context>(
+ static_cast<typename make_unsigned_or_bool<U>::type>(value));
+ }
+ }
+ }
+
+ template <typename U, FMT_ENABLE_IF(!std::is_integral<U>::value)>
+ void operator()(U) {} // No conversion needed for non-integral types.
+};
+
+// Converts an integer argument to T for printf, if T is an integral type.
+// If T is void, the argument is converted to corresponding signed or unsigned
+// type depending on the type specifier: 'd' and 'i' - signed, other -
+// unsigned).
+template <typename T, typename Context, typename Char>
+void convert_arg(basic_format_arg<Context>& arg, Char type) {
+ visit_format_arg(arg_converter<T, Context>(arg, type), arg);
+}
+
+// Converts an integer argument to char for printf.
+template <typename Context> class char_converter {
+ private:
+ basic_format_arg<Context>& arg_;
+
+ public:
+ explicit char_converter(basic_format_arg<Context>& arg) : arg_(arg) {}
+
+ 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));
+ }
+
+ template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
+ void operator()(T) {} // No conversion needed for non-integral types.
+};
+
+// 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; }
+};
+
+// 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_;
+
+ public:
+ explicit printf_width_handler(format_specs& specs) : specs_(specs) {}
+
+ template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
+ unsigned operator()(T value) {
+ 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"));
+ 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"));
+ return 0;
+ }
+};
+
+// The ``printf`` argument formatter.
+template <typename OutputIt, 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>;
+
+ context_type& context_;
+
+ OutputIt write_null_pointer(bool is_string = false) {
+ auto s = this->specs;
+ s.type = 0;
+ return 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) {}
+
+ OutputIt operator()(monostate value) { return base::operator()(value); }
+
+ template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::value)>
+ OutputIt 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 && fmt_specs.type != 'c')
+ 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);
+ }
+ return base::operator()(value);
+ }
+
+ template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
+ OutputIt operator()(T value) {
+ return 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 != 'p');
+ }
+
+ /** 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 != 'p');
+ }
+
+ OutputIt operator()(basic_string_view<Char> value) {
+ return base::operator()(value);
+ }
+
+ /** Formats a pointer. */
+ OutputIt operator()(const void* value) {
+ return value ? base::operator()(value) : 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>());
+ 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) {
+ for (; it != end; ++it) {
+ switch (*it) {
+ case '-':
+ specs.align = align::left;
+ break;
+ case '+':
+ specs.sign = sign::plus;
+ break;
+ case '0':
+ specs.fill[0] = '0';
+ break;
+ case ' ':
+ if (specs.sign != sign::plus) {
+ specs.sign = sign::space;
+ }
+ break;
+ case '#':
+ specs.alt = true;
+ break;
+ default:
+ return;
+ }
+ }
+}
+
+template <typename Char, typename GetArg>
+int parse_header(const Char*& it, const Char* end,
+ basic_format_specs<Char>& specs, GetArg get_arg) {
+ int arg_index = -1;
+ Char c = *it;
+ if (c >= '0' && c <= '9') {
+ // Parse an argument index (if followed by '$') or a width possibly
+ // preceded with '0' flag(s).
+ int value = parse_nonnegative_int(it, end, -1);
+ if (it != end && *it == '$') { // value is an argument index
+ ++it;
+ arg_index = value != -1 ? value : max_value<int>();
+ } else {
+ if (c == '0') specs.fill[0] = '0';
+ 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"));
+ specs.width = value;
+ return arg_index;
+ }
+ }
+ }
+ parse_flags(specs, it, end);
+ // Parse width.
+ 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"));
+ } else if (*it == '*') {
+ ++it;
+ specs.width = static_cast<int>(visit_format_arg(
+ detail::printf_width_handler<Char>(specs), get_arg(-1)));
+ }
+ }
+ return arg_index;
+}
+
+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);
+
+ // Returns the argument with specified index or, if arg_index is -1, the next
+ // argument.
+ auto get_arg = [&](int arg_index) {
+ if (arg_index < 0)
+ arg_index = parse_ctx.next_arg_id();
+ else
+ parse_ctx.check_arg_id(--arg_index);
+ return detail::get_arg(context, arg_index);
+ };
+
+ const Char* start = parse_ctx.begin();
+ 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 '%'
+ break;
+ }
+ Char c = *it++;
+ if (it != end && *it == c) {
+ out = detail::write(
+ out, basic_string_view<Char>(start, detail::to_unsigned(it - start)));
+ start = ++it;
+ continue;
+ }
+ out = detail::write(out, basic_string_view<Char>(
+ start, detail::to_unsigned(it - 1 - start)));
+
+ basic_format_specs<Char> specs;
+ 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");
+
+ // Parse precision.
+ if (it != end && *it == '.') {
+ ++it;
+ c = it != end ? *it : 0;
+ if ('0' <= c && c <= '9') {
+ specs.precision = parse_nonnegative_int(it, end, 0);
+ } else if (c == '*') {
+ ++it;
+ specs.precision = static_cast<int>(
+ visit_format_arg(detail::printf_precision_handler(), get_arg(-1)));
+ } else {
+ specs.precision = 0;
+ }
+ }
+
+ 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);
+ 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)));
+ }
+ if (specs.alt && visit_format_arg(detail::is_zero_int(), arg))
+ specs.alt = false;
+ if (specs.fill[0] == '0') {
+ if (arg.is_arithmetic() && specs.align != align::left)
+ specs.align = align::numeric;
+ else
+ specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types or if '-'
+ // flag is also present.
+ }
+
+ // 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') {
+ ++it;
+ t = it != end ? *it : 0;
+ convert_arg<signed char>(arg, t);
+ } else {
+ convert_arg<short>(arg, t);
+ }
+ break;
+ case 'l':
+ if (t == 'l') {
+ ++it;
+ t = it != end ? *it : 0;
+ convert_arg<long long>(arg, t);
+ } else {
+ convert_arg<long>(arg, t);
+ }
+ break;
+ case 'j':
+ convert_arg<intmax_t>(arg, t);
+ break;
+ case 'z':
+ convert_arg<size_t>(arg, t);
+ break;
+ case 't':
+ convert_arg<std::ptrdiff_t>(arg, t);
+ break;
+ case 'L':
+ // printf produces garbage when 'L' is omitted for long double, no
+ // need to do the same.
+ break;
+ default:
+ --it;
+ convert_arg<void>(arg, c);
+ }
+
+ // Parse type.
+ if (it == end) FMT_THROW(format_error("invalid format string"));
+ specs.type = static_cast<char>(*it++);
+ if (arg.is_integral()) {
+ // Normalize type.
+ switch (specs.type) {
+ case 'i':
+ case 'u':
+ specs.type = 'd';
+ break;
+ case 'c':
+ visit_format_arg(
+ detail::char_converter<basic_printf_context<OutputIt, Char>>(arg),
+ arg);
+ break;
+ }
+ }
+
+ start = it;
+
+ // Format argument.
+ out = visit_format_arg(
+ detail::printf_arg_formatter<OutputIt, Char>(out, specs, context), arg);
+ }
+ detail::write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
+}
+FMT_END_DETAIL_NAMESPACE
+
+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_args = basic_format_args<printf_context>;
+using wprintf_args = basic_format_args<wprintf_context>;
+
+/**
+ \rst
+ Constructs an `~fmt::format_arg_store` object that contains references to
+ arguments and can be implicitly converted to `~fmt::printf_args`.
+ \endrst
+ */
+template <typename... T>
+inline auto make_printf_args(const T&... args)
+ -> format_arg_store<printf_context, T...> {
+ 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
+ */
+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>>
+inline auto vsprintf(
+ const S& fmt,
+ basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
+ -> std::basic_string<Char> {
+ basic_memory_buffer<Char> buffer;
+ vprintf(buffer, to_string_view(fmt), args);
+ return to_string(buffer);
+}
+
+/**
+ \rst
+ Formats arguments and returns the result as a string.
+
+ **Example**::
+
+ std::string message = fmt::sprintf("The answer is %d", 42);
+ \endrst
+*/
+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(to_string_view(fmt), fmt::make_format_args<context>(args...));
+}
+
+template <typename S, typename Char = char_t<S>>
+inline auto vfprintf(
+ std::FILE* f, const S& fmt,
+ basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
+ -> int {
+ basic_memory_buffer<Char> buffer;
+ vprintf(buffer, to_string_view(fmt), args);
+ size_t size = buffer.size();
+ return std::fwrite(buffer.data(), sizeof(Char), size, f) < size
+ ? -1
+ : static_cast<int>(size);
+}
+
+/**
+ \rst
+ Prints formatted data to the file *f*.
+
+ **Example**::
+
+ fmt::fprintf(stderr, "Don't %s!", "panic");
+ \endrst
+ */
+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, to_string_view(fmt),
+ fmt::make_format_args<context>(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)
+ -> int {
+ return vfprintf(stdout, to_string_view(fmt), args);
+}
+
+/**
+ \rst
+ Prints formatted data to ``stdout``.
+
+ **Example**::
+
+ 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(
+ to_string_view(fmt),
+ fmt::make_format_args<basic_printf_context_t<char_t<S>>>(args...));
+}
+
+template <typename S, typename Char = char_t<S>>
+FMT_DEPRECATED auto vfprintf(
+ std::basic_ostream<Char>& os, const S& fmt,
+ basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
+ -> int {
+ basic_memory_buffer<Char> buffer;
+ vprintf(buffer, to_string_view(fmt), args);
+ os.write(buffer.data(), static_cast<std::streamsize>(buffer.size()));
+ return static_cast<int>(buffer.size());
+}
+template <typename S, typename... T, typename Char = char_t<S>>
+FMT_DEPRECATED auto fprintf(std::basic_ostream<Char>& os, const S& fmt,
+ const T&... args) -> int {
+ return vfprintf(os, to_string_view(fmt),
+ fmt::make_format_args<basic_printf_context_t<Char>>(args...));
+}
+
+FMT_MODULE_EXPORT_END
+FMT_END_NAMESPACE
+
+#endif // FMT_PRINTF_H_
diff --git a/wpiutil/src/main/native/fmtlib/include/fmt/ranges.h b/wpiutil/src/main/native/fmtlib/include/fmt/ranges.h
new file mode 100644
index 0000000..f0390df
--- /dev/null
+++ b/wpiutil/src/main/native/fmtlib/include/fmt/ranges.h
@@ -0,0 +1,468 @@
+// Formatting library for C++ - experimental range support
+//
+// Copyright (c) 2012 - present, Victor Zverovich
+// All rights reserved.
+//
+// For the license information refer to format.h.
+//
+// Copyright (c) 2018 - present, Remotion (Igor Schulz)
+// All Rights Reserved
+// {fmt} support for ranges, containers and types tuple interface.
+
+#ifndef FMT_RANGES_H_
+#define FMT_RANGES_H_
+
+#include <initializer_list>
+#include <type_traits>
+
+#include "format.h"
+
+FMT_BEGIN_NAMESPACE
+
+template <typename Char, typename Enable = void> struct formatting_range {
+#ifdef FMT_DEPRECATED_BRACED_RANGES
+ Char prefix = '{';
+ Char postfix = '}';
+#else
+ Char prefix = '[';
+ Char postfix = ']';
+#endif
+
+ template <typename ParseContext>
+ FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
+ return ctx.begin();
+ }
+};
+
+template <typename Char, typename Enable = void> struct formatting_tuple {
+ Char prefix = '(';
+ Char postfix = ')';
+
+ template <typename ParseContext>
+ FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
+ return ctx.begin();
+ }
+};
+
+namespace detail {
+
+template <typename RangeT, typename OutputIterator>
+OutputIterator copy(const RangeT& range, OutputIterator out) {
+ 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) {
+ while (*str) *out++ = *str++;
+ return out;
+}
+
+template <typename OutputIterator>
+OutputIterator copy(char ch, OutputIterator out) {
+ *out++ = ch;
+ return out;
+}
+
+template <typename OutputIterator>
+OutputIterator copy(wchar_t ch, OutputIterator out) {
+ *out++ = ch;
+ return out;
+}
+
+/// Return true value if T has std::string interface, like std::string_view.
+template <typename T> class is_std_string_like {
+ template <typename U>
+ static auto check(U* p)
+ -> decltype((void)p->find('a'), p->length(), (void)p->data(), int());
+ template <typename> static void check(...);
+
+ public:
+ static FMT_CONSTEXPR_DECL const bool value =
+ is_string<T>::value || !std::is_void<decltype(check<T>(nullptr))>::value;
+};
+
+template <typename Char>
+struct is_std_string_like<fmt::basic_string_view<Char>> : std::true_type {};
+
+template <typename... Ts> struct conditional_helper {};
+
+template <typename T, typename _ = void> struct is_range_ : std::false_type {};
+
+#if !FMT_MSC_VER || FMT_MSC_VER > 1800
+
+# define FMT_DECLTYPE_RETURN(val) \
+ ->decltype(val) { return val; } \
+ static_assert( \
+ true, "") // This makes it so that a semicolon is required after the
+ // macro, which helps clang-format handle the formatting.
+
+// C array overload
+template <typename T, std::size_t N>
+auto range_begin(const T (&arr)[N]) -> const T* {
+ return arr;
+}
+template <typename T, std::size_t N>
+auto range_end(const T (&arr)[N]) -> const T* {
+ return arr + N;
+}
+
+template <typename T, typename Enable = void>
+struct has_member_fn_begin_end_t : std::false_type {};
+
+template <typename T>
+struct has_member_fn_begin_end_t<T, void_t<decltype(std::declval<T>().begin()),
+ decltype(std::declval<T>().end())>>
+ : std::true_type {};
+
+// Member function overload
+template <typename T>
+auto range_begin(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).begin());
+template <typename T>
+auto range_end(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).end());
+
+// ADL overload. Only participates in overload resolution if member functions
+// are not found.
+template <typename T>
+auto range_begin(T&& rng)
+ -> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
+ decltype(begin(static_cast<T&&>(rng)))> {
+ return begin(static_cast<T&&>(rng));
+}
+template <typename T>
+auto range_end(T&& rng) -> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
+ decltype(end(static_cast<T&&>(rng)))> {
+ return end(static_cast<T&&>(rng));
+}
+
+template <typename T, typename Enable = void>
+struct has_const_begin_end : std::false_type {};
+template <typename T, typename Enable = void>
+struct has_mutable_begin_end : std::false_type {};
+
+template <typename T>
+struct has_const_begin_end<
+ T, void_t<decltype(detail::range_begin(
+ std::declval<const remove_cvref_t<T>&>())),
+ decltype(detail::range_begin(
+ std::declval<const remove_cvref_t<T>&>()))>>
+ : std::true_type {};
+
+template <typename T>
+struct has_mutable_begin_end<
+ T, void_t<decltype(detail::range_begin(std::declval<T>())),
+ decltype(detail::range_begin(std::declval<T>())),
+ enable_if_t<std::is_copy_constructible<T>::value>>>
+ : std::true_type {};
+
+template <typename T>
+struct is_range_<T, void>
+ : std::integral_constant<bool, (has_const_begin_end<T>::value ||
+ has_mutable_begin_end<T>::value)> {};
+
+template <typename T, typename Enable = void> struct range_to_view;
+template <typename T>
+struct range_to_view<T, enable_if_t<has_const_begin_end<T>::value>> {
+ struct view_t {
+ const T* m_range_ptr;
+
+ auto begin() const FMT_DECLTYPE_RETURN(detail::range_begin(*m_range_ptr));
+ auto end() const FMT_DECLTYPE_RETURN(detail::range_end(*m_range_ptr));
+ };
+ static auto view(const T& range) -> view_t { return {&range}; }
+};
+
+template <typename T>
+struct range_to_view<T, enable_if_t<!has_const_begin_end<T>::value &&
+ has_mutable_begin_end<T>::value>> {
+ struct view_t {
+ T m_range_copy;
+
+ auto begin() FMT_DECLTYPE_RETURN(detail::range_begin(m_range_copy));
+ auto end() FMT_DECLTYPE_RETURN(detail::range_end(m_range_copy));
+ };
+ static auto view(const T& range) -> view_t { return {range}; }
+};
+# undef FMT_DECLTYPE_RETURN
+#endif
+
+/// tuple_size and tuple_element check.
+template <typename T> class is_tuple_like_ {
+ template <typename U>
+ static auto check(U* p) -> decltype(std::tuple_size<U>::value, int());
+ template <typename> static void check(...);
+
+ public:
+ static FMT_CONSTEXPR_DECL const bool value =
+ !std::is_void<decltype(check<T>(nullptr))>::value;
+};
+
+// Check for integer_sequence
+#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900
+template <typename T, T... N>
+using integer_sequence = std::integer_sequence<T, N...>;
+template <size_t... N> using index_sequence = std::index_sequence<N...>;
+template <size_t N> using make_index_sequence = std::make_index_sequence<N>;
+#else
+template <typename T, T... N> struct integer_sequence {
+ using value_type = T;
+
+ static FMT_CONSTEXPR size_t size() { return sizeof...(N); }
+};
+
+template <size_t... N> using index_sequence = integer_sequence<size_t, N...>;
+
+template <typename T, size_t N, T... Ns>
+struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {};
+template <typename T, T... Ns>
+struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {};
+
+template <size_t N>
+using make_index_sequence = make_integer_sequence<size_t, N>;
+#endif
+
+template <class Tuple, class F, size_t... Is>
+void for_each(index_sequence<Is...>, Tuple&& tup, F&& f) FMT_NOEXCEPT {
+ using std::get;
+ // using free function get<I>(T) now.
+ const int _[] = {0, ((void)f(get<Is>(tup)), 0)...};
+ (void)_; // blocks warnings
+}
+
+template <class T>
+FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value> get_indexes(
+ T const&) {
+ return {};
+}
+
+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 Range>
+using value_type =
+ remove_cvref_t<decltype(*detail::range_begin(std::declval<Range>()))>;
+
+template <typename OutputIt> OutputIt write_delimiter(OutputIt out) {
+ *out++ = ',';
+ *out++ = ' ';
+ return out;
+}
+
+template <
+ typename Char, typename OutputIt, typename Arg,
+ FMT_ENABLE_IF(is_std_string_like<typename std::decay<Arg>::type>::value)>
+OutputIt write_range_entry(OutputIt out, const Arg& v) {
+ *out++ = '"';
+ out = write<Char>(out, v);
+ *out++ = '"';
+ return out;
+}
+
+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) {
+ *out++ = '\'';
+ *out++ = v;
+ *out++ = '\'';
+ return out;
+}
+
+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);
+}
+
+} // namespace detail
+
+template <typename T> struct is_tuple_like {
+ static FMT_CONSTEXPR_DECL const bool value =
+ detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value;
+};
+
+template <typename TupleT, typename Char>
+struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
+ private:
+ // 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::write_delimiter(out);
+ out = detail::write_range_entry<Char>(out, v);
+ ++i;
+ }
+ formatting_tuple<Char>& formatting;
+ size_t& i;
+ typename std::add_lvalue_reference<
+ decltype(std::declval<FormatContext>().out())>::type out;
+ };
+
+ public:
+ formatting_tuple<Char> formatting;
+
+ template <typename ParseContext>
+ FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
+ return formatting.parse(ctx);
+ }
+
+ template <typename FormatContext = format_context>
+ auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) {
+ auto out = ctx.out();
+ size_t i = 0;
+
+ detail::copy(formatting.prefix, out);
+ detail::for_each(values, format_each<FormatContext>{formatting, i, out});
+ detail::copy(formatting.postfix, out);
+
+ return ctx.out();
+ }
+};
+
+template <typename T, typename Char> struct is_range {
+ static FMT_CONSTEXPR_DECL const bool value =
+ detail::is_range_<T>::value && !detail::is_std_string_like<T>::value &&
+ !std::is_convertible<T, std::basic_string<Char>>::value &&
+ !std::is_constructible<detail::std_string_view<Char>, T>::value;
+};
+
+template <typename T, typename Char>
+struct formatter<
+ T, Char,
+ enable_if_t<
+ fmt::is_range<T, Char>::value
+// Workaround a bug in MSVC 2017 and earlier.
+#if !FMT_MSC_VER || FMT_MSC_VER >= 1927
+ && (has_formatter<detail::value_type<T>, format_context>::value ||
+ detail::has_fallback_formatter<detail::value_type<T>, Char>::value)
+#endif
+ >> {
+ formatting_range<Char> formatting;
+
+ template <typename ParseContext>
+ FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
+ return formatting.parse(ctx);
+ }
+
+ template <typename FormatContext>
+ typename FormatContext::iterator format(const T& values, FormatContext& ctx) {
+ auto out = detail::copy(formatting.prefix, ctx.out());
+ size_t i = 0;
+ auto view = detail::range_to_view<T>::view(values);
+ auto it = view.begin();
+ auto end = view.end();
+ for (; it != end; ++it) {
+ if (i > 0) out = detail::write_delimiter(out);
+ out = detail::write_range_entry<Char>(out, *it);
+ ++i;
+ }
+ return detail::copy(formatting.postfix, out);
+ }
+};
+
+template <typename Char, typename... T> struct tuple_join_view : detail::view {
+ const std::tuple<T...>& tuple;
+ basic_string_view<Char> sep;
+
+ tuple_join_view(const std::tuple<T...>& t, basic_string_view<Char> s)
+ : tuple(t), sep{s} {}
+};
+
+template <typename Char, typename... T>
+using tuple_arg_join = tuple_join_view<Char, T...>;
+
+template <typename Char, typename... T>
+struct formatter<tuple_join_view<Char, T...>, Char> {
+ template <typename ParseContext>
+ FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
+ return ctx.begin();
+ }
+
+ template <typename FormatContext>
+ auto format(const tuple_join_view<Char, T...>& value, FormatContext& ctx) ->
+ typename FormatContext::iterator {
+ return format(value, ctx, detail::make_index_sequence<sizeof...(T)>{});
+ }
+
+ private:
+ template <typename FormatContext, size_t... N>
+ auto format(const tuple_join_view<Char, T...>& value, FormatContext& ctx,
+ detail::index_sequence<N...>) ->
+ typename FormatContext::iterator {
+ using std::get;
+ return format_args(value, ctx, get<N>(value.tuple)...);
+ }
+
+ template <typename FormatContext>
+ auto format_args(const tuple_join_view<Char, T...>&, FormatContext& ctx) ->
+ typename FormatContext::iterator {
+ // NOTE: for compilers that support C++17, this empty function instantiation
+ // can be replaced with a constexpr branch in the variadic overload.
+ return ctx.out();
+ }
+
+ template <typename FormatContext, typename Arg, typename... Args>
+ auto format_args(const tuple_join_view<Char, T...>& value, FormatContext& ctx,
+ const Arg& arg, const Args&... args) ->
+ typename FormatContext::iterator {
+ using base = formatter<typename std::decay<Arg>::type, Char>;
+ auto out = base().format(arg, ctx);
+ if (sizeof...(Args) > 0) {
+ out = std::copy(value.sep.begin(), value.sep.end(), out);
+ ctx.advance_to(out);
+ return format_args(value, ctx, args...);
+ }
+ return out;
+ }
+};
+
+FMT_MODULE_EXPORT_BEGIN
+
+/**
+ \rst
+ Returns an object that formats `tuple` with elements separated by `sep`.
+
+ **Example**::
+
+ std::tuple<int, char> t = {1, 'a'};
+ fmt::print("{}", fmt::join(t, ", "));
+ // Output: "1, a"
+ \endrst
+ */
+template <typename... T>
+FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple, string_view sep)
+ -> tuple_join_view<char, T...> {
+ return {tuple, sep};
+}
+
+template <typename... T>
+FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple,
+ basic_string_view<wchar_t> sep)
+ -> tuple_join_view<wchar_t, T...> {
+ return {tuple, sep};
+}
+
+/**
+ \rst
+ Returns an object that formats `initializer_list` with elements separated by
+ `sep`.
+
+ **Example**::
+
+ fmt::print("{}", fmt::join({1, 2, 3}, ", "));
+ // Output: "1, 2, 3"
+ \endrst
+ */
+template <typename T>
+auto join(std::initializer_list<T> list, string_view sep)
+ -> join_view<const T*, const T*> {
+ return join(std::begin(list), std::end(list), sep);
+}
+
+FMT_MODULE_EXPORT_END
+FMT_END_NAMESPACE
+
+#endif // FMT_RANGES_H_
diff --git a/wpiutil/src/main/native/fmtlib/include/fmt/xchar.h b/wpiutil/src/main/native/fmtlib/include/fmt/xchar.h
new file mode 100644
index 0000000..a0dd032
--- /dev/null
+++ b/wpiutil/src/main/native/fmtlib/include/fmt/xchar.h
@@ -0,0 +1,236 @@
+// Formatting library for C++ - optional wchar_t and exotic character support
+//
+// Copyright (c) 2012 - present, Victor Zverovich
+// All rights reserved.
+//
+// For the license information refer to format.h.
+
+#ifndef FMT_WCHAR_H_
+#define FMT_WCHAR_H_
+
+#include <cwchar>
+#include <tuple>
+
+#include "format.h"
+
+FMT_BEGIN_NAMESPACE
+namespace detail {
+template <typename T>
+using is_exotic_char = bool_constant<!std::is_same<T, char>::value>;
+}
+
+FMT_MODULE_EXPORT_BEGIN
+
+using wstring_view = basic_string_view<wchar_t>;
+using wformat_parse_context = basic_format_parse_context<wchar_t>;
+using wformat_context = buffer_context<wchar_t>;
+using wformat_args = basic_format_args<wformat_context>;
+using wmemory_buffer = basic_memory_buffer<wchar_t>;
+
+#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
+// Workaround broken conversion on older gcc.
+template <typename... Args> using wformat_string = wstring_view;
+#else
+template <typename... Args>
+using wformat_string = basic_format_string<wchar_t, type_identity_t<Args>...>;
+#endif
+
+template <> struct is_char<wchar_t> : std::true_type {};
+template <> struct is_char<detail::char8_type> : std::true_type {};
+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) {
+ return {args...};
+}
+
+inline namespace literals {
+constexpr auto operator"" _format(const wchar_t* s, size_t n)
+ -> detail::udl_formatter<wchar_t> {
+ return {{s, n}};
+}
+
+#if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
+constexpr detail::udl_arg<wchar_t> operator"" _a(const wchar_t* s, size_t) {
+ return {s};
+}
+#endif
+} // namespace literals
+
+template <typename It, typename Sentinel>
+auto join(It begin, Sentinel end, wstring_view sep)
+ -> join_view<It, Sentinel, wchar_t> {
+ return {begin, end, sep};
+}
+
+template <typename Range>
+auto join(Range&& range, wstring_view sep)
+ -> join_view<detail::iterator_t<Range>, detail::sentinel_t<Range>,
+ wchar_t> {
+ return join(std::begin(range), std::end(range), sep);
+}
+
+template <typename T>
+auto join(std::initializer_list<T> list, wstring_view sep)
+ -> join_view<const T*, const T*, wchar_t> {
+ return join(std::begin(list), std::end(list), sep);
+}
+
+template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
+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);
+}
+
+// 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>,
+ FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
+auto format(const S& format_str, Args&&... args) -> std::basic_string<Char> {
+ const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
+ return vformat(to_string_view(format_str), vargs);
+}
+
+template <typename Locale, typename S, typename Char = char_t<S>,
+ FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
+ detail::is_exotic_char<Char>::value)>
+inline auto vformat(
+ const Locale& loc, const S& format_str,
+ basic_format_args<buffer_context<type_identity_t<Char>>> args)
+ -> std::basic_string<Char> {
+ return detail::vformat(loc, to_string_view(format_str), args);
+}
+
+template <typename Locale, typename S, typename... Args,
+ 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)
+ -> std::basic_string<Char> {
+ return detail::vformat(loc, to_string_view(format_str),
+ fmt::make_args_checked<Args...>(format_str, args...));
+}
+
+template <typename OutputIt, typename S, typename Char = char_t<S>,
+ FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
+ detail::is_exotic_char<Char>::value)>
+auto vformat_to(OutputIt out, const S& format_str,
+ basic_format_args<buffer_context<type_identity_t<Char>>> args)
+ -> OutputIt {
+ auto&& buf = detail::get_buffer<Char>(out);
+ detail::vformat_to(buf, to_string_view(format_str), args);
+ return detail::get_iterator(buf);
+}
+
+template <typename OutputIt, typename S, typename... Args,
+ 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 {
+ const auto& vargs = fmt::make_args_checked<Args...>(fmt, args...);
+ return vformat_to(out, to_string_view(fmt), vargs);
+}
+
+template <typename S, typename... Args, typename Char, size_t SIZE,
+ typename Allocator, FMT_ENABLE_IF(detail::is_string<S>::value)>
+FMT_DEPRECATED auto format_to(basic_memory_buffer<Char, SIZE, Allocator>& buf,
+ const S& format_str, Args&&... args) ->
+ typename buffer_context<Char>::iterator {
+ const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
+ detail::vformat_to(buf, to_string_view(format_str), vargs, {});
+ return detail::buffer_appender<Char>(buf);
+}
+
+template <typename Locale, typename S, typename OutputIt, typename... Args,
+ typename Char = char_t<S>,
+ FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
+ detail::is_locale<Locale>::value&&
+ detail::is_exotic_char<Char>::value)>
+inline auto vformat_to(
+ OutputIt out, const Locale& loc, const S& format_str,
+ basic_format_args<buffer_context<type_identity_t<Char>>> args) -> OutputIt {
+ auto&& buf = detail::get_buffer<Char>(out);
+ vformat_to(buf, to_string_view(format_str), args, detail::locale_ref(loc));
+ return detail::get_iterator(buf);
+}
+
+template <
+ typename OutputIt, typename Locale, typename S, typename... Args,
+ 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) ->
+ typename std::enable_if<enable, OutputIt>::type {
+ const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
+ return vformat_to(out, loc, to_string_view(format_str), vargs);
+}
+
+template <typename OutputIt, typename Char, typename... Args,
+ FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
+ detail::is_exotic_char<Char>::value)>
+inline auto vformat_to_n(
+ 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);
+ detail::vformat_to(buf, format_str, args);
+ return {buf.out(), buf.count()};
+}
+
+template <typename OutputIt, typename S, typename... Args,
+ 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> {
+ const auto& vargs = fmt::make_args_checked<Args...>(fmt, args...);
+ return vformat_to_n(out, n, to_string_view(fmt), vargs);
+}
+
+template <typename S, typename... Args, 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;
+ const auto& vargs = fmt::make_args_checked<Args...>(fmt, args...);
+ detail::vformat_to(buf, to_string_view(fmt), vargs);
+ 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');
+ if (std::fputws(buffer.data(), f) == -1)
+ FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
+}
+
+inline void vprint(wstring_view fmt, wformat_args args) {
+ vprint(stdout, fmt, args);
+}
+
+template <typename... T>
+void print(std::FILE* f, wformat_string<T...> fmt, T&&... args) {
+ return vprint(f, wstring_view(fmt), make_wformat_args(args...));
+}
+
+template <typename... T> void print(wformat_string<T...> fmt, T&&... args) {
+ return vprint(wstring_view(fmt), make_wformat_args(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_NAMESPACE
+
+#endif // FMT_WCHAR_H_
diff --git a/wpiutil/src/main/native/fmtlib/src/format.cpp b/wpiutil/src/main/native/fmtlib/src/format.cpp
new file mode 100644
index 0000000..66925b4
--- /dev/null
+++ b/wpiutil/src/main/native/fmtlib/src/format.cpp
@@ -0,0 +1,78 @@
+// Formatting library for C++
+//
+// Copyright (c) 2012 - 2016, Victor Zverovich
+// All rights reserved.
+//
+// For the license information refer to format.h.
+
+#include "fmt/format-inl.h"
+
+FMT_BEGIN_NAMESPACE
+namespace detail {
+
+template <typename T>
+int format_float(char* buf, std::size_t size, const char* format, int precision,
+ T value) {
+#ifdef FMT_FUZZ
+ if (precision > 100000)
+ throw std::runtime_error(
+ "fuzz mode - avoid large allocation inside snprintf");
+#endif
+ // Suppress the warning about nonliteral format string.
+ int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF;
+ return precision < 0 ? snprintf_ptr(buf, size, format, value)
+ : snprintf_ptr(buf, size, format, precision, value);
+}
+
+template FMT_API dragonbox::decimal_fp<float> dragonbox::to_decimal(float x)
+ FMT_NOEXCEPT;
+template FMT_API dragonbox::decimal_fp<double> dragonbox::to_decimal(double x)
+ FMT_NOEXCEPT;
+} // namespace detail
+
+// Workaround a bug in MSVC2013 that prevents instantiation of format_float.
+int (*instantiate_format_float)(double, int, detail::float_specs,
+ detail::buffer<char>&) = detail::format_float;
+
+#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
+template FMT_API detail::locale_ref::locale_ref(const std::locale& loc);
+template FMT_API std::locale detail::locale_ref::get<std::locale>() const;
+#endif
+
+// Explicit instantiations for char.
+
+template FMT_API auto detail::thousands_sep_impl(locale_ref)
+ -> thousands_sep_result<char>;
+template FMT_API char detail::decimal_point_impl(locale_ref);
+
+template FMT_API void detail::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 detail::vformat_to(
+ detail::buffer<char>&, string_view,
+ basic_format_args<FMT_BUFFER_CONTEXT(char)>, detail::locale_ref);
+
+template FMT_API int detail::snprintf_float(double, int, detail::float_specs,
+ detail::buffer<char>&);
+template FMT_API int detail::snprintf_float(long double, int,
+ detail::float_specs,
+ detail::buffer<char>&);
+template FMT_API int detail::format_float(double, int, detail::float_specs,
+ detail::buffer<char>&);
+template FMT_API int detail::format_float(long double, int, detail::float_specs,
+ detail::buffer<char>&);
+
+// Explicit instantiations for wchar_t.
+
+template FMT_API auto detail::thousands_sep_impl(locale_ref)
+ -> thousands_sep_result<wchar_t>;
+template FMT_API wchar_t detail::decimal_point_impl(locale_ref);
+
+template FMT_API void detail::buffer<wchar_t>::append(const wchar_t*,
+ const wchar_t*);
+
+template struct detail::basic_data<void>;
+
+FMT_END_NAMESPACE
diff --git a/wpiutil/src/main/native/fmtlib/src/os.cpp b/wpiutil/src/main/native/fmtlib/src/os.cpp
new file mode 100644
index 0000000..934629d
--- /dev/null
+++ b/wpiutil/src/main/native/fmtlib/src/os.cpp
@@ -0,0 +1,360 @@
+// Formatting library for C++ - optional OS-specific functionality
+//
+// Copyright (c) 2012 - 2016, Victor Zverovich
+// All rights reserved.
+//
+// For the license information refer to format.h.
+
+// Disable bogus MSVC warnings.
+#if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER)
+# define _CRT_SECURE_NO_WARNINGS
+#endif
+
+#include "fmt/os.h"
+
+#include <climits>
+
+#if FMT_USE_FCNTL
+# include <sys/stat.h>
+# include <sys/types.h>
+
+# ifndef _WIN32
+# include <unistd.h>
+# else
+# ifndef WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+# endif
+# include <io.h>
+
+# define O_CREAT _O_CREAT
+# define O_TRUNC _O_TRUNC
+
+# ifndef S_IRUSR
+# define S_IRUSR _S_IREAD
+# endif
+
+# ifndef S_IWUSR
+# define S_IWUSR _S_IWRITE
+# endif
+
+# ifdef __MINGW32__
+# define _SH_DENYNO 0x40
+# endif
+# endif // _WIN32
+#endif // FMT_USE_FCNTL
+
+#ifdef _WIN32
+# include <windows.h>
+#endif
+
+#ifdef fileno
+# undef fileno
+#endif
+
+namespace {
+#ifdef _WIN32
+// Return type of read and write functions.
+using rwresult = int;
+
+// On Windows the count argument to read and write is unsigned, so convert
+// it from size_t preventing integer overflow.
+inline unsigned convert_rwcount(std::size_t count) {
+ return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX;
+}
+#elif FMT_USE_FCNTL
+// Return type of read and write functions.
+using rwresult = ssize_t;
+
+inline std::size_t convert_rwcount(std::size_t count) { return count; }
+#endif
+} // namespace
+
+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 {
+ system_message(const system_message&) = delete;
+ void operator=(const system_message&) = delete;
+
+ unsigned long result_;
+ wchar_t* message_;
+
+ static bool is_whitespace(wchar_t c) FMT_NOEXCEPT {
+ return c == L' ' || c == L'\n' || c == L'\r' || c == L'\t' || c == L'\0';
+ }
+
+ public:
+ explicit system_message(unsigned long error_code)
+ : result_(0), message_(nullptr) {
+ result_ = FormatMessageW(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ nullptr, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ reinterpret_cast<wchar_t*>(&message_), 0, nullptr);
+ if (result_ != 0) {
+ while (result_ != 0 && is_whitespace(message_[result_ - 1])) {
+ --result_;
+ }
+ }
+ }
+ ~system_message() { LocalFree(message_); }
+ explicit operator bool() const FMT_NOEXCEPT { return result_ != 0; }
+ operator basic_string_view<wchar_t>() const FMT_NOEXCEPT {
+ return basic_string_view<wchar_t>(message_, result_);
+ }
+};
+
+class utf8_system_category final : public std::error_category {
+ public:
+ const char* name() const FMT_NOEXCEPT override { return "system"; }
+ std::string message(int error_code) const override {
+ system_message msg(error_code);
+ if (msg) {
+ utf16_to_utf8 utf8_message;
+ if (utf8_message.convert(msg) == ERROR_SUCCESS) {
+ return utf8_message.str();
+ }
+ }
+ return "unknown error";
+ }
+};
+
+} // namespace detail
+
+FMT_API const std::error_category& system_category() FMT_NOEXCEPT {
+ static const detail::utf8_system_category category;
+ return category;
+}
+
+std::system_error vwindows_error(int err_code, string_view format_str,
+ format_args args) {
+ auto ec = std::error_code(err_code, system_category());
+ return std::system_error(ec, vformat(format_str, args));
+}
+
+void detail::format_windows_error(detail::buffer<char>& out, int error_code,
+ const char* message) FMT_NOEXCEPT {
+ FMT_TRY {
+ system_message msg(error_code);
+ if (msg) {
+ utf16_to_utf8 utf8_message;
+ if (utf8_message.convert(msg) == ERROR_SUCCESS) {
+ format_to(buffer_appender<char>(out), "{}: {}", message, utf8_message);
+ return;
+ }
+ }
+ }
+ FMT_CATCH(...) {}
+ format_error_code(out, error_code, message);
+}
+
+void report_windows_error(int error_code, const char* message) FMT_NOEXCEPT {
+ report_error(detail::format_windows_error, error_code, message);
+}
+#endif // _WIN32
+
+buffered_file::~buffered_file() FMT_NOEXCEPT {
+ if (file_ && FMT_SYSTEM(fclose(file_)) != 0)
+ report_system_error(errno, "cannot close file");
+}
+
+buffered_file::buffered_file(cstring_view filename, cstring_view mode) {
+ 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()));
+}
+
+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"));
+}
+
+// A macro used to prevent expansion of fileno on broken versions of MinGW.
+#define FMT_ARGS
+
+int buffered_file::fileno() const {
+ int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_));
+ if (fd == -1) FMT_THROW(system_error(errno, "cannot get file descriptor"));
+ return fd;
+}
+
+#if FMT_USE_FCNTL
+file::file(cstring_view path, int oflag) {
+ int mode = S_IRUSR | S_IWUSR;
+# if defined(_WIN32) && !defined(__MINGW32__)
+ fd_ = -1;
+ FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode));
+# else
+ FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode)));
+# endif
+ if (fd_ == -1)
+ FMT_THROW(system_error(errno, "cannot open file {}", path.c_str()));
+}
+
+file::~file() FMT_NOEXCEPT {
+ // Don't retry close in case of EINTR!
+ // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
+ if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0)
+ report_system_error(errno, "cannot close file");
+}
+
+void file::close() {
+ if (fd_ == -1) return;
+ // Don't retry close in case of EINTR!
+ // 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"));
+}
+
+long long file::size() const {
+# ifdef _WIN32
+ // Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT
+ // is less than 0x0500 as is the case with some default MinGW builds.
+ // Both functions support large file sizes.
+ DWORD size_upper = 0;
+ HANDLE handle = reinterpret_cast<HANDLE>(_get_osfhandle(fd_));
+ DWORD size_lower = FMT_SYSTEM(GetFileSize(handle, &size_upper));
+ if (size_lower == INVALID_FILE_SIZE) {
+ DWORD error = GetLastError();
+ if (error != NO_ERROR)
+ FMT_THROW(windows_error(GetLastError(), "cannot get file size"));
+ }
+ unsigned long long long_size = size_upper;
+ return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower;
+# else
+ 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"));
+ static_assert(sizeof(long long) >= sizeof(file_stat.st_size),
+ "return type of file::size is not large enough");
+ return file_stat.st_size;
+# endif
+}
+
+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);
+}
+
+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))));
+ if (result < 0) FMT_THROW(system_error(errno, "cannot write to file"));
+ return detail::to_unsigned(result);
+}
+
+file file::dup(int fd) {
+ // Don't retry as dup doesn't return EINTR.
+ // 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));
+ return file(new_fd);
+}
+
+void file::dup2(int fd) {
+ 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));
+ }
+}
+
+void file::dup2(int fd, std::error_code& ec) FMT_NOEXCEPT {
+ int result = 0;
+ FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
+ if (result == -1) ec = std::error_code(errno, std::generic_category());
+}
+
+void file::pipe(file& read_end, file& write_end) {
+ // Close the descriptors first to make sure that assignments don't throw
+ // and there are no leaks.
+ read_end.close();
+ write_end.close();
+ int fds[2] = {};
+# ifdef _WIN32
+ // Make the default pipe capacity same as on Linux 2.6.11+.
+ enum { DEFAULT_CAPACITY = 65536 };
+ int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY));
+# else
+ // Don't retry as the pipe function doesn't return EINTR.
+ // 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"));
+ // The following assignments don't throw because read_fd and write_fd
+ // are closed.
+ read_end = file(fds[0]);
+ write_end = file(fds[1]);
+}
+
+buffered_file file::fdopen(const char* mode) {
+// Don't retry as fdopen doesn't return EINTR.
+# if defined(__MINGW32__) && defined(_POSIX_)
+ FILE* f = ::fdopen(fd_, mode);
+# else
+ FILE* f = FMT_POSIX_CALL(fdopen(fd_, mode));
+# endif
+ if (!f)
+ FMT_THROW(
+ system_error(errno, "cannot associate stream with file descriptor"));
+ buffered_file bf(f);
+ fd_ = -1;
+ return bf;
+}
+
+long getpagesize() {
+# ifdef _WIN32
+ SYSTEM_INFO si;
+ GetSystemInfo(&si);
+ return si.dwPageSize;
+# 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
+}
+
+FMT_API void ostream::grow(size_t) {
+ if (this->size() == this->capacity()) flush();
+}
+#endif // FMT_USE_FCNTL
+FMT_END_NAMESPACE
diff --git a/wpiutil/src/main/native/include/wpi/Algorithm.h b/wpiutil/src/main/native/include/wpi/Algorithm.h
index ec8dc95..d0744dc 100644
--- a/wpiutil/src/main/native/include/wpi/Algorithm.h
+++ b/wpiutil/src/main/native/include/wpi/Algorithm.h
@@ -1,12 +1,10 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2019 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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 <vector>
namespace wpi {
diff --git a/wpiutil/src/main/native/include/wpi/ArrayRef.h b/wpiutil/src/main/native/include/wpi/ArrayRef.h
deleted file mode 100644
index c94312e..0000000
--- a/wpiutil/src/main/native/include/wpi/ArrayRef.h
+++ /dev/null
@@ -1,537 +0,0 @@
-//===- ArrayRef.h - Array Reference Wrapper ---------------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef WPIUTIL_WPI_ARRAYREF_H
-#define WPIUTIL_WPI_ARRAYREF_H
-
-#include "wpi/Hashing.h"
-#include "wpi/SmallVector.h"
-#include "wpi/STLExtras.h"
-#include "wpi/Compiler.h"
-#include <algorithm>
-#include <array>
-#include <cassert>
-#include <cstddef>
-#include <initializer_list>
-#include <iterator>
-#include <memory>
-#include <optional>
-#include <type_traits>
-#include <vector>
-
-namespace wpi {
-
- /// ArrayRef - Represent a constant reference to an array (0 or more elements
- /// consecutively in memory), i.e. a start pointer and a length. It allows
- /// various APIs to take consecutive elements easily and conveniently.
- ///
- /// This class does not own the underlying data, it is expected to be used in
- /// situations where the data resides in some other buffer, whose lifetime
- /// extends past that of the ArrayRef. For this reason, it is not in general
- /// safe to store an ArrayRef.
- ///
- /// This is intended to be trivially copyable, so it should be passed by
- /// value.
- template<typename T>
- class LLVM_NODISCARD ArrayRef {
- public:
- using iterator = const T *;
- using const_iterator = const T *;
- using size_type = size_t;
- using reverse_iterator = std::reverse_iterator<iterator>;
- using value_type = T;
-
- private:
- /// The start of the array, in an external buffer.
- const T *Data = nullptr;
-
- /// The number of elements.
- size_type Length = 0;
-
- public:
- /// @name Constructors
- /// @{
-
- /// Construct an empty ArrayRef.
- /*implicit*/ ArrayRef() = default;
-
- /// Construct an empty ArrayRef from nullopt.
- /*implicit*/ ArrayRef(std::nullopt_t) {}
-
- /// Construct an ArrayRef from a single element.
- /*implicit*/ ArrayRef(const T &OneElt)
- : Data(&OneElt), Length(1) {}
-
- /// Construct an ArrayRef from a pointer and length.
- /*implicit*/ ArrayRef(const T *data, size_t length)
- : Data(data), Length(length) {}
-
- /// Construct an ArrayRef from a range.
- ArrayRef(const T *begin, const T *end)
- : Data(begin), Length(end - begin) {}
-
- /// Construct an ArrayRef from a SmallVector. This is templated in order to
- /// avoid instantiating SmallVectorTemplateCommon<T> whenever we
- /// copy-construct an ArrayRef.
- template<typename U>
- /*implicit*/ ArrayRef(const SmallVectorTemplateCommon<T, U> &Vec)
- : Data(Vec.data()), Length(Vec.size()) {
- }
-
- /// Construct an ArrayRef from a std::vector.
- template<typename A>
- /*implicit*/ ArrayRef(const std::vector<T, A> &Vec)
- : Data(Vec.data()), Length(Vec.size()) {}
-
- /// Construct an ArrayRef from a std::array
- template <size_t N>
- /*implicit*/ constexpr ArrayRef(const std::array<T, N> &Arr)
- : Data(Arr.data()), Length(N) {}
-
- /// Construct an ArrayRef from a C array.
- template <size_t N>
- /*implicit*/ constexpr ArrayRef(const T (&Arr)[N]) : Data(Arr), Length(N) {}
-
- /// Construct an ArrayRef<const T*> from ArrayRef<T*>. This uses SFINAE to
- /// ensure that only ArrayRefs of pointers can be converted.
- template <typename U>
- ArrayRef(
- const ArrayRef<U *> &A,
- typename std::enable_if<
- std::is_convertible<U *const *, T const *>::value>::type * = nullptr)
- : Data(A.data()), Length(A.size()) {}
-
- /// Construct an ArrayRef<const T*> from a SmallVector<T*>. This is
- /// templated in order to avoid instantiating SmallVectorTemplateCommon<T>
- /// whenever we copy-construct an ArrayRef.
- template<typename U, typename DummyT>
- /*implicit*/ ArrayRef(
- const SmallVectorTemplateCommon<U *, DummyT> &Vec,
- typename std::enable_if<
- std::is_convertible<U *const *, T const *>::value>::type * = nullptr)
- : Data(Vec.data()), Length(Vec.size()) {
- }
-
- /// Construct an ArrayRef<const T*> from std::vector<T*>. This uses SFINAE
- /// to ensure that only vectors of pointers can be converted.
- template<typename U, typename A>
- ArrayRef(const std::vector<U *, A> &Vec,
- typename std::enable_if<
- std::is_convertible<U *const *, T const *>::value>::type* = 0)
- : Data(Vec.data()), Length(Vec.size()) {}
-
- /// @}
- /// @name Simple Operations
- /// @{
-
- iterator begin() const { return Data; }
- iterator end() const { return Data + Length; }
-
- reverse_iterator rbegin() const { return reverse_iterator(end()); }
- reverse_iterator rend() const { return reverse_iterator(begin()); }
-
- /// empty - Check if the array is empty.
- bool empty() const { return Length == 0; }
-
- const T *data() const { return Data; }
-
- /// size - Get the array size.
- size_t size() const { return Length; }
-
- /// front - Get the first element.
- const T &front() const {
- assert(!empty());
- return Data[0];
- }
-
- /// back - Get the last element.
- const T &back() const {
- assert(!empty());
- return Data[Length-1];
- }
-
- // copy - Allocate copy in Allocator and return ArrayRef<T> to it.
- template <typename Allocator> ArrayRef<T> copy(Allocator &A) {
- T *Buff = A.template Allocate<T>(Length);
- std::uninitialized_copy(begin(), end(), Buff);
- return ArrayRef<T>(Buff, Length);
- }
-
- /// equals - Check for element-wise equality.
- bool equals(ArrayRef RHS) const {
- if (Length != RHS.Length)
- return false;
- return std::equal(begin(), end(), RHS.begin());
- }
-
- /// slice(n, m) - Chop off the first N elements of the array, and keep M
- /// elements in the array.
- ArrayRef<T> slice(size_t N, size_t M) const {
- assert(N+M <= size() && "Invalid specifier");
- return ArrayRef<T>(data()+N, M);
- }
-
- /// slice(n) - Chop off the first N elements of the array.
- ArrayRef<T> slice(size_t N) const { return slice(N, size() - N); }
-
- /// Drop the first \p N elements of the array.
- ArrayRef<T> drop_front(size_t N = 1) const {
- assert(size() >= N && "Dropping more elements than exist");
- return slice(N, size() - N);
- }
-
- /// Drop the last \p N elements of the array.
- ArrayRef<T> drop_back(size_t N = 1) const {
- assert(size() >= N && "Dropping more elements than exist");
- return slice(0, size() - N);
- }
-
- /// Return a copy of *this with the first N elements satisfying the
- /// given predicate removed.
- template <class PredicateT> ArrayRef<T> drop_while(PredicateT Pred) const {
- return ArrayRef<T>(find_if_not(*this, Pred), end());
- }
-
- /// Return a copy of *this with the first N elements not satisfying
- /// the given predicate removed.
- template <class PredicateT> ArrayRef<T> drop_until(PredicateT Pred) const {
- return ArrayRef<T>(find_if(*this, Pred), end());
- }
-
- /// Return a copy of *this with only the first \p N elements.
- ArrayRef<T> take_front(size_t N = 1) const {
- if (N >= size())
- return *this;
- return drop_back(size() - N);
- }
-
- /// Return a copy of *this with only the last \p N elements.
- ArrayRef<T> take_back(size_t N = 1) const {
- if (N >= size())
- return *this;
- return drop_front(size() - N);
- }
-
- /// Return the first N elements of this Array that satisfy the given
- /// predicate.
- template <class PredicateT> ArrayRef<T> take_while(PredicateT Pred) const {
- return ArrayRef<T>(begin(), find_if_not(*this, Pred));
- }
-
- /// Return the first N elements of this Array that don't satisfy the
- /// given predicate.
- template <class PredicateT> ArrayRef<T> take_until(PredicateT Pred) const {
- return ArrayRef<T>(begin(), find_if(*this, Pred));
- }
-
- /// @}
- /// @name Operator Overloads
- /// @{
- const T &operator[](size_t Index) const {
- assert(Index < Length && "Invalid index!");
- return Data[Index];
- }
-
- /// Disallow accidental assignment from a temporary.
- ///
- /// The declaration here is extra complicated so that "arrayRef = {}"
- /// continues to select the move assignment operator.
- template <typename U>
- typename std::enable_if<std::is_same<U, T>::value, ArrayRef<T>>::type &
- operator=(U &&Temporary) = delete;
-
- /// Disallow accidental assignment from a temporary.
- ///
- /// The declaration here is extra complicated so that "arrayRef = {}"
- /// continues to select the move assignment operator.
- template <typename U>
- typename std::enable_if<std::is_same<U, T>::value, ArrayRef<T>>::type &
- operator=(std::initializer_list<U>) = delete;
-
- /// @}
- /// @name Expensive Operations
- /// @{
- std::vector<T> vec() const {
- return std::vector<T>(Data, Data+Length);
- }
-
- /// @}
- /// @name Conversion operators
- /// @{
- operator std::vector<T>() const {
- return std::vector<T>(Data, Data+Length);
- }
-
- /// @}
- };
-
- /// MutableArrayRef - Represent a mutable reference to an array (0 or more
- /// elements consecutively in memory), i.e. a start pointer and a length. It
- /// allows various APIs to take and modify consecutive elements easily and
- /// conveniently.
- ///
- /// This class does not own the underlying data, it is expected to be used in
- /// situations where the data resides in some other buffer, whose lifetime
- /// extends past that of the MutableArrayRef. For this reason, it is not in
- /// general safe to store a MutableArrayRef.
- ///
- /// This is intended to be trivially copyable, so it should be passed by
- /// value.
- template<typename T>
- class LLVM_NODISCARD MutableArrayRef : public ArrayRef<T> {
- public:
- using iterator = T *;
- using reverse_iterator = std::reverse_iterator<iterator>;
-
- /// Construct an empty MutableArrayRef.
- /*implicit*/ MutableArrayRef() = default;
-
- /// Construct an empty MutableArrayRef from nullopt.
- /*implicit*/ MutableArrayRef(std::nullopt_t) : ArrayRef<T>() {}
-
- /// Construct an MutableArrayRef from a single element.
- /*implicit*/ MutableArrayRef(T &OneElt) : ArrayRef<T>(OneElt) {}
-
- /// Construct an MutableArrayRef from a pointer and length.
- /*implicit*/ MutableArrayRef(T *data, size_t length)
- : ArrayRef<T>(data, length) {}
-
- /// Construct an MutableArrayRef from a range.
- MutableArrayRef(T *begin, T *end) : ArrayRef<T>(begin, end) {}
-
- /// Construct an MutableArrayRef from a SmallVector.
- /*implicit*/ MutableArrayRef(SmallVectorImpl<T> &Vec)
- : ArrayRef<T>(Vec) {}
-
- /// Construct a MutableArrayRef from a std::vector.
- /*implicit*/ MutableArrayRef(std::vector<T> &Vec)
- : ArrayRef<T>(Vec) {}
-
- /// Construct an ArrayRef from a std::array
- template <size_t N>
- /*implicit*/ constexpr MutableArrayRef(std::array<T, N> &Arr)
- : ArrayRef<T>(Arr) {}
-
- /// Construct an MutableArrayRef from a C array.
- template <size_t N>
- /*implicit*/ constexpr MutableArrayRef(T (&Arr)[N]) : ArrayRef<T>(Arr) {}
-
- T *data() const { return const_cast<T*>(ArrayRef<T>::data()); }
-
- iterator begin() const { return data(); }
- iterator end() const { return data() + this->size(); }
-
- reverse_iterator rbegin() const { return reverse_iterator(end()); }
- reverse_iterator rend() const { return reverse_iterator(begin()); }
-
- /// front - Get the first element.
- T &front() const {
- assert(!this->empty());
- return data()[0];
- }
-
- /// back - Get the last element.
- T &back() const {
- assert(!this->empty());
- return data()[this->size()-1];
- }
-
- /// slice(n, m) - Chop off the first N elements of the array, and keep M
- /// elements in the array.
- MutableArrayRef<T> slice(size_t N, size_t M) const {
- assert(N + M <= this->size() && "Invalid specifier");
- return MutableArrayRef<T>(this->data() + N, M);
- }
-
- /// slice(n) - Chop off the first N elements of the array.
- MutableArrayRef<T> slice(size_t N) const {
- return slice(N, this->size() - N);
- }
-
- /// Drop the first \p N elements of the array.
- MutableArrayRef<T> drop_front(size_t N = 1) const {
- assert(this->size() >= N && "Dropping more elements than exist");
- return slice(N, this->size() - N);
- }
-
- MutableArrayRef<T> drop_back(size_t N = 1) const {
- assert(this->size() >= N && "Dropping more elements than exist");
- return slice(0, this->size() - N);
- }
-
- /// Return a copy of *this with the first N elements satisfying the
- /// given predicate removed.
- template <class PredicateT>
- MutableArrayRef<T> drop_while(PredicateT Pred) const {
- return MutableArrayRef<T>(find_if_not(*this, Pred), end());
- }
-
- /// Return a copy of *this with the first N elements not satisfying
- /// the given predicate removed.
- template <class PredicateT>
- MutableArrayRef<T> drop_until(PredicateT Pred) const {
- return MutableArrayRef<T>(find_if(*this, Pred), end());
- }
-
- /// Return a copy of *this with only the first \p N elements.
- MutableArrayRef<T> take_front(size_t N = 1) const {
- if (N >= this->size())
- return *this;
- return drop_back(this->size() - N);
- }
-
- /// Return a copy of *this with only the last \p N elements.
- MutableArrayRef<T> take_back(size_t N = 1) const {
- if (N >= this->size())
- return *this;
- return drop_front(this->size() - N);
- }
-
- /// Return the first N elements of this Array that satisfy the given
- /// predicate.
- template <class PredicateT>
- MutableArrayRef<T> take_while(PredicateT Pred) const {
- return MutableArrayRef<T>(begin(), find_if_not(*this, Pred));
- }
-
- /// Return the first N elements of this Array that don't satisfy the
- /// given predicate.
- template <class PredicateT>
- MutableArrayRef<T> take_until(PredicateT Pred) const {
- return MutableArrayRef<T>(begin(), find_if(*this, Pred));
- }
-
- /// @}
- /// @name Operator Overloads
- /// @{
- T &operator[](size_t Index) const {
- assert(Index < this->size() && "Invalid index!");
- return data()[Index];
- }
- };
-
- /// This is a MutableArrayRef that owns its array.
- template <typename T> class OwningArrayRef : public MutableArrayRef<T> {
- public:
- OwningArrayRef() = default;
- OwningArrayRef(size_t Size) : MutableArrayRef<T>(new T[Size], Size) {}
-
- OwningArrayRef(ArrayRef<T> Data)
- : MutableArrayRef<T>(new T[Data.size()], Data.size()) {
- std::copy(Data.begin(), Data.end(), this->begin());
- }
-
- OwningArrayRef(OwningArrayRef &&Other) { *this = Other; }
-
- OwningArrayRef &operator=(OwningArrayRef &&Other) {
- delete[] this->data();
- this->MutableArrayRef<T>::operator=(Other);
- Other.MutableArrayRef<T>::operator=(MutableArrayRef<T>());
- return *this;
- }
-
- ~OwningArrayRef() { delete[] this->data(); }
- };
-
- /// @name ArrayRef Convenience constructors
- /// @{
-
- /// Construct an ArrayRef from a single element.
- template<typename T>
- ArrayRef<T> makeArrayRef(const T &OneElt) {
- return OneElt;
- }
-
- /// Construct an ArrayRef from a pointer and length.
- template<typename T>
- ArrayRef<T> makeArrayRef(const T *data, size_t length) {
- return ArrayRef<T>(data, length);
- }
-
- /// Construct an ArrayRef from a range.
- template<typename T>
- ArrayRef<T> makeArrayRef(const T *begin, const T *end) {
- return ArrayRef<T>(begin, end);
- }
-
- /// Construct an ArrayRef from a SmallVector.
- template <typename T>
- ArrayRef<T> makeArrayRef(const SmallVectorImpl<T> &Vec) {
- return Vec;
- }
-
- /// Construct an ArrayRef from a SmallVector.
- template <typename T, unsigned N>
- ArrayRef<T> makeArrayRef(const SmallVector<T, N> &Vec) {
- return Vec;
- }
-
- /// Construct an ArrayRef from a std::vector.
- template<typename T>
- ArrayRef<T> makeArrayRef(const std::vector<T> &Vec) {
- return Vec;
- }
-
- /// Construct an ArrayRef from an ArrayRef (no-op) (const)
- template <typename T> ArrayRef<T> makeArrayRef(const ArrayRef<T> &Vec) {
- return Vec;
- }
-
- /// Construct an ArrayRef from an ArrayRef (no-op)
- template <typename T> ArrayRef<T> &makeArrayRef(ArrayRef<T> &Vec) {
- return Vec;
- }
-
- /// Construct an ArrayRef from a C array.
- template<typename T, size_t N>
- ArrayRef<T> makeArrayRef(const T (&Arr)[N]) {
- return ArrayRef<T>(Arr);
- }
-
- /// Construct a MutableArrayRef from a single element.
- template<typename T>
- MutableArrayRef<T> makeMutableArrayRef(T &OneElt) {
- return OneElt;
- }
-
- /// Construct a MutableArrayRef from a pointer and length.
- template<typename T>
- MutableArrayRef<T> makeMutableArrayRef(T *data, size_t length) {
- return MutableArrayRef<T>(data, length);
- }
-
- /// @}
- /// @name ArrayRef Comparison Operators
- /// @{
-
- template<typename T>
- inline bool operator==(ArrayRef<T> LHS, ArrayRef<T> RHS) {
- return LHS.equals(RHS);
- }
-
- template<typename T>
- inline bool operator!=(ArrayRef<T> LHS, ArrayRef<T> RHS) {
- return !(LHS == RHS);
- }
-
- /// @}
-
- // ArrayRefs can be treated like a POD type.
- template <typename T> struct isPodLike;
- template <typename T> struct isPodLike<ArrayRef<T>> {
- static const bool value = true;
- };
-
- template <typename T> hash_code hash_value(ArrayRef<T> S) {
- return hash_combine_range(S.begin(), S.end());
- }
-
-} // end namespace wpi
-
-#endif // LLVM_ADT_ARRAYREF_H
diff --git a/wpiutil/src/main/native/include/wpi/Base64.h b/wpiutil/src/main/native/include/wpi/Base64.h
index cdbf611..41b0062 100644
--- a/wpiutil/src/main/native/include/wpi/Base64.h
+++ b/wpiutil/src/main/native/include/wpi/Base64.h
@@ -1,35 +1,47 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2015-2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_BASE64_H_
#define WPIUTIL_WPI_BASE64_H_
#include <cstddef>
#include <string>
+#include <string_view>
+#include <vector>
-#include "wpi/StringRef.h"
+#include "wpi/span.h"
namespace wpi {
template <typename T>
class SmallVectorImpl;
class raw_ostream;
-size_t Base64Decode(raw_ostream& os, StringRef encoded);
+size_t Base64Decode(raw_ostream& os, std::string_view encoded);
-size_t Base64Decode(StringRef encoded, std::string* plain);
+size_t Base64Decode(std::string_view encoded, std::string* plain);
-StringRef Base64Decode(StringRef encoded, size_t* num_read,
- SmallVectorImpl<char>& buf);
+std::string_view Base64Decode(std::string_view encoded, size_t* num_read,
+ SmallVectorImpl<char>& buf);
-void Base64Encode(raw_ostream& os, StringRef plain);
+size_t Base64Decode(std::string_view encoded, std::vector<uint8_t>* plain);
-void Base64Encode(StringRef plain, std::string* encoded);
+span<uint8_t> Base64Decode(std::string_view encoded, size_t* num_read,
+ SmallVectorImpl<uint8_t>& buf);
-StringRef Base64Encode(StringRef plain, SmallVectorImpl<char>& buf);
+void Base64Encode(raw_ostream& os, std::string_view plain);
+
+void Base64Encode(std::string_view plain, std::string* encoded);
+
+std::string_view Base64Encode(std::string_view plain,
+ SmallVectorImpl<char>& buf);
+
+void Base64Encode(raw_ostream& os, span<const uint8_t> plain);
+
+void Base64Encode(span<const uint8_t> plain, std::string* encoded);
+
+std::string_view Base64Encode(span<const uint8_t> plain,
+ SmallVectorImpl<char>& buf);
} // namespace wpi
diff --git a/wpiutil/src/main/native/include/wpi/CallbackManager.h b/wpiutil/src/main/native/include/wpi/CallbackManager.h
new file mode 100644
index 0000000..32e2add
--- /dev/null
+++ b/wpiutil/src/main/native/include/wpi/CallbackManager.h
@@ -0,0 +1,395 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef WPIUTIL_WPI_CALLBACKMANAGER_H_
+#define WPIUTIL_WPI_CALLBACKMANAGER_H_
+
+#include <atomic>
+#include <climits>
+#include <functional>
+#include <memory>
+#include <queue>
+#include <utility>
+#include <vector>
+
+#include "wpi/SafeThread.h"
+#include "wpi/UidVector.h"
+#include "wpi/condition_variable.h"
+#include "wpi/mutex.h"
+#include "wpi/raw_ostream.h"
+
+namespace wpi {
+
+template <typename Callback>
+class CallbackListenerData {
+ public:
+ CallbackListenerData() = default;
+ explicit CallbackListenerData(Callback callback_) : callback(callback_) {}
+ explicit CallbackListenerData(unsigned int poller_uid_)
+ : poller_uid(poller_uid_) {}
+
+ explicit operator bool() const { return callback || poller_uid != UINT_MAX; }
+
+ Callback callback;
+ unsigned int poller_uid = UINT_MAX;
+};
+
+// CRTP callback manager thread
+// @tparam Derived derived class
+// @tparam NotifierData data buffered for each callback
+// @tparam ListenerData data stored for each listener
+// Derived must define the following functions:
+// bool Matches(const ListenerData& listener, const NotifierData& data);
+// void SetListener(NotifierData* data, unsigned int listener_uid);
+// void DoCallback(Callback callback, const NotifierData& data);
+template <typename Derived, typename TUserInfo,
+ typename TListenerData =
+ CallbackListenerData<std::function<void(const TUserInfo& info)>>,
+ typename TNotifierData = TUserInfo>
+class CallbackThread : public wpi::SafeThread {
+ public:
+ using UserInfo = TUserInfo;
+ using NotifierData = TNotifierData;
+ using ListenerData = TListenerData;
+
+ CallbackThread(std::function<void()> on_start, std::function<void()> on_exit)
+ : m_on_start(std::move(on_start)), m_on_exit(std::move(on_exit)) {}
+
+ ~CallbackThread() override {
+ // Wake up any blocked pollers
+ for (size_t i = 0; i < m_pollers.size(); ++i) {
+ if (auto poller = m_pollers[i]) {
+ poller->Terminate();
+ }
+ }
+ }
+
+ void Main() override;
+
+ wpi::UidVector<ListenerData, 64> m_listeners;
+
+ std::queue<std::pair<unsigned int, NotifierData>> m_queue;
+ wpi::condition_variable m_queue_empty;
+
+ struct Poller {
+ void Terminate() {
+ {
+ std::scoped_lock lock(poll_mutex);
+ terminating = true;
+ }
+ poll_cond.notify_all();
+ }
+ std::queue<NotifierData> poll_queue;
+ wpi::mutex poll_mutex;
+ wpi::condition_variable poll_cond;
+ bool terminating = false;
+ bool canceling = false;
+ };
+ wpi::UidVector<std::shared_ptr<Poller>, 64> m_pollers;
+
+ std::function<void()> m_on_start;
+ std::function<void()> m_on_exit;
+
+ // Must be called with m_mutex held
+ template <typename... Args>
+ void SendPoller(unsigned int poller_uid, Args&&... args) {
+ if (poller_uid > m_pollers.size()) {
+ return;
+ }
+ auto poller = m_pollers[poller_uid];
+ if (!poller) {
+ return;
+ }
+ {
+ std::scoped_lock lock(poller->poll_mutex);
+ poller->poll_queue.emplace(std::forward<Args>(args)...);
+ }
+ poller->poll_cond.notify_one();
+ }
+};
+
+template <typename Derived, typename TUserInfo, typename TListenerData,
+ typename TNotifierData>
+void CallbackThread<Derived, TUserInfo, TListenerData, TNotifierData>::Main() {
+ if (m_on_start) {
+ m_on_start();
+ }
+
+ std::unique_lock lock(m_mutex);
+ while (m_active) {
+ while (m_queue.empty()) {
+ m_cond.wait(lock);
+ if (!m_active) {
+ goto done;
+ }
+ }
+
+ while (!m_queue.empty()) {
+ if (!m_active) {
+ goto done;
+ }
+ auto item = std::move(m_queue.front());
+
+ if (item.first != UINT_MAX) {
+ if (item.first < m_listeners.size()) {
+ auto& listener = m_listeners[item.first];
+ if (listener &&
+ static_cast<Derived*>(this)->Matches(listener, item.second)) {
+ static_cast<Derived*>(this)->SetListener(&item.second, item.first);
+ if (listener.callback) {
+ lock.unlock();
+ static_cast<Derived*>(this)->DoCallback(listener.callback,
+ item.second);
+ lock.lock();
+ } else if (listener.poller_uid != UINT_MAX) {
+ SendPoller(listener.poller_uid, std::move(item.second));
+ }
+ }
+ }
+ } else {
+ // Use index because iterator might get invalidated.
+ for (size_t i = 0; i < m_listeners.size(); ++i) {
+ auto& listener = m_listeners[i];
+ if (!listener) {
+ continue;
+ }
+
+ if (!static_cast<Derived*>(this)->Matches(listener, item.second)) {
+ continue;
+ }
+ static_cast<Derived*>(this)->SetListener(&item.second,
+ static_cast<unsigned>(i));
+ if (listener.callback) {
+ lock.unlock();
+ static_cast<Derived*>(this)->DoCallback(listener.callback,
+ item.second);
+ lock.lock();
+ } else if (listener.poller_uid != UINT_MAX) {
+ SendPoller(listener.poller_uid, item.second);
+ }
+ }
+ }
+ m_queue.pop();
+ }
+
+ m_queue_empty.notify_all();
+ }
+
+done:
+ if (m_on_exit) {
+ m_on_exit();
+ }
+}
+
+// CRTP callback manager
+// @tparam Derived derived class
+// @tparam Thread custom thread (must be derived from impl::CallbackThread)
+//
+// Derived must define the following functions:
+// void Start();
+template <typename Derived, typename Thread>
+class CallbackManager {
+ friend class RpcServerTest;
+
+ public:
+ void SetOnStart(std::function<void()> on_start) {
+ m_on_start = std::move(on_start);
+ }
+
+ void SetOnExit(std::function<void()> on_exit) {
+ m_on_exit = std::move(on_exit);
+ }
+
+ void Stop() { m_owner.Stop(); }
+
+ void Remove(unsigned int listener_uid) {
+ auto thr = m_owner.GetThread();
+ if (!thr) {
+ return;
+ }
+ thr->m_listeners.erase(listener_uid);
+ }
+
+ unsigned int CreatePoller() {
+ static_cast<Derived*>(this)->Start();
+ auto thr = m_owner.GetThread();
+ return thr->m_pollers.emplace_back(
+ std::make_shared<typename Thread::Poller>());
+ }
+
+ void RemovePoller(unsigned int poller_uid) {
+ auto thr = m_owner.GetThread();
+ if (!thr) {
+ return;
+ }
+
+ // Remove any listeners that are associated with this poller
+ for (size_t i = 0; i < thr->m_listeners.size(); ++i) {
+ if (thr->m_listeners[i].poller_uid == poller_uid) {
+ thr->m_listeners.erase(i);
+ }
+ }
+
+ // Wake up any blocked pollers
+ if (poller_uid >= thr->m_pollers.size()) {
+ return;
+ }
+ auto poller = thr->m_pollers[poller_uid];
+ if (!poller) {
+ return;
+ }
+ poller->Terminate();
+ thr->m_pollers.erase(poller_uid);
+ }
+
+ bool WaitForQueue(double timeout) {
+ auto thr = m_owner.GetThread();
+ if (!thr) {
+ return true;
+ }
+
+ auto& lock = thr.GetLock();
+ auto timeout_time = std::chrono::steady_clock::now() +
+ std::chrono::duration<double>(timeout);
+ while (!thr->m_queue.empty()) {
+ if (!thr->m_active) {
+ return true;
+ }
+ if (timeout == 0) {
+ return false;
+ }
+ if (timeout < 0) {
+ thr->m_queue_empty.wait(lock);
+ } else {
+ auto cond_timed_out = thr->m_queue_empty.wait_until(lock, timeout_time);
+ if (cond_timed_out == std::cv_status::timeout) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ std::vector<typename Thread::UserInfo> Poll(unsigned int poller_uid) {
+ bool timed_out = false;
+ return Poll(poller_uid, -1, &timed_out);
+ }
+
+ std::vector<typename Thread::UserInfo> Poll(unsigned int poller_uid,
+ double timeout, bool* timed_out) {
+ std::vector<typename Thread::UserInfo> infos;
+ std::shared_ptr<typename Thread::Poller> poller;
+ {
+ auto thr = m_owner.GetThread();
+ if (!thr) {
+ return infos;
+ }
+ if (poller_uid > thr->m_pollers.size()) {
+ return infos;
+ }
+ poller = thr->m_pollers[poller_uid];
+ if (!poller) {
+ return infos;
+ }
+ }
+
+ std::unique_lock lock(poller->poll_mutex);
+ auto timeout_time = std::chrono::steady_clock::now() +
+ std::chrono::duration<double>(timeout);
+ *timed_out = false;
+ while (poller->poll_queue.empty()) {
+ if (poller->terminating) {
+ return infos;
+ }
+ if (poller->canceling) {
+ // Note: this only works if there's a single thread calling this
+ // function for any particular poller, but that's the intended use.
+ poller->canceling = false;
+ return infos;
+ }
+ if (timeout == 0) {
+ *timed_out = true;
+ return infos;
+ }
+ if (timeout < 0) {
+ poller->poll_cond.wait(lock);
+ } else {
+ auto cond_timed_out = poller->poll_cond.wait_until(lock, timeout_time);
+ if (cond_timed_out == std::cv_status::timeout) {
+ *timed_out = true;
+ return infos;
+ }
+ }
+ }
+
+ while (!poller->poll_queue.empty()) {
+ infos.emplace_back(std::move(poller->poll_queue.front()));
+ poller->poll_queue.pop();
+ }
+ return infos;
+ }
+
+ void CancelPoll(unsigned int poller_uid) {
+ std::shared_ptr<typename Thread::Poller> poller;
+ {
+ auto thr = m_owner.GetThread();
+ if (!thr) {
+ return;
+ }
+ if (poller_uid > thr->m_pollers.size()) {
+ return;
+ }
+ poller = thr->m_pollers[poller_uid];
+ if (!poller) {
+ return;
+ }
+ }
+
+ {
+ std::scoped_lock lock(poller->poll_mutex);
+ poller->canceling = true;
+ }
+ poller->poll_cond.notify_one();
+ }
+
+ protected:
+ template <typename... Args>
+ void DoStart(Args&&... args) {
+ m_owner.Start(m_on_start, m_on_exit, std::forward<Args>(args)...);
+ }
+
+ template <typename... Args>
+ unsigned int DoAdd(Args&&... args) {
+ static_cast<Derived*>(this)->Start();
+ auto thr = m_owner.GetThread();
+ return thr->m_listeners.emplace_back(std::forward<Args>(args)...);
+ }
+
+ template <typename... Args>
+ void Send(unsigned int only_listener, Args&&... args) {
+ auto thr = m_owner.GetThread();
+ if (!thr || thr->m_listeners.empty()) {
+ return;
+ }
+ thr->m_queue.emplace(std::piecewise_construct,
+ std::make_tuple(only_listener),
+ std::forward_as_tuple(std::forward<Args>(args)...));
+ thr->m_cond.notify_one();
+ }
+
+ typename wpi::SafeThreadOwner<Thread>::Proxy GetThread() const {
+ return m_owner.GetThread();
+ }
+
+ private:
+ wpi::SafeThreadOwner<Thread> m_owner;
+
+ std::function<void()> m_on_start;
+ std::function<void()> m_on_exit;
+};
+
+} // namespace wpi
+
+#endif // WPIUTIL_WPI_CALLBACKMANAGER_H_
diff --git a/wpiutil/src/main/native/include/wpi/ConvertUTF.h b/wpiutil/src/main/native/include/wpi/ConvertUTF.h
index a9bdb60..aa113b1 100644
--- a/wpiutil/src/main/native/include/wpi/ConvertUTF.h
+++ b/wpiutil/src/main/native/include/wpi/ConvertUTF.h
@@ -90,11 +90,11 @@
#ifndef LLVM_SUPPORT_CONVERTUTF_H
#define LLVM_SUPPORT_CONVERTUTF_H
-#include "wpi/ArrayRef.h"
-#include "wpi/StringRef.h"
+#include "wpi/span.h"
#include <cstddef>
#include <string>
+#include <string_view>
#include <system_error>
// Wrap everything in namespace wpi so that programs can link with wpiutil and
@@ -102,6 +102,9 @@
namespace wpi {
+template <typename T>
+class SmallVectorImpl;
+
/* ---------------------------------------------------------------------
The following 4 definitions are compiler-specific.
The C standard does not guarantee that wchar_t has at least
@@ -228,14 +231,14 @@
* 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 UTF-16 string into a UTF-8 string.
*
* \returns true on success
*/
-bool convertUTF16ToUTF8String(ArrayRef<UTF16> SrcUTF16,
+bool convertUTF16ToUTF8String(span<const UTF16> SrcUTF16,
SmallVectorImpl<char> &DstUTF8);
/**
@@ -243,15 +246,15 @@
*
* \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/wpiutil/src/main/native/include/wpi/Demangle.h b/wpiutil/src/main/native/include/wpi/Demangle.h
index 5989ccc..03a7d3f 100644
--- a/wpiutil/src/main/native/include/wpi/Demangle.h
+++ b/wpiutil/src/main/native/include/wpi/Demangle.h
@@ -1,16 +1,12 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2020 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. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_DEMANGLE_H_
#define WPIUTIL_WPI_DEMANGLE_H_
#include <string>
-
-#include "wpi/Twine.h"
+#include <string_view>
namespace wpi {
@@ -20,7 +16,7 @@
* @param mangledSymbol the mangled symbol.
* @return The demangled symbol, or mangledSymbol if demangling fails.
*/
-std::string Demangle(const Twine& mangledSymbol);
+std::string Demangle(std::string_view mangledSymbol);
} // namespace wpi
diff --git a/wpiutil/src/main/native/include/wpi/DenseMapInfo.h b/wpiutil/src/main/native/include/wpi/DenseMapInfo.h
index 81ccdc7..b6525a3 100644
--- a/wpiutil/src/main/native/include/wpi/DenseMapInfo.h
+++ b/wpiutil/src/main/native/include/wpi/DenseMapInfo.h
@@ -14,13 +14,13 @@
#ifndef WPIUTIL_WPI_DENSEMAPINFO_H
#define WPIUTIL_WPI_DENSEMAPINFO_H
-#include "wpi/ArrayRef.h"
#include "wpi/Hashing.h"
-#include "wpi/StringRef.h"
#include "wpi/PointerLikeTypeTraits.h"
+#include "wpi/span.h"
#include <cassert>
#include <cstddef>
#include <cstdint>
+#include <string_view>
#include <utility>
namespace wpi {
@@ -206,26 +206,26 @@
}
};
-// Provide DenseMapInfo for StringRefs.
-template <> struct DenseMapInfo<StringRef> {
- static inline StringRef getEmptyKey() {
- return StringRef(reinterpret_cast<const char *>(~static_cast<uintptr_t>(0)),
+// Provide DenseMapInfo for std::string_view.
+template <> struct DenseMapInfo<std::string_view> {
+ static inline std::string_view getEmptyKey() {
+ return std::string_view(reinterpret_cast<const char *>(~static_cast<uintptr_t>(0)),
0);
}
- static inline StringRef getTombstoneKey() {
- return StringRef(reinterpret_cast<const char *>(~static_cast<uintptr_t>(1)),
+ static inline std::string_view getTombstoneKey() {
+ return std::string_view(reinterpret_cast<const char *>(~static_cast<uintptr_t>(1)),
0);
}
- static unsigned getHashValue(StringRef Val) {
+ static unsigned getHashValue(std::string_view Val) {
assert(Val.data() != getEmptyKey().data() && "Cannot hash the empty key!");
assert(Val.data() != getTombstoneKey().data() &&
"Cannot hash the tombstone key!");
return (unsigned)(hash_value(Val));
}
- static bool isEqual(StringRef LHS, StringRef RHS) {
+ static bool isEqual(std::string_view LHS, std::string_view RHS) {
if (RHS.data() == getEmptyKey().data())
return LHS.data() == getEmptyKey().data();
if (RHS.data() == getTombstoneKey().data())
@@ -234,26 +234,26 @@
}
};
-// Provide DenseMapInfo for ArrayRefs.
-template <typename T> struct DenseMapInfo<ArrayRef<T>> {
- static inline ArrayRef<T> getEmptyKey() {
- return ArrayRef<T>(reinterpret_cast<const T *>(~static_cast<uintptr_t>(0)),
- size_t(0));
+// Provide DenseMapInfo for spans.
+template <typename T> struct DenseMapInfo<span<T>> {
+ static inline span<T> getEmptyKey() {
+ return span<T>(reinterpret_cast<const T *>(~static_cast<uintptr_t>(0)),
+ size_t(0));
}
- static inline ArrayRef<T> getTombstoneKey() {
- return ArrayRef<T>(reinterpret_cast<const T *>(~static_cast<uintptr_t>(1)),
- size_t(0));
+ static inline span<T> getTombstoneKey() {
+ return span<T>(reinterpret_cast<const T *>(~static_cast<uintptr_t>(1)),
+ size_t(0));
}
- static unsigned getHashValue(ArrayRef<T> Val) {
+ static unsigned getHashValue(span<T> Val) {
assert(Val.data() != getEmptyKey().data() && "Cannot hash the empty key!");
assert(Val.data() != getTombstoneKey().data() &&
"Cannot hash the tombstone key!");
return (unsigned)(hash_value(Val));
}
- static bool isEqual(ArrayRef<T> LHS, ArrayRef<T> RHS) {
+ static bool isEqual(span<T> LHS, span<T> RHS) {
if (RHS.data() == getEmptyKey().data())
return LHS.data() == getEmptyKey().data();
if (RHS.data() == getTombstoneKey().data())
diff --git a/wpiutil/src/main/native/include/wpi/DsClient.h b/wpiutil/src/main/native/include/wpi/DsClient.h
new file mode 100644
index 0000000..c24ba3b
--- /dev/null
+++ b/wpiutil/src/main/native/include/wpi/DsClient.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 <memory>
+#include <string>
+#include <string_view>
+
+#include "wpi/Signal.h"
+
+namespace wpi {
+
+class Logger;
+
+namespace uv {
+class Loop;
+class Tcp;
+class Timer;
+} // namespace uv
+
+class DsClient : public std::enable_shared_from_this<DsClient> {
+ struct private_init {};
+
+ public:
+ static std::shared_ptr<DsClient> Create(wpi::uv::Loop& loop,
+ wpi::Logger& logger) {
+ return std::make_shared<DsClient>(loop, logger, private_init{});
+ }
+
+ DsClient(wpi::uv::Loop& loop, wpi::Logger& logger, const private_init&);
+ ~DsClient();
+ DsClient(const DsClient&) = delete;
+ DsClient& operator=(const DsClient&) = delete;
+
+ void Close();
+
+ sig::Signal<std::string_view> setIp;
+ sig::Signal<> clearIp;
+
+ private:
+ void Connect();
+ void HandleIncoming(std::string_view in);
+ void ParseJson();
+
+ wpi::Logger& m_logger;
+
+ std::shared_ptr<wpi::uv::Tcp> m_tcp;
+ std::shared_ptr<wpi::uv::Timer> m_timer;
+
+ std::string m_json;
+};
+
+} // namespace wpi
diff --git a/wpiutil/src/main/native/include/wpi/Endian.h b/wpiutil/src/main/native/include/wpi/Endian.h
index ee4a51d..b31cb2d 100644
--- a/wpiutil/src/main/native/include/wpi/Endian.h
+++ b/wpiutil/src/main/native/include/wpi/Endian.h
@@ -38,7 +38,7 @@
namespace detail {
-/// ::value is either alignment, or alignof(T) if alignment is 0.
+// value is either alignment, or alignof(T) if alignment is 0.
template<class T, int alignment>
struct PickAlignment {
enum { value = alignment == 0 ? alignof(T) : alignment };
diff --git a/wpiutil/src/main/native/include/wpi/Error.h b/wpiutil/src/main/native/include/wpi/Error.h
deleted file mode 100644
index 1821a49..0000000
--- a/wpiutil/src/main/native/include/wpi/Error.h
+++ /dev/null
@@ -1,1197 +0,0 @@
-//===- llvm/Support/Error.h - Recoverable error handling --------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file defines an API used to report recoverable errors.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef WPIUTIL_WPI_ERROR_H
-#define WPIUTIL_WPI_ERROR_H
-
-#include "wpi/STLExtras.h"
-#include "wpi/SmallVector.h"
-#include "wpi/StringExtras.h"
-#include "wpi/Twine.h"
-#include "wpi/AlignOf.h"
-#include "wpi/Compiler.h"
-#include "wpi/ErrorHandling.h"
-#include "wpi/ErrorOr.h"
-#include "wpi/Format.h"
-#include "wpi/raw_ostream.h"
-#include <algorithm>
-#include <cassert>
-#include <cstdint>
-#include <cstdlib>
-#include <functional>
-#include <memory>
-#include <new>
-#include <string>
-#include <system_error>
-#include <type_traits>
-#include <utility>
-#include <vector>
-
-namespace wpi {
-
-class ErrorSuccess;
-
-/// Base class for error info classes. Do not extend this directly: Extend
-/// the ErrorInfo template subclass instead.
-class ErrorInfoBase {
-public:
- virtual ~ErrorInfoBase() = default;
-
- /// Print an error message to an output stream.
- virtual void log(raw_ostream &OS) const = 0;
-
- /// Return the error message as a string.
- virtual std::string message() const {
- std::string Msg;
- raw_string_ostream OS(Msg);
- log(OS);
- return OS.str();
- }
-
- /// Convert this error to a std::error_code.
- ///
- /// This is a temporary crutch to enable interaction with code still
- /// using std::error_code. It will be removed in the future.
- virtual std::error_code convertToErrorCode() const = 0;
-
- // Returns the class ID for this type.
- static const void *classID() { return &ID; }
-
- // Returns the class ID for the dynamic type of this ErrorInfoBase instance.
- virtual const void *dynamicClassID() const = 0;
-
- // Check whether this instance is a subclass of the class identified by
- // ClassID.
- virtual bool isA(const void *const ClassID) const {
- return ClassID == classID();
- }
-
- // Check whether this instance is a subclass of ErrorInfoT.
- template <typename ErrorInfoT> bool isA() const {
- return isA(ErrorInfoT::classID());
- }
-
-private:
- virtual void anchor();
-
- static char ID;
-};
-
-/// Lightweight error class with error context and mandatory checking.
-///
-/// Instances of this class wrap a ErrorInfoBase pointer. Failure states
-/// are represented by setting the pointer to a ErrorInfoBase subclass
-/// instance containing information describing the failure. Success is
-/// represented by a null pointer value.
-///
-/// Instances of Error also contains a 'Checked' flag, which must be set
-/// before the destructor is called, otherwise the destructor will trigger a
-/// runtime error. This enforces at runtime the requirement that all Error
-/// instances be checked or returned to the caller.
-///
-/// There are two ways to set the checked flag, depending on what state the
-/// Error instance is in. For Error instances indicating success, it
-/// is sufficient to invoke the boolean conversion operator. E.g.:
-///
-/// @code{.cpp}
-/// Error foo(<...>);
-///
-/// if (auto E = foo(<...>))
-/// return E; // <- Return E if it is in the error state.
-/// // We have verified that E was in the success state. It can now be safely
-/// // destroyed.
-/// @endcode
-///
-/// A success value *can not* be dropped. For example, just calling 'foo(<...>)'
-/// without testing the return value will raise a runtime error, even if foo
-/// returns success.
-///
-/// For Error instances representing failure, you must use either the
-/// handleErrors or handleAllErrors function with a typed handler. E.g.:
-///
-/// @code{.cpp}
-/// class MyErrorInfo : public ErrorInfo<MyErrorInfo> {
-/// // Custom error info.
-/// };
-///
-/// Error foo(<...>) { return make_error<MyErrorInfo>(...); }
-///
-/// auto E = foo(<...>); // <- foo returns failure with MyErrorInfo.
-/// auto NewE =
-/// handleErrors(E,
-/// [](const MyErrorInfo &M) {
-/// // Deal with the error.
-/// },
-/// [](std::unique_ptr<OtherError> M) -> Error {
-/// if (canHandle(*M)) {
-/// // handle error.
-/// return Error::success();
-/// }
-/// // Couldn't handle this error instance. Pass it up the stack.
-/// return Error(std::move(M));
-/// );
-/// // Note - we must check or return NewE in case any of the handlers
-/// // returned a new error.
-/// @endcode
-///
-/// The handleAllErrors function is identical to handleErrors, except
-/// that it has a void return type, and requires all errors to be handled and
-/// no new errors be returned. It prevents errors (assuming they can all be
-/// handled) from having to be bubbled all the way to the top-level.
-///
-/// *All* Error instances must be checked before destruction, even if
-/// they're moved-assigned or constructed from Success values that have already
-/// been checked. This enforces checking through all levels of the call stack.
-class LLVM_NODISCARD Error {
- // Both ErrorList and FileError need to be able to yank ErrorInfoBase
- // pointers out of this class to add to the error list.
- friend class ErrorList;
- friend class FileError;
-
- // handleErrors needs to be able to set the Checked flag.
- template <typename... HandlerTs>
- friend Error handleErrors(Error E, HandlerTs &&... Handlers);
-
- // Expected<T> needs to be able to steal the payload when constructed from an
- // error.
- template <typename T> friend class Expected;
-
-protected:
- /// Create a success value. Prefer using 'Error::success()' for readability
- Error() {
- setPtr(nullptr);
- setChecked(false);
- }
-
-public:
- /// Create a success value.
- static ErrorSuccess success();
-
- // Errors are not copy-constructable.
- Error(const Error &Other) = delete;
-
- /// Move-construct an error value. The newly constructed error is considered
- /// unchecked, even if the source error had been checked. The original error
- /// becomes a checked Success value, regardless of its original state.
- Error(Error &&Other) noexcept {
- setChecked(true);
- *this = std::move(Other);
- }
-
- /// Create an error value. Prefer using the 'make_error' function, but
- /// this constructor can be useful when "re-throwing" errors from handlers.
- Error(std::unique_ptr<ErrorInfoBase> Payload) {
- setPtr(Payload.release());
- setChecked(false);
- }
-
- // Errors are not copy-assignable.
- Error &operator=(const Error &Other) = delete;
-
- /// Move-assign an error value. The current error must represent success, you
- /// you cannot overwrite an unhandled error. The current error is then
- /// considered unchecked. The source error becomes a checked success value,
- /// regardless of its original state.
- Error &operator=(Error &&Other) noexcept {
- // Don't allow overwriting of unchecked values.
- assertIsChecked();
- setPtr(Other.getPtr());
-
- // This Error is unchecked, even if the source error was checked.
- setChecked(false);
-
- // Null out Other's payload and set its checked bit.
- Other.setPtr(nullptr);
- Other.setChecked(true);
-
- return *this;
- }
-
- /// Destroy a Error. Fails with a call to abort() if the error is
- /// unchecked.
- ~Error() {
- assertIsChecked();
- delete getPtr();
- }
-
- /// Bool conversion. Returns true if this Error is in a failure state,
- /// and false if it is in an accept state. If the error is in a Success state
- /// it will be considered checked.
- explicit operator bool() {
- setChecked(getPtr() == nullptr);
- return getPtr() != nullptr;
- }
-
- /// Check whether one error is a subclass of another.
- template <typename ErrT> bool isA() const {
- return getPtr() && getPtr()->isA(ErrT::classID());
- }
-
- /// Returns the dynamic class id of this error, or null if this is a success
- /// value.
- const void* dynamicClassID() const {
- if (!getPtr())
- return nullptr;
- return getPtr()->dynamicClassID();
- }
-
-private:
- void assertIsChecked() {
- }
-
- ErrorInfoBase *getPtr() const {
- return reinterpret_cast<ErrorInfoBase*>(
- reinterpret_cast<uintptr_t>(Payload) &
- ~static_cast<uintptr_t>(0x1));
- }
-
- void setPtr(ErrorInfoBase *EI) {
- Payload = EI;
- }
-
- bool getChecked() const {
- return true;
- }
-
- void setChecked(bool V) {
- Payload = reinterpret_cast<ErrorInfoBase*>(
- (reinterpret_cast<uintptr_t>(Payload) &
- ~static_cast<uintptr_t>(0x1)) |
- (V ? 0 : 1));
- }
-
- std::unique_ptr<ErrorInfoBase> takePayload() {
- std::unique_ptr<ErrorInfoBase> Tmp(getPtr());
- setPtr(nullptr);
- setChecked(true);
- return Tmp;
- }
-
- friend raw_ostream &operator<<(raw_ostream &OS, const Error &E) {
- if (auto P = E.getPtr())
- P->log(OS);
- else
- OS << "success";
- return OS;
- }
-
- ErrorInfoBase *Payload = nullptr;
-};
-
-/// Subclass of Error for the sole purpose of identifying the success path in
-/// the type system. This allows to catch invalid conversion to Expected<T> at
-/// compile time.
-class ErrorSuccess final : public Error {};
-
-inline ErrorSuccess Error::success() { return ErrorSuccess(); }
-
-/// Make a Error instance representing failure using the given error info
-/// type.
-template <typename ErrT, typename... ArgTs> Error make_error(ArgTs &&... Args) {
- return Error(std::make_unique<ErrT>(std::forward<ArgTs>(Args)...));
-}
-
-/// Base class for user error types. Users should declare their error types
-/// like:
-///
-/// class MyError : public ErrorInfo<MyError> {
-/// ....
-/// };
-///
-/// This class provides an implementation of the ErrorInfoBase::kind
-/// method, which is used by the Error RTTI system.
-template <typename ThisErrT, typename ParentErrT = ErrorInfoBase>
-class ErrorInfo : public ParentErrT {
-public:
- using ParentErrT::ParentErrT; // inherit constructors
-
- static const void *classID() { return &ThisErrT::ID; }
-
- const void *dynamicClassID() const override { return &ThisErrT::ID; }
-
- bool isA(const void *const ClassID) const override {
- return ClassID == classID() || ParentErrT::isA(ClassID);
- }
-};
-
-/// Special ErrorInfo subclass representing a list of ErrorInfos.
-/// Instances of this class are constructed by joinError.
-class ErrorList final : public ErrorInfo<ErrorList> {
- // handleErrors needs to be able to iterate the payload list of an
- // ErrorList.
- template <typename... HandlerTs>
- friend Error handleErrors(Error E, HandlerTs &&... Handlers);
-
- // joinErrors is implemented in terms of join.
- friend Error joinErrors(Error, Error);
-
-public:
- void log(raw_ostream &OS) const override {
- OS << "Multiple errors:\n";
- for (auto &ErrPayload : Payloads) {
- ErrPayload->log(OS);
- OS << "\n";
- }
- }
-
- std::error_code convertToErrorCode() const override;
-
- // Used by ErrorInfo::classID.
- static char ID;
-
-private:
- ErrorList(std::unique_ptr<ErrorInfoBase> Payload1,
- std::unique_ptr<ErrorInfoBase> Payload2) {
- assert(!Payload1->isA<ErrorList>() && !Payload2->isA<ErrorList>() &&
- "ErrorList constructor payloads should be singleton errors");
- Payloads.push_back(std::move(Payload1));
- Payloads.push_back(std::move(Payload2));
- }
-
- static Error join(Error E1, Error E2) {
- if (!E1)
- return E2;
- if (!E2)
- return E1;
- if (E1.isA<ErrorList>()) {
- auto &E1List = static_cast<ErrorList &>(*E1.getPtr());
- if (E2.isA<ErrorList>()) {
- auto E2Payload = E2.takePayload();
- auto &E2List = static_cast<ErrorList &>(*E2Payload);
- for (auto &Payload : E2List.Payloads)
- E1List.Payloads.push_back(std::move(Payload));
- } else
- E1List.Payloads.push_back(E2.takePayload());
-
- return E1;
- }
- if (E2.isA<ErrorList>()) {
- auto &E2List = static_cast<ErrorList &>(*E2.getPtr());
- E2List.Payloads.insert(E2List.Payloads.begin(), E1.takePayload());
- return E2;
- }
- return Error(std::unique_ptr<ErrorList>(
- new ErrorList(E1.takePayload(), E2.takePayload())));
- }
-
- std::vector<std::unique_ptr<ErrorInfoBase>> Payloads;
-};
-
-/// Concatenate errors. The resulting Error is unchecked, and contains the
-/// ErrorInfo(s), if any, contained in E1, followed by the
-/// ErrorInfo(s), if any, contained in E2.
-inline Error joinErrors(Error E1, Error E2) {
- return ErrorList::join(std::move(E1), std::move(E2));
-}
-
-/// Tagged union holding either a T or a Error.
-///
-/// This class parallels ErrorOr, but replaces error_code with Error. Since
-/// Error cannot be copied, this class replaces getError() with
-/// takeError(). It also adds an bool errorIsA<ErrT>() method for testing the
-/// error class type.
-template <class T> class LLVM_NODISCARD Expected {
- template <class T1> friend class ExpectedAsOutParameter;
- template <class OtherT> friend class Expected;
-
- static const bool isRef = std::is_reference<T>::value;
-
- using wrap = std::reference_wrapper<typename std::remove_reference<T>::type>;
-
- using error_type = std::unique_ptr<ErrorInfoBase>;
-
-public:
- using storage_type = typename std::conditional<isRef, wrap, T>::type;
- using value_type = T;
-
-private:
- using reference = typename std::remove_reference<T>::type &;
- using const_reference = const typename std::remove_reference<T>::type &;
- using pointer = typename std::remove_reference<T>::type *;
- using const_pointer = const typename std::remove_reference<T>::type *;
-
-public:
- /// Create an Expected<T> error value from the given Error.
- Expected(Error Err)
- : HasError(true)
- {
- assert(Err && "Cannot create Expected<T> from Error success value.");
- new (getErrorStorage()) error_type(Err.takePayload());
- }
-
- /// Forbid to convert from Error::success() implicitly, this avoids having
- /// Expected<T> foo() { return Error::success(); } which compiles otherwise
- /// but triggers the assertion above.
- Expected(ErrorSuccess) = delete;
-
- /// Create an Expected<T> success value from the given OtherT value, which
- /// must be convertible to T.
- template <typename OtherT>
- Expected(OtherT &&Val,
- typename std::enable_if<std::is_convertible<OtherT, T>::value>::type
- * = nullptr)
- : HasError(false)
- {
- new (getStorage()) storage_type(std::forward<OtherT>(Val));
- }
-
- /// Move construct an Expected<T> value.
- Expected(Expected &&Other) { moveConstruct(std::move(Other)); }
-
- /// Move construct an Expected<T> value from an Expected<OtherT>, where OtherT
- /// must be convertible to T.
- template <class OtherT>
- Expected(Expected<OtherT> &&Other,
- typename std::enable_if<std::is_convertible<OtherT, T>::value>::type
- * = nullptr) {
- moveConstruct(std::move(Other));
- }
-
- /// Move construct an Expected<T> value from an Expected<OtherT>, where OtherT
- /// isn't convertible to T.
- template <class OtherT>
- explicit Expected(
- Expected<OtherT> &&Other,
- typename std::enable_if<!std::is_convertible<OtherT, T>::value>::type * =
- nullptr) {
- moveConstruct(std::move(Other));
- }
-
- /// Move-assign from another Expected<T>.
- Expected &operator=(Expected &&Other) {
- moveAssign(std::move(Other));
- return *this;
- }
-
- /// Destroy an Expected<T>.
- ~Expected() {
- assertIsChecked();
- if (!HasError)
- getStorage()->~storage_type();
- else
- getErrorStorage()->~error_type();
- }
-
- /// Return false if there is an error.
- explicit operator bool() {
- return !HasError;
- }
-
- /// Returns a reference to the stored T value.
- reference get() {
- assertIsChecked();
- return *getStorage();
- }
-
- /// Returns a const reference to the stored T value.
- const_reference get() const {
- assertIsChecked();
- return const_cast<Expected<T> *>(this)->get();
- }
-
- /// Check that this Expected<T> is an error of type ErrT.
- template <typename ErrT> bool errorIsA() const {
- return HasError && (*getErrorStorage())->template isA<ErrT>();
- }
-
- /// Take ownership of the stored error.
- /// After calling this the Expected<T> is in an indeterminate state that can
- /// only be safely destructed. No further calls (beside the destructor) should
- /// be made on the Expected<T> vaule.
- Error takeError() {
- return HasError ? Error(std::move(*getErrorStorage())) : Error::success();
- }
-
- /// Returns a pointer to the stored T value.
- pointer operator->() {
- assertIsChecked();
- return toPointer(getStorage());
- }
-
- /// Returns a const pointer to the stored T value.
- const_pointer operator->() const {
- assertIsChecked();
- return toPointer(getStorage());
- }
-
- /// Returns a reference to the stored T value.
- reference operator*() {
- assertIsChecked();
- return *getStorage();
- }
-
- /// Returns a const reference to the stored T value.
- const_reference operator*() const {
- assertIsChecked();
- return *getStorage();
- }
-
-private:
- template <class T1>
- static bool compareThisIfSameType(const T1 &a, const T1 &b) {
- return &a == &b;
- }
-
- template <class T1, class T2>
- static bool compareThisIfSameType(const T1 &a, const T2 &b) {
- return false;
- }
-
- template <class OtherT> void moveConstruct(Expected<OtherT> &&Other) {
- HasError = Other.HasError;
-
- if (!HasError)
- new (getStorage()) storage_type(std::move(*Other.getStorage()));
- else
- new (getErrorStorage()) error_type(std::move(*Other.getErrorStorage()));
- }
-
- template <class OtherT> void moveAssign(Expected<OtherT> &&Other) {
- assertIsChecked();
-
- if (compareThisIfSameType(*this, Other))
- return;
-
- this->~Expected();
- new (this) Expected(std::move(Other));
- }
-
- pointer toPointer(pointer Val) { return Val; }
-
- const_pointer toPointer(const_pointer Val) const { return Val; }
-
- pointer toPointer(wrap *Val) { return &Val->get(); }
-
- const_pointer toPointer(const wrap *Val) const { return &Val->get(); }
-
- storage_type *getStorage() {
- assert(!HasError && "Cannot get value when an error exists!");
- return reinterpret_cast<storage_type *>(TStorage.buffer);
- }
-
- const storage_type *getStorage() const {
- assert(!HasError && "Cannot get value when an error exists!");
- return reinterpret_cast<const storage_type *>(TStorage.buffer);
- }
-
- error_type *getErrorStorage() {
- assert(HasError && "Cannot get error when a value exists!");
- return reinterpret_cast<error_type *>(ErrorStorage.buffer);
- }
-
- const error_type *getErrorStorage() const {
- assert(HasError && "Cannot get error when a value exists!");
- return reinterpret_cast<const error_type *>(ErrorStorage.buffer);
- }
-
- // Used by ExpectedAsOutParameter to reset the checked flag.
- void setUnchecked() {
- }
-
- void assertIsChecked() {
- }
-
- union {
- AlignedCharArrayUnion<storage_type> TStorage;
- AlignedCharArrayUnion<error_type> ErrorStorage;
- };
- bool HasError : 1;
-};
-
-/// Report a serious error, calling any installed error handler. See
-/// ErrorHandling.h.
-LLVM_ATTRIBUTE_NORETURN void report_fatal_error(Error Err,
- bool gen_crash_diag = true);
-
-/// Report a fatal error if Err is a failure value.
-///
-/// This function can be used to wrap calls to fallible functions ONLY when it
-/// is known that the Error will always be a success value. E.g.
-///
-/// @code{.cpp}
-/// // foo only attempts the fallible operation if DoFallibleOperation is
-/// // true. If DoFallibleOperation is false then foo always returns
-/// // Error::success().
-/// Error foo(bool DoFallibleOperation);
-///
-/// cantFail(foo(false));
-/// @endcode
-inline void cantFail(Error Err, const char *Msg = nullptr) {
- if (Err) {
- if (!Msg)
- Msg = "Failure value returned from cantFail wrapped call";
- wpi_unreachable(Msg);
- }
-}
-
-/// Report a fatal error if ValOrErr is a failure value, otherwise unwraps and
-/// returns the contained value.
-///
-/// This function can be used to wrap calls to fallible functions ONLY when it
-/// is known that the Error will always be a success value. E.g.
-///
-/// @code{.cpp}
-/// // foo only attempts the fallible operation if DoFallibleOperation is
-/// // true. If DoFallibleOperation is false then foo always returns an int.
-/// Expected<int> foo(bool DoFallibleOperation);
-///
-/// int X = cantFail(foo(false));
-/// @endcode
-template <typename T>
-T cantFail(Expected<T> ValOrErr, const char *Msg = nullptr) {
- if (ValOrErr)
- return std::move(*ValOrErr);
- else {
- if (!Msg)
- Msg = "Failure value returned from cantFail wrapped call";
- wpi_unreachable(Msg);
- }
-}
-
-/// Report a fatal error if ValOrErr is a failure value, otherwise unwraps and
-/// returns the contained reference.
-///
-/// This function can be used to wrap calls to fallible functions ONLY when it
-/// is known that the Error will always be a success value. E.g.
-///
-/// @code{.cpp}
-/// // foo only attempts the fallible operation if DoFallibleOperation is
-/// // true. If DoFallibleOperation is false then foo always returns a Bar&.
-/// Expected<Bar&> foo(bool DoFallibleOperation);
-///
-/// Bar &X = cantFail(foo(false));
-/// @endcode
-template <typename T>
-T& cantFail(Expected<T&> ValOrErr, const char *Msg = nullptr) {
- if (ValOrErr)
- return *ValOrErr;
- else {
- if (!Msg)
- Msg = "Failure value returned from cantFail wrapped call";
- wpi_unreachable(Msg);
- }
-}
-
-/// Helper for testing applicability of, and applying, handlers for
-/// ErrorInfo types.
-template <typename HandlerT>
-class ErrorHandlerTraits
- : public ErrorHandlerTraits<decltype(
- &std::remove_reference<HandlerT>::type::operator())> {};
-
-// Specialization functions of the form 'Error (const ErrT&)'.
-template <typename ErrT> class ErrorHandlerTraits<Error (&)(ErrT &)> {
-public:
- static bool appliesTo(const ErrorInfoBase &E) {
- return E.template isA<ErrT>();
- }
-
- template <typename HandlerT>
- static Error apply(HandlerT &&H, std::unique_ptr<ErrorInfoBase> E) {
- assert(appliesTo(*E) && "Applying incorrect handler");
- return H(static_cast<ErrT &>(*E));
- }
-};
-
-// Specialization functions of the form 'void (const ErrT&)'.
-template <typename ErrT> class ErrorHandlerTraits<void (&)(ErrT &)> {
-public:
- static bool appliesTo(const ErrorInfoBase &E) {
- return E.template isA<ErrT>();
- }
-
- template <typename HandlerT>
- static Error apply(HandlerT &&H, std::unique_ptr<ErrorInfoBase> E) {
- assert(appliesTo(*E) && "Applying incorrect handler");
- H(static_cast<ErrT &>(*E));
- return Error::success();
- }
-};
-
-/// Specialization for functions of the form 'Error (std::unique_ptr<ErrT>)'.
-template <typename ErrT>
-class ErrorHandlerTraits<Error (&)(std::unique_ptr<ErrT>)> {
-public:
- static bool appliesTo(const ErrorInfoBase &E) {
- return E.template isA<ErrT>();
- }
-
- template <typename HandlerT>
- static Error apply(HandlerT &&H, std::unique_ptr<ErrorInfoBase> E) {
- assert(appliesTo(*E) && "Applying incorrect handler");
- std::unique_ptr<ErrT> SubE(static_cast<ErrT *>(E.release()));
- return H(std::move(SubE));
- }
-};
-
-/// Specialization for functions of the form 'void (std::unique_ptr<ErrT>)'.
-template <typename ErrT>
-class ErrorHandlerTraits<void (&)(std::unique_ptr<ErrT>)> {
-public:
- static bool appliesTo(const ErrorInfoBase &E) {
- return E.template isA<ErrT>();
- }
-
- template <typename HandlerT>
- static Error apply(HandlerT &&H, std::unique_ptr<ErrorInfoBase> E) {
- assert(appliesTo(*E) && "Applying incorrect handler");
- std::unique_ptr<ErrT> SubE(static_cast<ErrT *>(E.release()));
- H(std::move(SubE));
- return Error::success();
- }
-};
-
-// Specialization for member functions of the form 'RetT (const ErrT&)'.
-template <typename C, typename RetT, typename ErrT>
-class ErrorHandlerTraits<RetT (C::*)(ErrT &)>
- : public ErrorHandlerTraits<RetT (&)(ErrT &)> {};
-
-// Specialization for member functions of the form 'RetT (const ErrT&) const'.
-template <typename C, typename RetT, typename ErrT>
-class ErrorHandlerTraits<RetT (C::*)(ErrT &) const>
- : public ErrorHandlerTraits<RetT (&)(ErrT &)> {};
-
-// Specialization for member functions of the form 'RetT (const ErrT&)'.
-template <typename C, typename RetT, typename ErrT>
-class ErrorHandlerTraits<RetT (C::*)(const ErrT &)>
- : public ErrorHandlerTraits<RetT (&)(ErrT &)> {};
-
-// Specialization for member functions of the form 'RetT (const ErrT&) const'.
-template <typename C, typename RetT, typename ErrT>
-class ErrorHandlerTraits<RetT (C::*)(const ErrT &) const>
- : public ErrorHandlerTraits<RetT (&)(ErrT &)> {};
-
-/// Specialization for member functions of the form
-/// 'RetT (std::unique_ptr<ErrT>)'.
-template <typename C, typename RetT, typename ErrT>
-class ErrorHandlerTraits<RetT (C::*)(std::unique_ptr<ErrT>)>
- : public ErrorHandlerTraits<RetT (&)(std::unique_ptr<ErrT>)> {};
-
-/// Specialization for member functions of the form
-/// 'RetT (std::unique_ptr<ErrT>) const'.
-template <typename C, typename RetT, typename ErrT>
-class ErrorHandlerTraits<RetT (C::*)(std::unique_ptr<ErrT>) const>
- : public ErrorHandlerTraits<RetT (&)(std::unique_ptr<ErrT>)> {};
-
-inline Error handleErrorImpl(std::unique_ptr<ErrorInfoBase> Payload) {
- return Error(std::move(Payload));
-}
-
-template <typename HandlerT, typename... HandlerTs>
-Error handleErrorImpl(std::unique_ptr<ErrorInfoBase> Payload,
- HandlerT &&Handler, HandlerTs &&... Handlers) {
- if (ErrorHandlerTraits<HandlerT>::appliesTo(*Payload))
- return ErrorHandlerTraits<HandlerT>::apply(std::forward<HandlerT>(Handler),
- std::move(Payload));
- return handleErrorImpl(std::move(Payload),
- std::forward<HandlerTs>(Handlers)...);
-}
-
-/// Pass the ErrorInfo(s) contained in E to their respective handlers. Any
-/// unhandled errors (or Errors returned by handlers) are re-concatenated and
-/// returned.
-/// Because this function returns an error, its result must also be checked
-/// or returned. If you intend to handle all errors use handleAllErrors
-/// (which returns void, and will abort() on unhandled errors) instead.
-template <typename... HandlerTs>
-Error handleErrors(Error E, HandlerTs &&... Hs) {
- if (!E)
- return Error::success();
-
- std::unique_ptr<ErrorInfoBase> Payload = E.takePayload();
-
- if (Payload->isA<ErrorList>()) {
- ErrorList &List = static_cast<ErrorList &>(*Payload);
- Error R;
- for (auto &P : List.Payloads)
- R = ErrorList::join(
- std::move(R),
- handleErrorImpl(std::move(P), std::forward<HandlerTs>(Hs)...));
- return R;
- }
-
- return handleErrorImpl(std::move(Payload), std::forward<HandlerTs>(Hs)...);
-}
-
-/// Behaves the same as handleErrors, except that by contract all errors
-/// *must* be handled by the given handlers (i.e. there must be no remaining
-/// errors after running the handlers, or wpi_unreachable is called).
-template <typename... HandlerTs>
-void handleAllErrors(Error E, HandlerTs &&... Handlers) {
- cantFail(handleErrors(std::move(E), std::forward<HandlerTs>(Handlers)...));
-}
-
-/// Check that E is a non-error, then drop it.
-/// If E is an error, wpi_unreachable will be called.
-inline void handleAllErrors(Error E) {
- cantFail(std::move(E));
-}
-
-/// Handle any errors (if present) in an Expected<T>, then try a recovery path.
-///
-/// If the incoming value is a success value it is returned unmodified. If it
-/// is a failure value then it the contained error is passed to handleErrors.
-/// If handleErrors is able to handle the error then the RecoveryPath functor
-/// is called to supply the final result. If handleErrors is not able to
-/// handle all errors then the unhandled errors are returned.
-///
-/// This utility enables the follow pattern:
-///
-/// @code{.cpp}
-/// enum FooStrategy { Aggressive, Conservative };
-/// Expected<Foo> foo(FooStrategy S);
-///
-/// auto ResultOrErr =
-/// handleExpected(
-/// foo(Aggressive),
-/// []() { return foo(Conservative); },
-/// [](AggressiveStrategyError&) {
-/// // Implicitly conusme this - we'll recover by using a conservative
-/// // strategy.
-/// });
-///
-/// @endcode
-template <typename T, typename RecoveryFtor, typename... HandlerTs>
-Expected<T> handleExpected(Expected<T> ValOrErr, RecoveryFtor &&RecoveryPath,
- HandlerTs &&... Handlers) {
- if (ValOrErr)
- return ValOrErr;
-
- if (auto Err = handleErrors(ValOrErr.takeError(),
- std::forward<HandlerTs>(Handlers)...))
- return std::move(Err);
-
- return RecoveryPath();
-}
-
-/// Log all errors (if any) in E to OS. If there are any errors, ErrorBanner
-/// will be printed before the first one is logged. A newline will be printed
-/// after each error.
-///
-/// This function is compatible with the helpers from Support/WithColor.h. You
-/// can pass any of them as the OS. Please consider using them instead of
-/// including 'error: ' in the ErrorBanner.
-///
-/// This is useful in the base level of your program to allow clean termination
-/// (allowing clean deallocation of resources, etc.), while reporting error
-/// information to the user.
-void logAllUnhandledErrors(Error E, raw_ostream &OS, Twine ErrorBanner = {});
-
-/// Write all error messages (if any) in E to a string. The newline character
-/// is used to separate error messages.
-inline std::string toString(Error E) {
- SmallVector<std::string, 2> Errors;
- handleAllErrors(std::move(E), [&Errors](const ErrorInfoBase &EI) {
- Errors.push_back(EI.message());
- });
- return join(Errors.begin(), Errors.end(), "\n");
-}
-
-/// Consume a Error without doing anything. This method should be used
-/// only where an error can be considered a reasonable and expected return
-/// value.
-///
-/// Uses of this method are potentially indicative of design problems: If it's
-/// legitimate to do nothing while processing an "error", the error-producer
-/// might be more clearly refactored to return an Optional<T>.
-inline void consumeError(Error Err) {
- handleAllErrors(std::move(Err), [](const ErrorInfoBase &) {});
-}
-
-/// Helper for converting an Error to a bool.
-///
-/// This method returns true if Err is in an error state, or false if it is
-/// in a success state. Puts Err in a checked state in both cases (unlike
-/// Error::operator bool(), which only does this for success states).
-inline bool errorToBool(Error Err) {
- bool IsError = static_cast<bool>(Err);
- if (IsError)
- consumeError(std::move(Err));
- return IsError;
-}
-
-/// Helper for Errors used as out-parameters.
-///
-/// This helper is for use with the Error-as-out-parameter idiom, where an error
-/// is passed to a function or method by reference, rather than being returned.
-/// In such cases it is helpful to set the checked bit on entry to the function
-/// so that the error can be written to (unchecked Errors abort on assignment)
-/// and clear the checked bit on exit so that clients cannot accidentally forget
-/// to check the result. This helper performs these actions automatically using
-/// RAII:
-///
-/// @code{.cpp}
-/// Result foo(Error &Err) {
-/// ErrorAsOutParameter ErrAsOutParam(&Err); // 'Checked' flag set
-/// // <body of foo>
-/// // <- 'Checked' flag auto-cleared when ErrAsOutParam is destructed.
-/// }
-/// @endcode
-///
-/// ErrorAsOutParameter takes an Error* rather than Error& so that it can be
-/// used with optional Errors (Error pointers that are allowed to be null). If
-/// ErrorAsOutParameter took an Error reference, an instance would have to be
-/// created inside every condition that verified that Error was non-null. By
-/// taking an Error pointer we can just create one instance at the top of the
-/// function.
-class ErrorAsOutParameter {
-public:
- ErrorAsOutParameter(Error *Err) : Err(Err) {
- // Raise the checked bit if Err is success.
- if (Err)
- (void)!!*Err;
- }
-
- ~ErrorAsOutParameter() {
- // Clear the checked bit.
- if (Err && !*Err)
- *Err = Error::success();
- }
-
-private:
- Error *Err;
-};
-
-/// Helper for Expected<T>s used as out-parameters.
-///
-/// See ErrorAsOutParameter.
-template <typename T>
-class ExpectedAsOutParameter {
-public:
- ExpectedAsOutParameter(Expected<T> *ValOrErr)
- : ValOrErr(ValOrErr) {
- if (ValOrErr)
- (void)!!*ValOrErr;
- }
-
- ~ExpectedAsOutParameter() {
- if (ValOrErr)
- ValOrErr->setUnchecked();
- }
-
-private:
- Expected<T> *ValOrErr;
-};
-
-/// This class wraps a std::error_code in a Error.
-///
-/// This is useful if you're writing an interface that returns a Error
-/// (or Expected) and you want to call code that still returns
-/// std::error_codes.
-class ECError : public ErrorInfo<ECError> {
- friend Error errorCodeToError(std::error_code);
-
- virtual void anchor() override;
-
-public:
- void setErrorCode(std::error_code EC) { this->EC = EC; }
- std::error_code convertToErrorCode() const override { return EC; }
- void log(raw_ostream &OS) const override { OS << EC.message(); }
-
- // Used by ErrorInfo::classID.
- static char ID;
-
-protected:
- ECError() = default;
- ECError(std::error_code EC) : EC(EC) {}
-
- std::error_code EC;
-};
-
-/// The value returned by this function can be returned from convertToErrorCode
-/// for Error values where no sensible translation to std::error_code exists.
-/// It should only be used in this situation, and should never be used where a
-/// sensible conversion to std::error_code is available, as attempts to convert
-/// to/from this error will result in a fatal error. (i.e. it is a programmatic
-///error to try to convert such a value).
-std::error_code inconvertibleErrorCode();
-
-/// Helper for converting an std::error_code to a Error.
-Error errorCodeToError(std::error_code EC);
-
-/// Helper for converting an ECError to a std::error_code.
-///
-/// This method requires that Err be Error() or an ECError, otherwise it
-/// will trigger a call to abort().
-std::error_code errorToErrorCode(Error Err);
-
-/// Convert an ErrorOr<T> to an Expected<T>.
-template <typename T> Expected<T> errorOrToExpected(ErrorOr<T> &&EO) {
- if (auto EC = EO.getError())
- return errorCodeToError(EC);
- return std::move(*EO);
-}
-
-/// Convert an Expected<T> to an ErrorOr<T>.
-template <typename T> ErrorOr<T> expectedToErrorOr(Expected<T> &&E) {
- if (auto Err = E.takeError())
- return errorToErrorCode(std::move(Err));
- return std::move(*E);
-}
-
-/// This class wraps a string in an Error.
-///
-/// StringError is useful in cases where the client is not expected to be able
-/// to consume the specific error message programmatically (for example, if the
-/// error message is to be presented to the user).
-///
-/// StringError can also be used when additional information is to be printed
-/// along with a error_code message. Depending on the constructor called, this
-/// class can either display:
-/// 1. the error_code message (ECError behavior)
-/// 2. a string
-/// 3. the error_code message and a string
-///
-/// These behaviors are useful when subtyping is required; for example, when a
-/// specific library needs an explicit error type. In the example below,
-/// PDBError is derived from StringError:
-///
-/// @code{.cpp}
-/// Expected<int> foo() {
-/// return wpi::make_error<PDBError>(pdb_error_code::dia_failed_loading,
-/// "Additional information");
-/// }
-/// @endcode
-///
-class StringError : public ErrorInfo<StringError> {
-public:
- static char ID;
-
- // Prints EC + S and converts to EC
- StringError(std::error_code EC, const Twine &S = Twine());
-
- // Prints S and converts to EC
- StringError(const Twine &S, std::error_code EC);
-
- void log(raw_ostream &OS) const override;
- std::error_code convertToErrorCode() const override;
-
- const std::string &getMessage() const { return Msg; }
-
-private:
- std::string Msg;
- std::error_code EC;
- const bool PrintMsgOnly = false;
-};
-
-/// Create formatted StringError object.
-template <typename... Ts>
-Error createStringError(std::error_code EC, char const *Fmt,
- const Ts &... Vals) {
- std::string Buffer;
- raw_string_ostream Stream(Buffer);
- Stream << format(Fmt, Vals...);
- return make_error<StringError>(Stream.str(), EC);
-}
-
-Error createStringError(std::error_code EC, char const *Msg);
-
-/// This class wraps a filename and another Error.
-///
-/// In some cases, an error needs to live along a 'source' name, in order to
-/// show more detailed information to the user.
-class FileError final : public ErrorInfo<FileError> {
-
- friend Error createFileError(std::string, Error);
-
-public:
- void log(raw_ostream &OS) const override {
- assert(Err && !FileName.empty() && "Trying to log after takeError().");
- OS << "'" << FileName << "': ";
- Err->log(OS);
- }
-
- Error takeError() { return Error(std::move(Err)); }
-
- std::error_code convertToErrorCode() const override;
-
- // Used by ErrorInfo::classID.
- static char ID;
-
-private:
- FileError(std::string F, std::unique_ptr<ErrorInfoBase> E) {
- assert(E && "Cannot create FileError from Error success value.");
- assert(!F.empty() &&
- "The file name provided to FileError must not be empty.");
- FileName = F;
- Err = std::move(E);
- }
-
- static Error build(std::string F, Error E) {
- return Error(std::unique_ptr<FileError>(new FileError(F, E.takePayload())));
- }
-
- std::string FileName;
- std::unique_ptr<ErrorInfoBase> Err;
-};
-
-/// Concatenate a source file path and/or name with an Error. The resulting
-/// Error is unchecked.
-inline Error createFileError(std::string F, Error E) {
- return FileError::build(F, std::move(E));
-}
-
-Error createFileError(std::string F, ErrorSuccess) = delete;
-
-/// Helper for check-and-exit error handling.
-///
-/// For tool use only. NOT FOR USE IN LIBRARY CODE.
-///
-class ExitOnError {
-public:
- /// Create an error on exit helper.
- ExitOnError(std::string Banner = "", int DefaultErrorExitCode = 1)
- : Banner(std::move(Banner)),
- GetExitCode([=](const Error &) { return DefaultErrorExitCode; }) {}
-
- /// Set the banner string for any errors caught by operator().
- void setBanner(std::string Banner) { this->Banner = std::move(Banner); }
-
- /// Set the exit-code mapper function.
- void setExitCodeMapper(std::function<int(const Error &)> GetExitCode) {
- this->GetExitCode = std::move(GetExitCode);
- }
-
- /// Check Err. If it's in a failure state log the error(s) and exit.
- void operator()(Error Err) const { checkError(std::move(Err)); }
-
- /// Check E. If it's in a success state then return the contained value. If
- /// it's in a failure state log the error(s) and exit.
- template <typename T> T operator()(Expected<T> &&E) const {
- checkError(E.takeError());
- return std::move(*E);
- }
-
- /// Check E. If it's in a success state then return the contained reference. If
- /// it's in a failure state log the error(s) and exit.
- template <typename T> T& operator()(Expected<T&> &&E) const {
- checkError(E.takeError());
- return *E;
- }
-
-private:
- void checkError(Error Err) const {
- if (Err) {
- int ExitCode = GetExitCode(Err);
- logAllUnhandledErrors(std::move(Err), errs(), Banner);
- exit(ExitCode);
- }
- }
-
- std::string Banner;
- std::function<int(const Error &)> GetExitCode;
-};
-
-} // end namespace wpi
-
-#endif // WPIUTIL_WPI_ERROR_H
diff --git a/wpiutil/src/main/native/include/wpi/ErrorHandling.h b/wpiutil/src/main/native/include/wpi/ErrorHandling.h
index 5c89c5e..da9af4b 100644
--- a/wpiutil/src/main/native/include/wpi/ErrorHandling.h
+++ b/wpiutil/src/main/native/include/wpi/ErrorHandling.h
@@ -17,11 +17,9 @@
#include "wpi/Compiler.h"
#include <string>
+#include <string_view>
namespace wpi {
-class StringRef;
- class Twine;
-
/// An error handler callback.
typedef void (*fatal_error_handler_t)(void *user_data,
const std::string& reason,
@@ -73,9 +71,7 @@
bool gen_crash_diag = true);
LLVM_ATTRIBUTE_NORETURN void report_fatal_error(const std::string &reason,
bool gen_crash_diag = true);
-LLVM_ATTRIBUTE_NORETURN void report_fatal_error(StringRef reason,
- bool gen_crash_diag = true);
-LLVM_ATTRIBUTE_NORETURN void report_fatal_error(const Twine &reason,
+LLVM_ATTRIBUTE_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/wpiutil/src/main/native/include/wpi/ErrorOr.h b/wpiutil/src/main/native/include/wpi/ErrorOr.h
deleted file mode 100644
index e1803fd..0000000
--- a/wpiutil/src/main/native/include/wpi/ErrorOr.h
+++ /dev/null
@@ -1,279 +0,0 @@
-//===- llvm/Support/ErrorOr.h - Error Smart Pointer -------------*- C++ -*-===//
-//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-///
-/// \file
-///
-/// Provides ErrorOr<T> smart pointer.
-///
-//===----------------------------------------------------------------------===//
-
-#ifndef WPIUTIL_WPI_ERROROR_H
-#define WPIUTIL_WPI_ERROROR_H
-
-#include "wpi/AlignOf.h"
-#include <cassert>
-#include <system_error>
-#include <type_traits>
-#include <utility>
-
-namespace wpi {
-
-/// Represents either an error or a value T.
-///
-/// ErrorOr<T> is a pointer-like class that represents the result of an
-/// operation. The result is either an error, or a value of type T. This is
-/// designed to emulate the usage of returning a pointer where nullptr indicates
-/// failure. However instead of just knowing that the operation failed, we also
-/// have an error_code and optional user data that describes why it failed.
-///
-/// It is used like the following.
-/// \code
-/// ErrorOr<Buffer> getBuffer();
-///
-/// auto buffer = getBuffer();
-/// if (error_code ec = buffer.getError())
-/// return ec;
-/// buffer->write("adena");
-/// \endcode
-///
-///
-/// Implicit conversion to bool returns true if there is a usable value. The
-/// unary * and -> operators provide pointer like access to the value. Accessing
-/// the value when there is an error has undefined behavior.
-///
-/// When T is a reference type the behavior is slightly different. The reference
-/// is held in a std::reference_wrapper<std::remove_reference<T>::type>, and
-/// there is special handling to make operator -> work as if T was not a
-/// reference.
-///
-/// T cannot be a rvalue reference.
-template<class T>
-class ErrorOr {
- template <class OtherT> friend class ErrorOr;
-
- static const bool isRef = std::is_reference<T>::value;
-
- using wrap = std::reference_wrapper<typename std::remove_reference<T>::type>;
-
-public:
- using storage_type = typename std::conditional<isRef, wrap, T>::type;
-
-private:
- using reference = typename std::remove_reference<T>::type &;
- using const_reference = const typename std::remove_reference<T>::type &;
- using pointer = typename std::remove_reference<T>::type *;
- using const_pointer = const typename std::remove_reference<T>::type *;
-
-public:
- template <class E>
- ErrorOr(E ErrorCode,
- typename std::enable_if<std::is_error_code_enum<E>::value ||
- std::is_error_condition_enum<E>::value,
- void *>::type = nullptr)
- : HasError(true) {
- new (getErrorStorage()) std::error_code(make_error_code(ErrorCode));
- }
-
- ErrorOr(std::error_code EC) : HasError(true) {
- new (getErrorStorage()) std::error_code(EC);
- }
-
- template <class OtherT>
- ErrorOr(OtherT &&Val,
- typename std::enable_if<std::is_convertible<OtherT, T>::value>::type
- * = nullptr)
- : HasError(false) {
- new (getStorage()) storage_type(std::forward<OtherT>(Val));
- }
-
- ErrorOr(const ErrorOr &Other) {
- copyConstruct(Other);
- }
-
- template <class OtherT>
- ErrorOr(
- const ErrorOr<OtherT> &Other,
- typename std::enable_if<std::is_convertible<OtherT, T>::value>::type * =
- nullptr) {
- copyConstruct(Other);
- }
-
- template <class OtherT>
- explicit ErrorOr(
- const ErrorOr<OtherT> &Other,
- typename std::enable_if<
- !std::is_convertible<OtherT, const T &>::value>::type * = nullptr) {
- copyConstruct(Other);
- }
-
- ErrorOr(ErrorOr &&Other) {
- moveConstruct(std::move(Other));
- }
-
- template <class OtherT>
- ErrorOr(
- ErrorOr<OtherT> &&Other,
- typename std::enable_if<std::is_convertible<OtherT, T>::value>::type * =
- nullptr) {
- moveConstruct(std::move(Other));
- }
-
- // This might eventually need SFINAE but it's more complex than is_convertible
- // & I'm too lazy to write it right now.
- template <class OtherT>
- explicit ErrorOr(
- ErrorOr<OtherT> &&Other,
- typename std::enable_if<!std::is_convertible<OtherT, T>::value>::type * =
- nullptr) {
- moveConstruct(std::move(Other));
- }
-
- ErrorOr &operator=(const ErrorOr &Other) {
- copyAssign(Other);
- return *this;
- }
-
- ErrorOr &operator=(ErrorOr &&Other) {
- moveAssign(std::move(Other));
- return *this;
- }
-
- ~ErrorOr() {
- if (!HasError)
- getStorage()->~storage_type();
- }
-
- /// Return false if there is an error.
- explicit operator bool() const {
- return !HasError;
- }
-
- reference get() { return *getStorage(); }
- const_reference get() const { return const_cast<ErrorOr<T> *>(this)->get(); }
-
- std::error_code getError() const {
- return HasError ? *getErrorStorage() : std::error_code();
- }
-
- pointer operator ->() {
- return toPointer(getStorage());
- }
-
- const_pointer operator->() const { return toPointer(getStorage()); }
-
- reference operator *() {
- return *getStorage();
- }
-
- const_reference operator*() const { return *getStorage(); }
-
-private:
- template <class OtherT>
- void copyConstruct(const ErrorOr<OtherT> &Other) {
- if (!Other.HasError) {
- // Get the other value.
- HasError = false;
- new (getStorage()) storage_type(*Other.getStorage());
- } else {
- // Get other's error.
- HasError = true;
- new (getErrorStorage()) std::error_code(Other.getError());
- }
- }
-
- template <class T1>
- static bool compareThisIfSameType(const T1 &a, const T1 &b) {
- return &a == &b;
- }
-
- template <class T1, class T2>
- static bool compareThisIfSameType(const T1 &a, const T2 &b) {
- return false;
- }
-
- template <class OtherT>
- void copyAssign(const ErrorOr<OtherT> &Other) {
- if (compareThisIfSameType(*this, Other))
- return;
-
- this->~ErrorOr();
- new (this) ErrorOr(Other);
- }
-
- template <class OtherT>
- void moveConstruct(ErrorOr<OtherT> &&Other) {
- if (!Other.HasError) {
- // Get the other value.
- HasError = false;
- new (getStorage()) storage_type(std::move(*Other.getStorage()));
- } else {
- // Get other's error.
- HasError = true;
- new (getErrorStorage()) std::error_code(Other.getError());
- }
- }
-
- template <class OtherT>
- void moveAssign(ErrorOr<OtherT> &&Other) {
- if (compareThisIfSameType(*this, Other))
- return;
-
- this->~ErrorOr();
- new (this) ErrorOr(std::move(Other));
- }
-
- pointer toPointer(pointer Val) {
- return Val;
- }
-
- const_pointer toPointer(const_pointer Val) const { return Val; }
-
- pointer toPointer(wrap *Val) {
- return &Val->get();
- }
-
- const_pointer toPointer(const wrap *Val) const { return &Val->get(); }
-
- storage_type *getStorage() {
- assert(!HasError && "Cannot get value when an error exists!");
- return reinterpret_cast<storage_type*>(TStorage.buffer);
- }
-
- const storage_type *getStorage() const {
- assert(!HasError && "Cannot get value when an error exists!");
- return reinterpret_cast<const storage_type*>(TStorage.buffer);
- }
-
- std::error_code *getErrorStorage() {
- assert(HasError && "Cannot get error when a value exists!");
- return reinterpret_cast<std::error_code *>(ErrorStorage.buffer);
- }
-
- const std::error_code *getErrorStorage() const {
- return const_cast<ErrorOr<T> *>(this)->getErrorStorage();
- }
-
- union {
- AlignedCharArrayUnion<storage_type> TStorage;
- AlignedCharArrayUnion<std::error_code> ErrorStorage;
- };
- bool HasError : 1;
-};
-
-template <class T, class E>
-typename std::enable_if<std::is_error_code_enum<E>::value ||
- std::is_error_condition_enum<E>::value,
- bool>::type
-operator==(const ErrorOr<T> &Err, E Code) {
- return Err.getError() == Code;
-}
-
-} // end namespace wpi
-
-#endif // LLVM_SUPPORT_ERROROR_H
diff --git a/wpiutil/src/main/native/include/wpi/EventLoopRunner.h b/wpiutil/src/main/native/include/wpi/EventLoopRunner.h
index 8150091..5d35d40 100644
--- a/wpiutil/src/main/native/include/wpi/EventLoopRunner.h
+++ b/wpiutil/src/main/native/include/wpi/EventLoopRunner.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_EVENTLOOPRUNNER_H_
#define WPIUTIL_WPI_EVENTLOOPRUNNER_H_
diff --git a/wpiutil/src/main/native/include/wpi/FileSystem.h b/wpiutil/src/main/native/include/wpi/FileSystem.h
deleted file mode 100644
index 668eea6..0000000
--- a/wpiutil/src/main/native/include/wpi/FileSystem.h
+++ /dev/null
@@ -1,1023 +0,0 @@
-//===- llvm/Support/FileSystem.h - File System OS Concept -------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file declares the wpi::sys::fs namespace. It is designed after
-// TR2/boost filesystem (v3), but modified to remove exception handling and the
-// path class.
-//
-// All functions return an error_code and their actual work via the last out
-// argument. The out argument is defined if and only if errc::success is
-// returned. A function may return any error code in the generic or system
-// category. However, they shall be equivalent to any error conditions listed
-// in each functions respective documentation if the condition applies. [ note:
-// this does not guarantee that error_code will be in the set of explicitly
-// listed codes, but it does guarantee that if any of the explicitly listed
-// errors occur, the correct error_code will be used ]. All functions may
-// return errc::not_enough_memory if there is not enough memory to complete the
-// operation.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef WPIUTIL_WPI_FILESYSTEM_H
-#define WPIUTIL_WPI_FILESYSTEM_H
-
-#include "wpi/Chrono.h"
-#include "wpi/SmallString.h"
-#include "wpi/StringRef.h"
-#include "wpi/Twine.h"
-#include "wpi/Error.h"
-#include "wpi/ErrorHandling.h"
-#include "wpi/ErrorOr.h"
-#include <cassert>
-#include <cstdint>
-#include <ctime>
-#include <memory>
-#include <stack>
-#include <string>
-#include <system_error>
-#include <tuple>
-#include <vector>
-
-#include <sys/stat.h>
-
-namespace wpi {
-namespace sys {
-namespace fs {
-
-#if defined(_WIN32)
-// A Win32 HANDLE is a typedef of void*
-using file_t = void *;
-#else
-using file_t = int;
-#endif
-
-extern const file_t kInvalidFile;
-
-/// An enumeration for the file system's view of the type.
-enum class file_type {
- status_error,
- file_not_found,
- regular_file,
- directory_file,
- symlink_file,
- block_file,
- character_file,
- fifo_file,
- socket_file,
- type_unknown
-};
-
-/// space_info - Self explanatory.
-struct space_info {
- uint64_t capacity;
- uint64_t free;
- uint64_t available;
-};
-
-enum perms {
- no_perms = 0,
- owner_read = 0400,
- owner_write = 0200,
- owner_exe = 0100,
- owner_all = owner_read | owner_write | owner_exe,
- group_read = 040,
- group_write = 020,
- group_exe = 010,
- group_all = group_read | group_write | group_exe,
- others_read = 04,
- others_write = 02,
- others_exe = 01,
- others_all = others_read | others_write | others_exe,
- all_read = owner_read | group_read | others_read,
- all_write = owner_write | group_write | others_write,
- all_exe = owner_exe | group_exe | others_exe,
- all_all = owner_all | group_all | others_all,
- set_uid_on_exe = 04000,
- set_gid_on_exe = 02000,
- sticky_bit = 01000,
- all_perms = all_all | set_uid_on_exe | set_gid_on_exe | sticky_bit,
- perms_not_known = 0xFFFF
-};
-
-// Helper functions so that you can use & and | to manipulate perms bits:
-inline perms operator|(perms l, perms r) {
- return static_cast<perms>(static_cast<unsigned short>(l) |
- static_cast<unsigned short>(r));
-}
-inline perms operator&(perms l, perms r) {
- return static_cast<perms>(static_cast<unsigned short>(l) &
- static_cast<unsigned short>(r));
-}
-inline perms &operator|=(perms &l, perms r) {
- l = l | r;
- return l;
-}
-inline perms &operator&=(perms &l, perms r) {
- l = l & r;
- return l;
-}
-inline perms operator~(perms x) {
- // Avoid UB by explicitly truncating the (unsigned) ~ result.
- return static_cast<perms>(
- static_cast<unsigned short>(~static_cast<unsigned short>(x)));
-}
-
-class UniqueID {
- uint64_t Device;
- uint64_t File;
-
-public:
- UniqueID() = default;
- UniqueID(uint64_t Device, uint64_t File) : Device(Device), File(File) {}
-
- bool operator==(const UniqueID &Other) const {
- return Device == Other.Device && File == Other.File;
- }
- bool operator!=(const UniqueID &Other) const { return !(*this == Other); }
- bool operator<(const UniqueID &Other) const {
- return std::tie(Device, File) < std::tie(Other.Device, Other.File);
- }
-
- uint64_t getDevice() const { return Device; }
- uint64_t getFile() const { return File; }
-};
-
-/// Represents the result of a call to directory_iterator::status(). This is a
-/// subset of the information returned by a regular sys::fs::status() call, and
-/// represents the information provided by Windows FileFirstFile/FindNextFile.
-class basic_file_status {
-protected:
- #ifndef _WIN32
- time_t fs_st_atime = 0;
- time_t fs_st_mtime = 0;
- uint32_t fs_st_atime_nsec = 0;
- uint32_t fs_st_mtime_nsec = 0;
- uid_t fs_st_uid = 0;
- gid_t fs_st_gid = 0;
- off_t fs_st_size = 0;
- #else
- uint32_t LastAccessedTimeHigh = 0;
- uint32_t LastAccessedTimeLow = 0;
- uint32_t LastWriteTimeHigh = 0;
- uint32_t LastWriteTimeLow = 0;
- uint32_t FileSizeHigh = 0;
- uint32_t FileSizeLow = 0;
- #endif
- file_type Type = file_type::status_error;
- perms Perms = perms_not_known;
-
-public:
- basic_file_status() = default;
-
- explicit basic_file_status(file_type Type) : Type(Type) {}
-
- #ifndef _WIN32
- basic_file_status(file_type Type, perms Perms, time_t ATime,
- uint32_t ATimeNSec, time_t MTime, uint32_t MTimeNSec,
- uid_t UID, gid_t GID, off_t Size)
- : fs_st_atime(ATime), fs_st_mtime(MTime),
- fs_st_atime_nsec(ATimeNSec), fs_st_mtime_nsec(MTimeNSec),
- fs_st_uid(UID), fs_st_gid(GID),
- fs_st_size(Size), Type(Type), Perms(Perms) {}
- #else
- basic_file_status(file_type Type, perms Perms, uint32_t LastAccessTimeHigh,
- uint32_t LastAccessTimeLow, uint32_t LastWriteTimeHigh,
- uint32_t LastWriteTimeLow, uint32_t FileSizeHigh,
- uint32_t FileSizeLow)
- : LastAccessedTimeHigh(LastAccessTimeHigh),
- LastAccessedTimeLow(LastAccessTimeLow),
- LastWriteTimeHigh(LastWriteTimeHigh),
- LastWriteTimeLow(LastWriteTimeLow), FileSizeHigh(FileSizeHigh),
- FileSizeLow(FileSizeLow), Type(Type), Perms(Perms) {}
- #endif
-
- // getters
- file_type type() const { return Type; }
- perms permissions() const { return Perms; }
-
- /// The file access time as reported from the underlying file system.
- ///
- /// Also see comments on \c getLastModificationTime() related to the precision
- /// of the returned value.
- TimePoint<> getLastAccessedTime() const;
-
- /// The file modification time as reported from the underlying file system.
- ///
- /// The returned value allows for nanosecond precision but the actual
- /// resolution is an implementation detail of the underlying file system.
- /// There is no guarantee for what kind of resolution you can expect, the
- /// resolution can differ across platforms and even across mountpoints on the
- /// same machine.
- TimePoint<> getLastModificationTime() const;
-
- #ifndef _WIN32
- uint32_t getUser() const { return fs_st_uid; }
- uint32_t getGroup() const { return fs_st_gid; }
- uint64_t getSize() const { return fs_st_size; }
- #else
- uint32_t getUser() const {
- return 9999; // Not applicable to Windows, so...
- }
-
- uint32_t getGroup() const {
- return 9999; // Not applicable to Windows, so...
- }
-
- uint64_t getSize() const {
- return (uint64_t(FileSizeHigh) << 32) + FileSizeLow;
- }
- #endif
-
- // setters
- void type(file_type v) { Type = v; }
- void permissions(perms p) { Perms = p; }
-};
-
-/// Represents the result of a call to sys::fs::status().
-class file_status : public basic_file_status {
- friend bool equivalent(file_status A, file_status B);
-
- #ifndef _WIN32
- dev_t fs_st_dev = 0;
- nlink_t fs_st_nlinks = 0;
- ino_t fs_st_ino = 0;
- #else
- uint32_t NumLinks = 0;
- uint32_t VolumeSerialNumber = 0;
- uint32_t FileIndexHigh = 0;
- uint32_t FileIndexLow = 0;
- #endif
-
-public:
- file_status() = default;
-
- explicit file_status(file_type Type) : basic_file_status(Type) {}
-
- #ifndef _WIN32
- file_status(file_type Type, perms Perms, dev_t Dev, nlink_t Links, ino_t Ino,
- time_t ATime, uint32_t ATimeNSec,
- time_t MTime, uint32_t MTimeNSec,
- uid_t UID, gid_t GID, off_t Size)
- : basic_file_status(Type, Perms, ATime, ATimeNSec, MTime, MTimeNSec,
- UID, GID, Size),
- fs_st_dev(Dev), fs_st_nlinks(Links), fs_st_ino(Ino) {}
- #else
- file_status(file_type Type, perms Perms, uint32_t LinkCount,
- uint32_t LastAccessTimeHigh, uint32_t LastAccessTimeLow,
- uint32_t LastWriteTimeHigh, uint32_t LastWriteTimeLow,
- uint32_t VolumeSerialNumber, uint32_t FileSizeHigh,
- uint32_t FileSizeLow, uint32_t FileIndexHigh,
- uint32_t FileIndexLow)
- : basic_file_status(Type, Perms, LastAccessTimeHigh, LastAccessTimeLow,
- LastWriteTimeHigh, LastWriteTimeLow, FileSizeHigh,
- FileSizeLow),
- NumLinks(LinkCount), VolumeSerialNumber(VolumeSerialNumber),
- FileIndexHigh(FileIndexHigh), FileIndexLow(FileIndexLow) {}
- #endif
-
- UniqueID getUniqueID() const;
- uint32_t getLinkCount() const;
-};
-
-/// @}
-/// @name Physical Operators
-/// @{
-
-/// Make \a path an absolute path.
-///
-/// Makes \a path absolute using the \a current_directory if it is not already.
-/// An empty \a path will result in the \a current_directory.
-///
-/// /absolute/path => /absolute/path
-/// relative/../path => <current-directory>/relative/../path
-///
-/// @param path A path that is modified to be an absolute path.
-void make_absolute(const Twine ¤t_directory, SmallVectorImpl<char> &path);
-
-/// Make \a path an absolute path.
-///
-/// Makes \a path absolute using the current directory if it is not already. An
-/// empty \a path will result in the current directory.
-///
-/// /absolute/path => /absolute/path
-/// relative/../path => <current-directory>/relative/../path
-///
-/// @param path A path that is modified to be an absolute path.
-/// @returns errc::success if \a path has been made absolute, otherwise a
-/// platform-specific error_code.
-std::error_code make_absolute(SmallVectorImpl<char> &path);
-
-/// Get the current path.
-///
-/// @param result Holds the current path on return.
-/// @returns errc::success if the current path has been stored in result,
-/// otherwise a platform-specific error_code.
-std::error_code current_path(SmallVectorImpl<char> &result);
-
-/// @}
-/// @name Physical Observers
-/// @{
-
-/// Does file exist?
-///
-/// @param status A basic_file_status previously returned from stat.
-/// @returns True if the file represented by status exists, false if it does
-/// not.
-bool exists(const basic_file_status &status);
-
-enum class AccessMode { Exist, Write, Execute };
-
-/// Can the file be accessed?
-///
-/// @param Path Input path.
-/// @returns errc::success if the path can be accessed, otherwise a
-/// platform-specific error_code.
-std::error_code access(const Twine &Path, AccessMode Mode);
-
-/// Does file exist?
-///
-/// @param Path Input path.
-/// @returns True if it exists, false otherwise.
-inline bool exists(const Twine &Path) {
- return !access(Path, AccessMode::Exist);
-}
-
-/// Can we write this file?
-///
-/// @param Path Input path.
-/// @returns True if we can write to it, false otherwise.
-inline bool can_write(const Twine &Path) {
- return !access(Path, AccessMode::Write);
-}
-
-/// Do file_status's represent the same thing?
-///
-/// @param A Input file_status.
-/// @param B Input file_status.
-///
-/// assert(status_known(A) || status_known(B));
-///
-/// @returns True if A and B both represent the same file system entity, false
-/// otherwise.
-bool equivalent(file_status A, file_status B);
-
-/// Do paths represent the same thing?
-///
-/// assert(status_known(A) || status_known(B));
-///
-/// @param A Input path A.
-/// @param B Input path B.
-/// @param result Set to true if stat(A) and stat(B) have the same device and
-/// inode (or equivalent).
-/// @returns errc::success if result has been successfully set, otherwise a
-/// platform-specific error_code.
-std::error_code equivalent(const Twine &A, const Twine &B, bool &result);
-
-/// Simpler version of equivalent for clients that don't need to
-/// differentiate between an error and false.
-inline bool equivalent(const Twine &A, const Twine &B) {
- bool result;
- return !equivalent(A, B, result) && result;
-}
-
-/// Does status represent a directory?
-///
-/// @param Path The path to get the type of.
-/// @param Follow For symbolic links, indicates whether to return the file type
-/// of the link itself, or of the target.
-/// @returns A value from the file_type enumeration indicating the type of file.
-file_type get_file_type(const Twine &Path, bool Follow = true);
-
-/// Does status represent a directory?
-///
-/// @param status A basic_file_status previously returned from status.
-/// @returns status.type() == file_type::directory_file.
-bool is_directory(const basic_file_status &status);
-
-/// Is path a directory?
-///
-/// @param path Input path.
-/// @param result Set to true if \a path is a directory (after following
-/// symlinks, false if it is not. Undefined otherwise.
-/// @returns errc::success if result has been successfully set, otherwise a
-/// platform-specific error_code.
-std::error_code is_directory(const Twine &path, bool &result);
-
-/// Simpler version of is_directory for clients that don't need to
-/// differentiate between an error and false.
-inline bool is_directory(const Twine &Path) {
- bool Result;
- return !is_directory(Path, Result) && Result;
-}
-
-/// Does status represent a regular file?
-///
-/// @param status A basic_file_status previously returned from status.
-/// @returns status_known(status) && status.type() == file_type::regular_file.
-bool is_regular_file(const basic_file_status &status);
-
-/// Is path a regular file?
-///
-/// @param path Input path.
-/// @param result Set to true if \a path is a regular file (after following
-/// symlinks), false if it is not. Undefined otherwise.
-/// @returns errc::success if result has been successfully set, otherwise a
-/// platform-specific error_code.
-std::error_code is_regular_file(const Twine &path, bool &result);
-
-/// Simpler version of is_regular_file for clients that don't need to
-/// differentiate between an error and false.
-inline bool is_regular_file(const Twine &Path) {
- bool Result;
- if (is_regular_file(Path, Result))
- return false;
- return Result;
-}
-
-/// Does status represent a symlink file?
-///
-/// @param status A basic_file_status previously returned from status.
-/// @returns status_known(status) && status.type() == file_type::symlink_file.
-bool is_symlink_file(const basic_file_status &status);
-
-/// Is path a symlink file?
-///
-/// @param path Input path.
-/// @param result Set to true if \a path is a symlink file, false if it is not.
-/// Undefined otherwise.
-/// @returns errc::success if result has been successfully set, otherwise a
-/// platform-specific error_code.
-std::error_code is_symlink_file(const Twine &path, bool &result);
-
-/// Simpler version of is_symlink_file for clients that don't need to
-/// differentiate between an error and false.
-inline bool is_symlink_file(const Twine &Path) {
- bool Result;
- if (is_symlink_file(Path, Result))
- return false;
- return Result;
-}
-
-/// Does this status represent something that exists but is not a
-/// directory or regular file?
-///
-/// @param status A basic_file_status previously returned from status.
-/// @returns exists(s) && !is_regular_file(s) && !is_directory(s)
-bool is_other(const basic_file_status &status);
-
-/// Is path something that exists but is not a directory,
-/// regular file, or symlink?
-///
-/// @param path Input path.
-/// @param result Set to true if \a path exists, but is not a directory, regular
-/// file, or a symlink, false if it does not. Undefined otherwise.
-/// @returns errc::success if result has been successfully set, otherwise a
-/// platform-specific error_code.
-std::error_code is_other(const Twine &path, bool &result);
-
-/// Get file status as if by POSIX stat().
-///
-/// @param path Input path.
-/// @param result Set to the file status.
-/// @param follow When true, follows symlinks. Otherwise, the symlink itself is
-/// statted.
-/// @returns errc::success if result has been successfully set, otherwise a
-/// platform-specific error_code.
-std::error_code status(const Twine &path, file_status &result,
- bool follow = true);
-
-/// A version for when a file descriptor is already available.
-std::error_code status(int FD, file_status &Result);
-
-/// Is status available?
-///
-/// @param s Input file status.
-/// @returns True if status() != status_error.
-bool status_known(const basic_file_status &s);
-
-/// Is status available?
-///
-/// @param path Input path.
-/// @param result Set to true if status() != status_error.
-/// @returns errc::success if result has been successfully set, otherwise a
-/// platform-specific error_code.
-std::error_code status_known(const Twine &path, bool &result);
-
-enum CreationDisposition : unsigned {
- /// CD_CreateAlways - When opening a file:
- /// * If it already exists, truncate it.
- /// * If it does not already exist, create a new file.
- CD_CreateAlways = 0,
-
- /// CD_CreateNew - When opening a file:
- /// * If it already exists, fail.
- /// * If it does not already exist, create a new file.
- CD_CreateNew = 1,
-
- /// CD_OpenExisting - When opening a file:
- /// * If it already exists, open the file with the offset set to 0.
- /// * If it does not already exist, fail.
- CD_OpenExisting = 2,
-
- /// CD_OpenAlways - When opening a file:
- /// * If it already exists, open the file with the offset set to 0.
- /// * If it does not already exist, create a new file.
- CD_OpenAlways = 3,
-};
-
-enum FileAccess : unsigned {
- FA_Read = 1,
- FA_Write = 2,
-};
-
-enum OpenFlags : unsigned {
- OF_None = 0,
- F_None = 0, // For compatibility
-
- /// The file should be opened in text mode on platforms that make this
- /// distinction.
- OF_Text = 1,
- F_Text = 1, // For compatibility
-
- /// The file should be opened in append mode.
- OF_Append = 2,
- F_Append = 2, // For compatibility
-
- /// Delete the file on close. Only makes a difference on windows.
- OF_Delete = 4,
-
- /// When a child process is launched, this file should remain open in the
- /// child process.
- OF_ChildInherit = 8,
-
- /// Force files Atime to be updated on access. Only makes a difference on windows.
- OF_UpdateAtime = 16,
-};
-
-inline OpenFlags operator|(OpenFlags A, OpenFlags B) {
- return OpenFlags(unsigned(A) | unsigned(B));
-}
-
-inline OpenFlags &operator|=(OpenFlags &A, OpenFlags B) {
- A = A | B;
- return A;
-}
-
-inline FileAccess operator|(FileAccess A, FileAccess B) {
- return FileAccess(unsigned(A) | unsigned(B));
-}
-
-inline FileAccess &operator|=(FileAccess &A, FileAccess B) {
- A = A | B;
- return A;
-}
-
-/// @brief Opens a file with the specified creation disposition, access mode,
-/// and flags and returns a file descriptor.
-///
-/// The caller is responsible for closing the file descriptor once they are
-/// finished with it.
-///
-/// @param Name The path of the file to open, relative or absolute.
-/// @param ResultFD If the file could be opened successfully, its descriptor
-/// is stored in this location. Otherwise, this is set to -1.
-/// @param Disp Value specifying the existing-file behavior.
-/// @param Access Value specifying whether to open the file in read, write, or
-/// read-write mode.
-/// @param Flags Additional flags.
-/// @param Mode The access permissions of the file, represented in octal.
-/// @returns errc::success if \a Name has been opened, otherwise a
-/// platform-specific error_code.
-std::error_code openFile(const Twine &Name, int &ResultFD,
- CreationDisposition Disp, FileAccess Access,
- OpenFlags Flags, unsigned Mode = 0666);
-
-/// @brief Opens a file with the specified creation disposition, access mode,
-/// and flags and returns a platform-specific file object.
-///
-/// The caller is responsible for closing the file object once they are
-/// finished with it.
-///
-/// @param Name The path of the file to open, relative or absolute.
-/// @param Disp Value specifying the existing-file behavior.
-/// @param Access Value specifying whether to open the file in read, write, or
-/// read-write mode.
-/// @param Flags Additional flags.
-/// @param Mode The access permissions of the file, represented in octal.
-/// @returns errc::success if \a Name has been opened, otherwise a
-/// platform-specific error_code.
-Expected<file_t> openNativeFile(const Twine &Name, CreationDisposition Disp,
- FileAccess Access, OpenFlags Flags,
- unsigned Mode = 0666);
-
-/// @brief Opens the file with the given name in a write-only or read-write
-/// mode, returning its open file descriptor. If the file does not exist, it
-/// is created.
-///
-/// The caller is responsible for closing the file descriptor once they are
-/// finished with it.
-///
-/// @param Name The path of the file to open, relative or absolute.
-/// @param ResultFD If the file could be opened successfully, its descriptor
-/// is stored in this location. Otherwise, this is set to -1.
-/// @param Flags Additional flags used to determine whether the file should be
-/// opened in, for example, read-write or in write-only mode.
-/// @param Mode The access permissions of the file, represented in octal.
-/// @returns errc::success if \a Name has been opened, otherwise a
-/// platform-specific error_code.
-inline std::error_code
-openFileForWrite(const Twine &Name, int &ResultFD,
- CreationDisposition Disp = CD_CreateAlways,
- OpenFlags Flags = OF_None, unsigned Mode = 0666) {
- return openFile(Name, ResultFD, Disp, FA_Write, Flags, Mode);
-}
-
-/// @brief Opens the file with the given name in a write-only or read-write
-/// mode, returning its open file descriptor. If the file does not exist, it
-/// is created.
-///
-/// The caller is responsible for closing the freeing the file once they are
-/// finished with it.
-///
-/// @param Name The path of the file to open, relative or absolute.
-/// @param Flags Additional flags used to determine whether the file should be
-/// opened in, for example, read-write or in write-only mode.
-/// @param Mode The access permissions of the file, represented in octal.
-/// @returns a platform-specific file descriptor if \a Name has been opened,
-/// otherwise an error object.
-inline Expected<file_t> openNativeFileForWrite(const Twine &Name,
- CreationDisposition Disp,
- OpenFlags Flags,
- unsigned Mode = 0666) {
- return openNativeFile(Name, Disp, FA_Write, Flags, Mode);
-}
-
-/// @brief Opens the file with the given name in a write-only or read-write
-/// mode, returning its open file descriptor. If the file does not exist, it
-/// is created.
-///
-/// The caller is responsible for closing the file descriptor once they are
-/// finished with it.
-///
-/// @param Name The path of the file to open, relative or absolute.
-/// @param ResultFD If the file could be opened successfully, its descriptor
-/// is stored in this location. Otherwise, this is set to -1.
-/// @param Flags Additional flags used to determine whether the file should be
-/// opened in, for example, read-write or in write-only mode.
-/// @param Mode The access permissions of the file, represented in octal.
-/// @returns errc::success if \a Name has been opened, otherwise a
-/// platform-specific error_code.
-inline std::error_code openFileForReadWrite(const Twine &Name, int &ResultFD,
- CreationDisposition Disp,
- OpenFlags Flags,
- unsigned Mode = 0666) {
- return openFile(Name, ResultFD, Disp, FA_Write | FA_Read, Flags, Mode);
-}
-
-/// @brief Opens the file with the given name in a write-only or read-write
-/// mode, returning its open file descriptor. If the file does not exist, it
-/// is created.
-///
-/// The caller is responsible for closing the freeing the file once they are
-/// finished with it.
-///
-/// @param Name The path of the file to open, relative or absolute.
-/// @param Flags Additional flags used to determine whether the file should be
-/// opened in, for example, read-write or in write-only mode.
-/// @param Mode The access permissions of the file, represented in octal.
-/// @returns a platform-specific file descriptor if \a Name has been opened,
-/// otherwise an error object.
-inline Expected<file_t> openNativeFileForReadWrite(const Twine &Name,
- CreationDisposition Disp,
- OpenFlags Flags,
- unsigned Mode = 0666) {
- return openNativeFile(Name, Disp, FA_Write | FA_Read, Flags, Mode);
-}
-
-/// @brief Opens the file with the given name in a read-only mode, returning
-/// its open file descriptor.
-///
-/// The caller is responsible for closing the file descriptor once they are
-/// finished with it.
-///
-/// @param Name The path of the file to open, relative or absolute.
-/// @param ResultFD If the file could be opened successfully, its descriptor
-/// is stored in this location. Otherwise, this is set to -1.
-/// @param RealPath If nonnull, extra work is done to determine the real path
-/// of the opened file, and that path is stored in this
-/// location.
-/// @returns errc::success if \a Name has been opened, otherwise a
-/// platform-specific error_code.
-std::error_code openFileForRead(const Twine &Name, int &ResultFD,
- OpenFlags Flags = OF_None,
- SmallVectorImpl<char> *RealPath = nullptr);
-
-/// @brief Opens the file with the given name in a read-only mode, returning
-/// its open file descriptor.
-///
-/// The caller is responsible for closing the freeing the file once they are
-/// finished with it.
-///
-/// @param Name The path of the file to open, relative or absolute.
-/// @param RealPath If nonnull, extra work is done to determine the real path
-/// of the opened file, and that path is stored in this
-/// location.
-/// @returns a platform-specific file descriptor if \a Name has been opened,
-/// otherwise an error object.
-Expected<file_t>
-openNativeFileForRead(const Twine &Name, OpenFlags Flags = OF_None,
- SmallVectorImpl<char> *RealPath = nullptr);
-
-/// @brief Close the file object. This should be used instead of ::close for
-/// portability.
-///
-/// @param F On input, this is the file to close. On output, the file is
-/// set to kInvalidFile.
-void closeFile(file_t &F);
-
-std::error_code getUniqueID(const Twine Path, UniqueID &Result);
-
-/// This class represents a memory mapped file. It is based on
-/// boost::iostreams::mapped_file.
-class mapped_file_region {
-public:
- enum mapmode {
- readonly, ///< May only access map via const_data as read only.
- readwrite, ///< May access map via data and modify it. Written to path.
- priv ///< May modify via data, but changes are lost on destruction.
- };
-
-private:
- /// Platform-specific mapping state.
- size_t Size;
- void *Mapping;
-#ifdef _WIN32
- void *FileHandle;
-#endif
- mapmode Mode;
-
- std::error_code init(int FD, uint64_t Offset, mapmode Mode);
-
-public:
- mapped_file_region() = delete;
- mapped_file_region(mapped_file_region&) = delete;
- mapped_file_region &operator =(mapped_file_region&) = delete;
-
- /// \param fd An open file descriptor to map. mapped_file_region takes
- /// ownership if closefd is true. It must have been opended in the correct
- /// mode.
- mapped_file_region(int fd, mapmode mode, size_t length, uint64_t offset,
- std::error_code &ec);
-
- ~mapped_file_region();
-
- size_t size() const;
- char *data() const;
-
- /// Get a const view of the data. Modifying this memory has undefined
- /// behavior.
- const char *const_data() const;
-
- /// \returns The minimum alignment offset must be.
- static int alignment();
-};
-
-/// @}
-/// @name Iterators
-/// @{
-
-/// directory_entry - A single entry in a directory. Caches the status either
-/// from the result of the iteration syscall, or the first time status is
-/// called.
-class directory_entry {
- std::string Path;
- file_type Type; // Most platforms can provide this.
- bool FollowSymlinks; // Affects the behavior of status().
- basic_file_status Status; // If available.
-
-public:
- explicit directory_entry(const Twine &Path, bool FollowSymlinks = true,
- file_type Type = file_type::type_unknown,
- basic_file_status Status = basic_file_status())
- : Path(Path.str()), Type(Type), FollowSymlinks(FollowSymlinks),
- Status(Status) {}
-
- directory_entry() = default;
-
- void replace_filename(const Twine &Filename, file_type Type,
- basic_file_status Status = basic_file_status());
-
- const std::string &path() const { return Path; }
- ErrorOr<basic_file_status> status() const;
- file_type type() const {
- if (Type != file_type::type_unknown)
- return Type;
- auto S = status();
- return S ? S->type() : file_type::type_unknown;
- }
-
- bool operator==(const directory_entry& RHS) const { return Path == RHS.Path; }
- bool operator!=(const directory_entry& RHS) const { return !(*this == RHS); }
- bool operator< (const directory_entry& RHS) const;
- bool operator<=(const directory_entry& RHS) const;
- bool operator> (const directory_entry& RHS) const;
- bool operator>=(const directory_entry& RHS) const;
-};
-
-namespace detail {
-
- struct DirIterState;
-
- std::error_code directory_iterator_construct(DirIterState &, StringRef, bool);
- std::error_code directory_iterator_increment(DirIterState &);
- std::error_code directory_iterator_destruct(DirIterState &);
-
- /// Keeps state for the directory_iterator.
- struct DirIterState {
- ~DirIterState() {
- directory_iterator_destruct(*this);
- }
-
- intptr_t IterationHandle = 0;
- directory_entry CurrentEntry;
- };
-
-} // end namespace detail
-
-/// directory_iterator - Iterates through the entries in path. There is no
-/// operator++ because we need an error_code. If it's really needed we can make
-/// it call report_fatal_error on error.
-class directory_iterator {
- std::shared_ptr<detail::DirIterState> State;
- bool FollowSymlinks = true;
-
-public:
- explicit directory_iterator(const Twine &path, std::error_code &ec,
- bool follow_symlinks = true)
- : FollowSymlinks(follow_symlinks) {
- State = std::make_shared<detail::DirIterState>();
- SmallString<128> path_storage;
- ec = detail::directory_iterator_construct(
- *State, path.toStringRef(path_storage), FollowSymlinks);
- }
-
- explicit directory_iterator(const directory_entry &de, std::error_code &ec,
- bool follow_symlinks = true)
- : FollowSymlinks(follow_symlinks) {
- State = std::make_shared<detail::DirIterState>();
- ec = detail::directory_iterator_construct(
- *State, de.path(), FollowSymlinks);
- }
-
- /// Construct end iterator.
- directory_iterator() = default;
-
- // No operator++ because we need error_code.
- directory_iterator &increment(std::error_code &ec) {
- ec = directory_iterator_increment(*State);
- return *this;
- }
-
- const directory_entry &operator*() const { return State->CurrentEntry; }
- const directory_entry *operator->() const { return &State->CurrentEntry; }
-
- bool operator==(const directory_iterator &RHS) const {
- if (State == RHS.State)
- return true;
- if (!RHS.State)
- return State->CurrentEntry == directory_entry();
- if (!State)
- return RHS.State->CurrentEntry == directory_entry();
- return State->CurrentEntry == RHS.State->CurrentEntry;
- }
-
- bool operator!=(const directory_iterator &RHS) const {
- return !(*this == RHS);
- }
-};
-
-namespace detail {
-
- /// Keeps state for the recursive_directory_iterator.
- struct RecDirIterState {
- std::stack<directory_iterator, std::vector<directory_iterator>> Stack;
- uint16_t Level = 0;
- bool HasNoPushRequest = false;
- };
-
-} // end namespace detail
-
-/// recursive_directory_iterator - Same as directory_iterator except for it
-/// recurses down into child directories.
-class recursive_directory_iterator {
- std::shared_ptr<detail::RecDirIterState> State;
- bool Follow;
-
-public:
- recursive_directory_iterator() = default;
- explicit recursive_directory_iterator(const Twine &path, std::error_code &ec,
- bool follow_symlinks = true)
- : State(std::make_shared<detail::RecDirIterState>()),
- Follow(follow_symlinks) {
- State->Stack.push(directory_iterator(path, ec, Follow));
- if (State->Stack.top() == directory_iterator())
- State.reset();
- }
-
- // No operator++ because we need error_code.
- recursive_directory_iterator &increment(std::error_code &ec) {
- const directory_iterator end_itr = {};
-
- if (State->HasNoPushRequest)
- State->HasNoPushRequest = false;
- else {
- file_type type = State->Stack.top()->type();
- if (type == file_type::symlink_file && Follow) {
- // Resolve the symlink: is it a directory to recurse into?
- ErrorOr<basic_file_status> status = State->Stack.top()->status();
- if (status)
- type = status->type();
- // Otherwise broken symlink, and we'll continue.
- }
- if (type == file_type::directory_file) {
- State->Stack.push(directory_iterator(*State->Stack.top(), ec, Follow));
- if (State->Stack.top() != end_itr) {
- ++State->Level;
- return *this;
- }
- State->Stack.pop();
- }
- }
-
- while (!State->Stack.empty()
- && State->Stack.top().increment(ec) == end_itr) {
- State->Stack.pop();
- --State->Level;
- }
-
- // Check if we are done. If so, create an end iterator.
- if (State->Stack.empty())
- State.reset();
-
- return *this;
- }
-
- const directory_entry &operator*() const { return *State->Stack.top(); }
- const directory_entry *operator->() const { return &*State->Stack.top(); }
-
- // observers
- /// Gets the current level. Starting path is at level 0.
- int level() const { return State->Level; }
-
- /// Returns true if no_push has been called for this directory_entry.
- bool no_push_request() const { return State->HasNoPushRequest; }
-
- // modifiers
- /// Goes up one level if Level > 0.
- void pop() {
- assert(State && "Cannot pop an end iterator!");
- assert(State->Level > 0 && "Cannot pop an iterator with level < 1");
-
- const directory_iterator end_itr = {};
- std::error_code ec;
- do {
- if (ec) {
- //report_fatal_error("Error incrementing directory iterator.");
- while (!State->Stack.empty()) State->Stack.pop();
- break;
- }
- State->Stack.pop();
- --State->Level;
- } while (!State->Stack.empty()
- && State->Stack.top().increment(ec) == end_itr);
-
- // Check if we are done. If so, create an end iterator.
- if (State->Stack.empty())
- State.reset();
- }
-
- /// Does not go down into the current directory_entry.
- void no_push() { State->HasNoPushRequest = true; }
-
- bool operator==(const recursive_directory_iterator &RHS) const {
- return State == RHS.State;
- }
-
- bool operator!=(const recursive_directory_iterator &RHS) const {
- return !(*this == RHS);
- }
-};
-
-/// @}
-
-} // end namespace fs
-} // end namespace sys
-} // end namespace wpi
-
-#endif // LLVM_SUPPORT_FILESYSTEM_H
diff --git a/wpiutil/src/main/native/include/wpi/Format.h b/wpiutil/src/main/native/include/wpi/Format.h
deleted file mode 100644
index cb8b26b..0000000
--- a/wpiutil/src/main/native/include/wpi/Format.h
+++ /dev/null
@@ -1,265 +0,0 @@
-//===- Format.h - Efficient printf-style formatting for streams -*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file implements the format() function, which can be used with other
-// LLVM subsystems to provide printf-style formatting. This gives all the power
-// and risk of printf. This can be used like this (with raw_ostreams as an
-// example):
-//
-// OS << "mynumber: " << format("%4.5f", 1234.412) << '\n';
-//
-// Or if you prefer:
-//
-// OS << format("mynumber: %4.5f\n", 1234.412);
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef WPIUTIL_WPI_FORMAT_H
-#define WPIUTIL_WPI_FORMAT_H
-
-#include "wpi/ArrayRef.h"
-#include "wpi/StringRef.h"
-#include <cassert>
-#include <cstdint>
-#include <cstdio>
-#include <optional>
-#include <tuple>
-#include <utility>
-
-namespace wpi {
-
-/// This is a helper class used for handling formatted output. It is the
-/// abstract base class of a templated derived class.
-class format_object_base {
-protected:
- const char *Fmt;
- ~format_object_base() = default; // Disallow polymorphic deletion.
- format_object_base(const format_object_base &) = default;
- virtual void home(); // Out of line virtual method.
-
- /// Call snprintf() for this object, on the given buffer and size.
- virtual int snprint(char *Buffer, unsigned BufferSize) const = 0;
-
-public:
- format_object_base(const char *fmt) : Fmt(fmt) {}
-
- /// Format the object into the specified buffer. On success, this returns
- /// the length of the formatted string. If the buffer is too small, this
- /// returns a length to retry with, which will be larger than BufferSize.
- unsigned print(char *Buffer, unsigned BufferSize) const {
- assert(BufferSize && "Invalid buffer size!");
-
- // Print the string, leaving room for the terminating null.
- int N = snprint(Buffer, BufferSize);
-
- // VC++ and old GlibC return negative on overflow, just double the size.
- if (N < 0)
- return BufferSize * 2;
-
- // Other implementations yield number of bytes needed, not including the
- // final '\0'.
- if (unsigned(N) >= BufferSize)
- return N + 1;
-
- // Otherwise N is the length of output (not including the final '\0').
- return N;
- }
-};
-
-/// These are templated helper classes used by the format function that
-/// capture the object to be formatted and the format string. When actually
-/// printed, this synthesizes the string into a temporary buffer provided and
-/// returns whether or not it is big enough.
-
-// Helper to validate that format() parameters are scalars or pointers.
-template <typename... Args> struct validate_format_parameters;
-template <typename Arg, typename... Args>
-struct validate_format_parameters<Arg, Args...> {
- static_assert(std::is_scalar<Arg>::value,
- "format can't be used with non fundamental / non pointer type");
- validate_format_parameters() { validate_format_parameters<Args...>(); }
-};
-template <> struct validate_format_parameters<> {};
-
-template <typename... Ts>
-class format_object final : public format_object_base {
- std::tuple<Ts...> Vals;
-
- template <std::size_t... Is>
- int snprint_tuple(char *Buffer, unsigned BufferSize,
- std::index_sequence<Is...>) const {
-#ifdef _MSC_VER
- return _snprintf_s(Buffer, BufferSize, BufferSize, Fmt, std::get<Is>(Vals)...);
-#else
-#ifdef __GNUC__
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wformat-nonliteral"
-#endif
- return snprintf(Buffer, BufferSize, Fmt, std::get<Is>(Vals)...);
-#ifdef __GNUC__
-#pragma GCC diagnostic pop
-#endif
-#endif
- }
-
-public:
- format_object(const char *fmt, const Ts &... vals)
- : format_object_base(fmt), Vals(vals...) {
- validate_format_parameters<Ts...>();
- }
-
- int snprint(char *Buffer, unsigned BufferSize) const override {
- return snprint_tuple(Buffer, BufferSize, std::index_sequence_for<Ts...>());
- }
-};
-
-/// These are helper functions used to produce formatted output. They use
-/// template type deduction to construct the appropriate instance of the
-/// format_object class to simplify their construction.
-///
-/// This is typically used like:
-/// \code
-/// OS << format("%0.4f", myfloat) << '\n';
-/// \endcode
-
-template <typename... Ts>
-inline format_object<Ts...> format(const char *Fmt, const Ts &... Vals) {
- return format_object<Ts...>(Fmt, Vals...);
-}
-
-/// This is a helper class for left_justify, right_justify, and center_justify.
-class FormattedString {
-public:
- enum Justification { JustifyNone, JustifyLeft, JustifyRight, JustifyCenter };
- FormattedString(StringRef S, unsigned W, Justification J)
- : Str(S), Width(W), Justify(J) {}
-
-private:
- StringRef Str;
- unsigned Width;
- Justification Justify;
- friend class raw_ostream;
-};
-
-/// left_justify - append spaces after string so total output is
-/// \p Width characters. If \p Str is larger that \p Width, full string
-/// is written with no padding.
-inline FormattedString left_justify(StringRef Str, unsigned Width) {
- return FormattedString(Str, Width, FormattedString::JustifyLeft);
-}
-
-/// right_justify - add spaces before string so total output is
-/// \p Width characters. If \p Str is larger that \p Width, full string
-/// is written with no padding.
-inline FormattedString right_justify(StringRef Str, unsigned Width) {
- return FormattedString(Str, Width, FormattedString::JustifyRight);
-}
-
-/// center_justify - add spaces before and after string so total output is
-/// \p Width characters. If \p Str is larger that \p Width, full string
-/// is written with no padding.
-inline FormattedString center_justify(StringRef Str, unsigned Width) {
- return FormattedString(Str, Width, FormattedString::JustifyCenter);
-}
-
-/// This is a helper class used for format_hex() and format_decimal().
-class FormattedNumber {
- uint64_t HexValue;
- int64_t DecValue;
- unsigned Width;
- bool Hex;
- bool Upper;
- bool HexPrefix;
- friend class raw_ostream;
-
-public:
- FormattedNumber(uint64_t HV, int64_t DV, unsigned W, bool H, bool U,
- bool Prefix)
- : HexValue(HV), DecValue(DV), Width(W), Hex(H), Upper(U),
- HexPrefix(Prefix) {}
-};
-
-/// format_hex - Output \p N as a fixed width hexadecimal. If number will not
-/// fit in width, full number is still printed. Examples:
-/// OS << format_hex(255, 4) => 0xff
-/// OS << format_hex(255, 4, true) => 0xFF
-/// OS << format_hex(255, 6) => 0x00ff
-/// OS << format_hex(255, 2) => 0xff
-inline FormattedNumber format_hex(uint64_t N, unsigned Width,
- bool Upper = false) {
- assert(Width <= 18 && "hex width must be <= 18");
- return FormattedNumber(N, 0, Width, true, Upper, true);
-}
-
-/// format_hex_no_prefix - Output \p N as a fixed width hexadecimal. Does not
-/// prepend '0x' to the outputted string. If number will not fit in width,
-/// full number is still printed. Examples:
-/// OS << format_hex_no_prefix(255, 2) => ff
-/// OS << format_hex_no_prefix(255, 2, true) => FF
-/// OS << format_hex_no_prefix(255, 4) => 00ff
-/// OS << format_hex_no_prefix(255, 1) => ff
-inline FormattedNumber format_hex_no_prefix(uint64_t N, unsigned Width,
- bool Upper = false) {
- assert(Width <= 16 && "hex width must be <= 16");
- return FormattedNumber(N, 0, Width, true, Upper, false);
-}
-
-/// format_decimal - Output \p N as a right justified, fixed-width decimal. If
-/// number will not fit in width, full number is still printed. Examples:
-/// OS << format_decimal(0, 5) => " 0"
-/// OS << format_decimal(255, 5) => " 255"
-/// OS << format_decimal(-1, 3) => " -1"
-/// OS << format_decimal(12345, 3) => "12345"
-inline FormattedNumber format_decimal(int64_t N, unsigned Width) {
- return FormattedNumber(0, N, Width, false, false, false);
-}
-
-class FormattedBytes {
- ArrayRef<uint8_t> Bytes;
-
- // If not nullopt, display offsets for each line relative to starting value.
- std::optional<uint64_t> FirstByteOffset;
- uint32_t IndentLevel; // Number of characters to indent each line.
- uint32_t NumPerLine; // Number of bytes to show per line.
- uint8_t ByteGroupSize; // How many hex bytes are grouped without spaces
- bool Upper; // Show offset and hex bytes as upper case.
- bool ASCII; // Show the ASCII bytes for the hex bytes to the right.
- friend class raw_ostream;
-
-public:
- FormattedBytes(ArrayRef<uint8_t> B, uint32_t IL, std::optional<uint64_t> O,
- uint32_t NPL, uint8_t BGS, bool U, bool A)
- : Bytes(B), FirstByteOffset(O), IndentLevel(IL), NumPerLine(NPL),
- ByteGroupSize(BGS), Upper(U), ASCII(A) {
-
- if (ByteGroupSize > NumPerLine)
- ByteGroupSize = NumPerLine;
- }
-};
-
-inline FormattedBytes
-format_bytes(ArrayRef<uint8_t> Bytes, std::optional<uint64_t> FirstByteOffset = std::nullopt,
- uint32_t NumPerLine = 16, uint8_t ByteGroupSize = 4,
- uint32_t IndentLevel = 0, bool Upper = false) {
- return FormattedBytes(Bytes, IndentLevel, FirstByteOffset, NumPerLine,
- ByteGroupSize, Upper, false);
-}
-
-inline FormattedBytes
-format_bytes_with_ascii(ArrayRef<uint8_t> Bytes,
- std::optional<uint64_t> FirstByteOffset = std::nullopt,
- uint32_t NumPerLine = 16, uint8_t ByteGroupSize = 4,
- uint32_t IndentLevel = 0, bool Upper = false) {
- return FormattedBytes(Bytes, IndentLevel, FirstByteOffset, NumPerLine,
- ByteGroupSize, Upper, true);
-}
-
-} // end namespace wpi
-
-#endif
diff --git a/wpiutil/src/main/native/include/wpi/FunctionExtras.h b/wpiutil/src/main/native/include/wpi/FunctionExtras.h
index 8e45cda..57bc8dc 100644
--- a/wpiutil/src/main/native/include/wpi/FunctionExtras.h
+++ b/wpiutil/src/main/native/include/wpi/FunctionExtras.h
@@ -37,6 +37,7 @@
#include "wpi/PointerIntPair.h"
#include "wpi/PointerUnion.h"
#include <memory>
+#include <type_traits>
namespace wpi {
@@ -237,7 +238,11 @@
return *this;
}
- template <typename CallableT> unique_function(CallableT Callable) {
+ template <typename CallableT>
+ unique_function(CallableT Callable,
+ std::enable_if_t<
+ std::is_invocable_r_v<
+ ReturnT, CallableT, ParamTs...>>* = nullptr) {
bool IsInlineStorage = true;
void *CallableAddr = getInlineStorage();
if (sizeof(CallableT) > InlineStorageSize ||
diff --git a/wpiutil/src/main/native/include/wpi/Hashing.h b/wpiutil/src/main/native/include/wpi/Hashing.h
index d59d695..e58bfdd 100644
--- a/wpiutil/src/main/native/include/wpi/Hashing.h
+++ b/wpiutil/src/main/native/include/wpi/Hashing.h
@@ -53,6 +53,7 @@
#include <cassert>
#include <cstring>
#include <string>
+#include <string_view>
#include <utility>
#ifdef _WIN32
@@ -660,6 +661,11 @@
return hash_combine_range(arg.begin(), arg.end());
}
+template <typename T>
+hash_code hash_value(const std::basic_string_view<T> &arg) {
+ return hash_combine_range(arg.begin(), arg.end());
+}
+
} // namespace wpi
#ifdef _WIN32
diff --git a/wpiutil/src/main/native/include/wpi/HttpParser.h b/wpiutil/src/main/native/include/wpi/HttpParser.h
index b8b39de..72e5dad 100644
--- a/wpiutil/src/main/native/include/wpi/HttpParser.h
+++ b/wpiutil/src/main/native/include/wpi/HttpParser.h
@@ -1,18 +1,16 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_HTTPPARSER_H_
#define WPIUTIL_WPI_HTTPPARSER_H_
#include <stdint.h>
+#include <string_view>
+
#include "wpi/Signal.h"
#include "wpi/SmallString.h"
-#include "wpi/StringRef.h"
#include "wpi/http_parser.h"
namespace wpi {
@@ -61,9 +59,10 @@
* @param in input data
* @return Trailing input data after the parse.
*/
- StringRef Execute(StringRef in) {
- return in.drop_front(
+ std::string_view Execute(std::string_view in) {
+ in.remove_prefix(
http_parser_execute(&m_parser, &m_settings, in.data(), in.size()));
+ return in;
}
/**
@@ -139,7 +138,7 @@
/**
* Get URL. Valid in and after the url callback has been called.
*/
- StringRef GetUrl() const { return m_urlBuf; }
+ std::string_view GetUrl() const { return m_urlBuf.str(); }
/**
* Message begin callback.
@@ -151,7 +150,7 @@
*
* The parameter to the callback is the complete URL string.
*/
- sig::Signal<StringRef> url;
+ sig::Signal<std::string_view> url;
/**
* Status callback.
@@ -159,14 +158,14 @@
* The parameter to the callback is the complete status string.
* GetStatusCode() can be used to get the numeric status code.
*/
- sig::Signal<StringRef> status;
+ sig::Signal<std::string_view> status;
/**
* Header field callback.
*
* The parameters to the callback are the field name and field value.
*/
- sig::Signal<StringRef, StringRef> header;
+ sig::Signal<std::string_view, std::string_view> header;
/**
* Headers complete callback.
@@ -186,7 +185,7 @@
* multiple times arbitrarily (e.g. it's possible that it may be called with
* just a few characters at a time).
*/
- sig::Signal<StringRef, bool> body;
+ sig::Signal<std::string_view, bool> body;
/**
* Headers complete callback.
diff --git a/wpiutil/src/main/native/include/wpi/HttpServerConnection.h b/wpiutil/src/main/native/include/wpi/HttpServerConnection.h
index 17c9ca2..a4d4cf8 100644
--- a/wpiutil/src/main/native/include/wpi/HttpServerConnection.h
+++ b/wpiutil/src/main/native/include/wpi/HttpServerConnection.h
@@ -1,19 +1,15 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2020 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. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_HTTPSERVERCONNECTION_H_
#define WPIUTIL_WPI_HTTPSERVERCONNECTION_H_
#include <memory>
+#include <string_view>
-#include "wpi/ArrayRef.h"
#include "wpi/HttpParser.h"
-#include "wpi/StringRef.h"
-#include "wpi/Twine.h"
+#include "wpi/span.h"
#include "wpi/uv/Stream.h"
namespace wpi {
@@ -39,7 +35,7 @@
* Build common response headers.
*
* Called by SendHeader() to send headers common to every response.
- * Each line must be terminated with \r\n.
+ * Each line must be terminated with "\r\n".
*
* The default implementation sends the following:
* "Server: WebServer/1.0\r\n"
@@ -67,9 +63,9 @@
* be set to false.
* @param extra Extra HTTP headers to send, including final "\r\n"
*/
- virtual void BuildHeader(raw_ostream& os, int code, const Twine& codeText,
- const Twine& contentType, uint64_t contentLength,
- const Twine& extra = Twine{});
+ virtual void BuildHeader(raw_ostream& os, int code, std::string_view codeText,
+ std::string_view contentType, uint64_t contentLength,
+ std::string_view extra = {});
/**
* Send data to client.
@@ -82,7 +78,7 @@
* is desired, call m_stream.Write() directly instead.
* @param closeAfter close the connection after the write completes
*/
- void SendData(ArrayRef<uv::Buffer> bufs, bool closeAfter = false);
+ void SendData(span<const uv::Buffer> bufs, bool closeAfter = false);
/**
* Send HTTP response, along with other header information like mimetype.
@@ -94,9 +90,10 @@
* @param content Response message content
* @param extraHeader Extra HTTP headers to send, including final "\r\n"
*/
- virtual void SendResponse(int code, const Twine& codeText,
- const Twine& contentType, StringRef content,
- const Twine& extraHeader = Twine{});
+ virtual void SendResponse(int code, std::string_view codeText,
+ std::string_view contentType,
+ std::string_view content,
+ std::string_view extraHeader = {});
/**
* Send HTTP response from static data, along with other header information
@@ -112,10 +109,10 @@
* @param gzipped True if content is gzip compressed
* @param extraHeader Extra HTTP headers to send, including final "\r\n"
*/
- virtual void SendStaticResponse(int code, const Twine& codeText,
- const Twine& contentType, StringRef content,
- bool gzipped,
- const Twine& extraHeader = Twine{});
+ virtual void SendStaticResponse(int code, std::string_view codeText,
+ std::string_view contentType,
+ std::string_view content, bool gzipped,
+ std::string_view extraHeader = {});
/**
* Send error header and message.
@@ -126,7 +123,7 @@
* @param code HTTP error code (e.g. 404)
* @param message Additional message text
*/
- virtual void SendError(int code, const Twine& message = Twine{});
+ virtual void SendError(int code, std::string_view message = {});
/** The HTTP request. */
HttpParser m_request{HttpParser::kRequest};
diff --git a/wpiutil/src/main/native/include/wpi/HttpUtil.h b/wpiutil/src/main/native/include/wpi/HttpUtil.h
index 75b6e58..b10d127 100644
--- a/wpiutil/src/main/native/include/wpi/HttpUtil.h
+++ b/wpiutil/src/main/native/include/wpi/HttpUtil.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2020 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. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_HTTPUTIL_H_
#define WPIUTIL_WPI_HTTPUTIL_H_
@@ -12,19 +9,18 @@
#include <memory>
#include <optional>
#include <string>
+#include <string_view>
#include <utility>
#include <vector>
-#include "wpi/ArrayRef.h"
#include "wpi/NetworkStream.h"
#include "wpi/SmallString.h"
#include "wpi/SmallVector.h"
#include "wpi/StringMap.h"
-#include "wpi/StringRef.h"
-#include "wpi/Twine.h"
#include "wpi/raw_istream.h"
#include "wpi/raw_socket_istream.h"
#include "wpi/raw_socket_ostream.h"
+#include "wpi/span.h"
namespace wpi {
@@ -32,15 +28,15 @@
// @param buf Buffer for output
// @param error Set to true if an error occurred
// @return Escaped string
-StringRef UnescapeURI(const Twine& str, SmallVectorImpl<char>& buf,
- bool* error);
+std::string_view UnescapeURI(std::string_view str, SmallVectorImpl<char>& buf,
+ bool* error);
// Escape a string with %xx-encoding.
// @param buf Buffer for output
// @param spacePlus If true, encodes spaces to '+' rather than "%20"
// @return Escaped string
-StringRef EscapeURI(const Twine& str, SmallVectorImpl<char>& buf,
- bool spacePlus = true);
+std::string_view EscapeURI(std::string_view str, SmallVectorImpl<char>& buf,
+ bool spacePlus = true);
// Parse a set of HTTP headers. Saves just the Content-Type and Content-Length
// fields.
@@ -58,7 +54,7 @@
// @param saveBuf If not null, all scanned characters up to but not including
// the boundary are saved to this string
// @return False if error occurred on input stream, true if boundary found.
-bool FindMultipartBoundary(wpi::raw_istream& is, StringRef boundary,
+bool FindMultipartBoundary(wpi::raw_istream& is, std::string_view boundary,
std::string* saveBuf);
/**
@@ -80,7 +76,7 @@
*
* @param query query string
*/
- explicit HttpQueryMap(StringRef query);
+ explicit HttpQueryMap(std::string_view query);
/**
* Gets an element of the query string. Both the name and the returned
@@ -91,11 +87,11 @@
* @return Optional unescaped value. Returns an empty optional if the
* name is not present in the query map.
*/
- std::optional<StringRef> Get(StringRef name,
- SmallVectorImpl<char>& buf) const;
+ std::optional<std::string_view> Get(std::string_view name,
+ SmallVectorImpl<char>& buf) const;
private:
- StringMap<StringRef> m_elems;
+ StringMap<std::string_view> m_elems;
};
class HttpPathRef;
@@ -123,7 +119,7 @@
* Constructs a HTTP path from an escaped path string. Makes a copy of the
* path, so it's safe to be a temporary.
*/
- explicit HttpPath(StringRef path);
+ explicit HttpPath(std::string_view path);
/**
* Evaluates to true if the path is not empty.
@@ -146,11 +142,13 @@
* @param match match list
* @return True if path equals match list
*/
- bool equals(std::initializer_list<StringRef> match) const {
- return equals(0, makeArrayRef(match.begin(), match.end()));
+ bool equals(std::initializer_list<std::string_view> match) const {
+ return equals(0, {match.begin(), match.end()});
}
- bool equals(ArrayRef<StringRef> match) const { return equals(0, match); }
- bool equals(StringRef match) const { return equals(0, makeArrayRef(match)); }
+ bool equals(span<const std::string_view> match) const {
+ return equals(0, match);
+ }
+ bool equals(std::string_view match) const { return equals(0, {match}); }
/**
* Returns true if the elements of the path starting at the "start" element
@@ -160,15 +158,18 @@
* @param match match list
* @return True if match
*/
- bool equals(size_t start, std::initializer_list<StringRef> match) const {
- return equals(start, makeArrayRef(match.begin(), match.end()));
+ bool equals(size_t start,
+ std::initializer_list<std::string_view> match) const {
+ return equals(start, {match.begin(), match.end()});
}
- bool equals(size_t start, ArrayRef<StringRef> match) const {
- if (m_pathEnds.size() != (start + match.size())) return false;
+ bool equals(size_t start, span<const std::string_view> match) const {
+ if (m_pathEnds.size() != (start + match.size())) {
+ return false;
+ }
return startswith(start, match);
}
- bool equals(size_t start, StringRef match) const {
- return equals(start, makeArrayRef(match));
+ bool equals(size_t start, std::string_view match) const {
+ return equals(start, {match});
}
/**
@@ -178,14 +179,14 @@
* @param match match list
* @return True if path starts with match list
*/
- bool startswith(std::initializer_list<StringRef> match) const {
- return startswith(0, makeArrayRef(match.begin(), match.end()));
+ bool startswith(std::initializer_list<std::string_view> match) const {
+ return startswith(0, {match.begin(), match.end()});
}
- bool startswith(ArrayRef<StringRef> match) const {
+ bool startswith(span<const std::string_view> match) const {
return startswith(0, match);
}
- bool startswith(StringRef match) const {
- return startswith(0, makeArrayRef(match));
+ bool startswith(std::string_view match) const {
+ return startswith(0, {match});
}
/**
@@ -196,22 +197,21 @@
* @param match match list
* @return True if path starting at the start element matches the match list
*/
- bool startswith(size_t start, std::initializer_list<StringRef> match) const {
- return startswith(start, makeArrayRef(match.begin(), match.end()));
+ bool startswith(size_t start,
+ std::initializer_list<std::string_view> match) const {
+ return startswith(start, {match.begin(), match.end()});
}
- bool startswith(size_t start, ArrayRef<StringRef> match) const;
+ bool startswith(size_t start, span<const std::string_view> match) const;
- bool startswith(size_t start, StringRef match) const {
- return startswith(start, makeArrayRef(match));
+ bool startswith(size_t start, std::string_view match) const {
+ return startswith(start, {match});
}
/**
* Gets a single element of the path.
*/
- StringRef operator[](size_t n) const {
- return m_pathBuf.slice(n == 0 ? 0 : m_pathEnds[n - 1], m_pathEnds[n]);
- }
+ std::string_view operator[](size_t n) const;
/**
* Returns a path reference with the first N elements of the path removed.
@@ -229,7 +229,7 @@
class HttpPathRef {
public:
HttpPathRef() = default;
- /*implicit*/ HttpPathRef(const HttpPath& path, // NOLINT(runtime/explicit)
+ /*implicit*/ HttpPathRef(const HttpPath& path, // NOLINT
size_t start = 0)
: m_path(&path), m_start(start) {}
@@ -237,44 +237,48 @@
bool empty() const { return m_path && m_path->size() == m_start; }
size_t size() const { return m_path ? m_path->size() - m_start : 0; }
- bool equals(std::initializer_list<StringRef> match) const {
- return equals(0, makeArrayRef(match.begin(), match.end()));
+ bool equals(std::initializer_list<std::string_view> match) const {
+ return equals(0, {match.begin(), match.end()});
}
- bool equals(ArrayRef<StringRef> match) const { return equals(0, match); }
- bool equals(StringRef match) const { return equals(0, makeArrayRef(match)); }
+ bool equals(span<const std::string_view> match) const {
+ return equals(0, match);
+ }
+ bool equals(std::string_view match) const { return equals(0, {match}); }
- bool equals(size_t start, std::initializer_list<StringRef> match) const {
- return equals(start, makeArrayRef(match.begin(), match.end()));
+ bool equals(size_t start,
+ std::initializer_list<std::string_view> match) const {
+ return equals(start, {match.begin(), match.end()});
}
- bool equals(size_t start, ArrayRef<StringRef> match) const {
+ bool equals(size_t start, span<const std::string_view> match) const {
return m_path ? m_path->equals(m_start + start, match) : false;
}
- bool equals(size_t start, StringRef match) const {
- return equals(start, makeArrayRef(match));
+ bool equals(size_t start, std::string_view match) const {
+ return equals(start, {match});
}
- bool startswith(std::initializer_list<StringRef> match) const {
- return startswith(0, makeArrayRef(match.begin(), match.end()));
+ bool startswith(std::initializer_list<std::string_view> match) const {
+ return startswith(0, {match.begin(), match.end()});
}
- bool startswith(ArrayRef<StringRef> match) const {
+ bool startswith(span<const std::string_view> match) const {
return startswith(0, match);
}
- bool startswith(StringRef match) const {
- return startswith(0, makeArrayRef(match));
+ bool startswith(std::string_view match) const {
+ return startswith(0, {match});
}
- bool startswith(size_t start, std::initializer_list<StringRef> match) const {
- return startswith(start, makeArrayRef(match.begin(), match.end()));
+ bool startswith(size_t start,
+ std::initializer_list<std::string_view> match) const {
+ return startswith(start, {match.begin(), match.end()});
}
- bool startswith(size_t start, ArrayRef<StringRef> match) const {
+ bool startswith(size_t start, span<const std::string_view> match) const {
return m_path ? m_path->startswith(m_start + start, match) : false;
}
- bool startswith(size_t start, StringRef match) const {
- return startswith(start, makeArrayRef(match));
+ bool startswith(size_t start, std::string_view match) const {
+ return startswith(start, {match});
}
- StringRef operator[](size_t n) const {
- return m_path ? m_path->operator[](m_start + n) : StringRef{};
+ std::string_view operator[](size_t n) const {
+ return m_path ? m_path->operator[](m_start + n) : std::string_view{};
}
HttpPathRef drop_front(size_t n) const {
return m_path ? m_path->drop_front(m_start + n) : HttpPathRef{};
@@ -288,7 +292,7 @@
class HttpLocation {
public:
HttpLocation() = default;
- HttpLocation(const Twine& url_, bool* error, std::string* errorMsg);
+ HttpLocation(std::string_view url_, bool* error, std::string* errorMsg);
std::string url; // retain copy
std::string user; // unescaped
@@ -313,13 +317,13 @@
template <typename T>
HttpRequest(const HttpLocation& loc, const T& extraParams);
- HttpRequest(const HttpLocation& loc, StringRef path_)
+ HttpRequest(const HttpLocation& loc, std::string_view path_)
: host{loc.host}, port{loc.port}, path{path_} {
SetAuth(loc);
}
template <typename T>
- HttpRequest(const HttpLocation& loc, StringRef path_, const T& params)
+ HttpRequest(const HttpLocation& loc, std::string_view path_, const T& params)
: host{loc.host}, port{loc.port} {
SetPath(path_, params);
SetAuth(loc);
@@ -333,18 +337,18 @@
private:
void SetAuth(const HttpLocation& loc);
template <typename T>
- void SetPath(StringRef path_, const T& params);
+ void SetPath(std::string_view path_, const T& params);
template <typename T>
- static StringRef GetFirst(const T& elem) {
+ static std::string_view GetFirst(const T& elem) {
return elem.first;
}
template <typename T>
- static StringRef GetFirst(const StringMapEntry<T>& elem) {
+ static std::string_view GetFirst(const StringMapEntry<T>& elem) {
return elem.getKey();
}
template <typename T>
- static StringRef GetSecond(const T& elem) {
+ static std::string_view GetSecond(const T& elem) {
return elem.second;
}
};
@@ -369,14 +373,15 @@
class HttpMultipartScanner {
public:
- explicit HttpMultipartScanner(StringRef boundary, bool saveSkipped = false) {
+ explicit HttpMultipartScanner(std::string_view boundary,
+ bool saveSkipped = false) {
Reset(saveSkipped);
SetBoundary(boundary);
}
// Change the boundary. This is only safe to do when IsDone() is true (or
// immediately after construction).
- void SetBoundary(StringRef boundary);
+ void SetBoundary(std::string_view boundary);
// Reset the scanner. This allows reuse of internal buffers.
void Reset(bool saveSkipped = false);
@@ -385,14 +390,14 @@
// is true.
// @param in input data
// @return the input not consumed; empty if all input consumed
- StringRef Execute(StringRef in);
+ std::string_view Execute(std::string_view in);
// Returns true when the boundary has been found.
bool IsDone() const { return m_state == kDone; }
// Get the skipped data. Will be empty if saveSkipped was false.
- StringRef GetSkipped() const {
- return m_saveSkipped ? StringRef{m_buf} : StringRef{};
+ std::string_view GetSkipped() const {
+ return m_saveSkipped ? std::string_view{m_buf} : std::string_view{};
}
private:
@@ -412,6 +417,6 @@
} // namespace wpi
-#include "HttpUtil.inl"
+#include "HttpUtil.inc"
#endif // WPIUTIL_WPI_HTTPUTIL_H_
diff --git a/wpiutil/src/main/native/include/wpi/HttpUtil.inc b/wpiutil/src/main/native/include/wpi/HttpUtil.inc
new file mode 100644
index 0000000..726cdfb
--- /dev/null
+++ b/wpiutil/src/main/native/include/wpi/HttpUtil.inc
@@ -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.
+
+#ifndef WPIUTIL_WPI_HTTPUTIL_INC_
+#define WPIUTIL_WPI_HTTPUTIL_INC_
+
+#include <utility>
+
+#include "wpi/HttpUtil.h"
+
+namespace wpi {
+
+inline HttpPathRef HttpPath::drop_front(size_t n) const {
+ return HttpPathRef(*this, n);
+}
+
+template <typename T>
+HttpRequest::HttpRequest(const HttpLocation& loc, const T& extraParams)
+ : host{loc.host}, port{loc.port} {
+ StringMap<std::string_view> params;
+ for (const auto& p : loc.params) {
+ params.insert(std::make_pair(GetFirst(p), GetSecond(p)));
+ }
+ for (const auto& p : extraParams) {
+ params.insert(std::make_pair(GetFirst(p), GetSecond(p)));
+ }
+ SetPath(loc.path, params);
+ SetAuth(loc);
+}
+
+template <typename T>
+void HttpRequest::SetPath(std::string_view path_, const T& params) {
+ // Build location including query string
+ raw_svector_ostream pathOs{path};
+ pathOs << path_;
+ bool first = true;
+ for (const auto& param : params) {
+ if (first) {
+ pathOs << '?';
+ first = false;
+ } else {
+ pathOs << '&';
+ }
+ SmallString<64> escapeBuf;
+ pathOs << EscapeURI(GetFirst(param), escapeBuf, false);
+ if (!GetSecond(param).empty()) {
+ pathOs << '=' << EscapeURI(GetSecond(param), escapeBuf, false);
+ }
+ }
+}
+
+} // namespace wpi
+
+#endif // WPIUTIL_WPI_HTTPUTIL_INC_
diff --git a/wpiutil/src/main/native/include/wpi/HttpUtil.inl b/wpiutil/src/main/native/include/wpi/HttpUtil.inl
deleted file mode 100644
index f66c746..0000000
--- a/wpiutil/src/main/native/include/wpi/HttpUtil.inl
+++ /dev/null
@@ -1,54 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2015-2020 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. */
-/*----------------------------------------------------------------------------*/
-
-#ifndef WPIUTIL_WPI_HTTPUTIL_INL_
-#define WPIUTIL_WPI_HTTPUTIL_INL_
-
-#include <utility>
-
-namespace wpi {
-
-inline HttpPathRef HttpPath::drop_front(size_t n) const {
- return HttpPathRef(*this, n);
-}
-
-template <typename T>
-HttpRequest::HttpRequest(const HttpLocation& loc, const T& extraParams)
- : host{loc.host}, port{loc.port} {
- StringMap<StringRef> params;
- for (const auto& p : loc.params)
- params.insert(std::make_pair(GetFirst(p), GetSecond(p)));
- for (const auto& p : extraParams)
- params.insert(std::make_pair(GetFirst(p), GetSecond(p)));
- SetPath(loc.path, params);
- SetAuth(loc);
-}
-
-template <typename T>
-void HttpRequest::SetPath(StringRef path_, const T& params) {
- // Build location including query string
- raw_svector_ostream pathOs{path};
- pathOs << path_;
- bool first = true;
- for (const auto& param : params) {
- if (first) {
- pathOs << '?';
- first = false;
- } else {
- pathOs << '&';
- }
- SmallString<64> escapeBuf;
- pathOs << EscapeURI(GetFirst(param), escapeBuf, false);
- if (!GetSecond(param).empty()) {
- pathOs << '=' << EscapeURI(GetSecond(param), escapeBuf, false);
- }
- }
-}
-
-} // namespace wpi
-
-#endif // WPIUTIL_WPI_HTTPUTIL_INL_
diff --git a/wpiutil/src/main/native/include/wpi/HttpWebSocketServerConnection.h b/wpiutil/src/main/native/include/wpi/HttpWebSocketServerConnection.h
index d662471..d89b61f 100644
--- a/wpiutil/src/main/native/include/wpi/HttpWebSocketServerConnection.h
+++ b/wpiutil/src/main/native/include/wpi/HttpWebSocketServerConnection.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2020 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. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_HTTPWEBSOCKETSERVERCONNECTION_H_
#define WPIUTIL_WPI_HTTPWEBSOCKETSERVERCONNECTION_H_
@@ -11,13 +8,13 @@
#include <initializer_list>
#include <memory>
#include <string>
+#include <string_view>
-#include "wpi/ArrayRef.h"
#include "wpi/HttpServerConnection.h"
#include "wpi/SmallVector.h"
-#include "wpi/StringRef.h"
#include "wpi/WebSocket.h"
#include "wpi/WebSocketServer.h"
+#include "wpi/span.h"
#include "wpi/uv/Stream.h"
namespace wpi {
@@ -39,7 +36,7 @@
* @param protocols Acceptable subprotocols
*/
HttpWebSocketServerConnection(std::shared_ptr<uv::Stream> stream,
- ArrayRef<StringRef> protocols);
+ span<const std::string_view> protocols);
/**
* Constructor.
@@ -47,10 +44,11 @@
* @param stream network stream
* @param protocols Acceptable subprotocols
*/
- HttpWebSocketServerConnection(std::shared_ptr<uv::Stream> stream,
- std::initializer_list<StringRef> protocols)
- : HttpWebSocketServerConnection(
- stream, makeArrayRef(protocols.begin(), protocols.end())) {}
+ HttpWebSocketServerConnection(
+ std::shared_ptr<uv::Stream> stream,
+ std::initializer_list<std::string_view> protocols)
+ : HttpWebSocketServerConnection(stream,
+ {protocols.begin(), protocols.end()}) {}
protected:
/**
@@ -62,7 +60,7 @@
*
* @param protocol negotiated subprotocol
*/
- virtual bool IsValidWsUpgrade(StringRef protocol) { return true; }
+ virtual bool IsValidWsUpgrade(std::string_view protocol) { return true; }
/**
* Process an incoming WebSocket upgrade. This is called after the header
@@ -89,6 +87,6 @@
} // namespace wpi
-#include "HttpWebSocketServerConnection.inl"
+#include "HttpWebSocketServerConnection.inc"
#endif // WPIUTIL_WPI_HTTPWEBSOCKETSERVERCONNECTION_H_
diff --git a/wpiutil/src/main/native/include/wpi/HttpWebSocketServerConnection.inc b/wpiutil/src/main/native/include/wpi/HttpWebSocketServerConnection.inc
new file mode 100644
index 0000000..68b9f2d
--- /dev/null
+++ b/wpiutil/src/main/native/include/wpi/HttpWebSocketServerConnection.inc
@@ -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.
+
+#ifndef WPIUTIL_WPI_HTTPWEBSOCKETSERVERCONNECTION_INC_
+#define WPIUTIL_WPI_HTTPWEBSOCKETSERVERCONNECTION_INC_
+
+#include <memory>
+
+#include "wpi/HttpWebSocketServerConnection.h"
+
+namespace wpi {
+
+template <typename Derived>
+HttpWebSocketServerConnection<Derived>::HttpWebSocketServerConnection(
+ std::shared_ptr<uv::Stream> stream, span<const std::string_view> protocols)
+ : HttpServerConnection{stream},
+ m_helper{m_request},
+ m_protocols{protocols.begin(), protocols.end()} {
+ // Handle upgrade event
+ m_helper.upgrade.connect([this] {
+ // Negotiate sub-protocol
+ SmallVector<std::string_view, 2> protocols{m_protocols.begin(),
+ m_protocols.end()};
+ std::string_view protocol = m_helper.MatchProtocol(protocols).second;
+
+ // Check that the upgrade is valid
+ if (!IsValidWsUpgrade(protocol)) {
+ return;
+ }
+
+ // Disconnect HttpServerConnection header reader
+ m_dataConn.disconnect();
+ m_messageCompleteConn.disconnect();
+
+ // Accepting the stream may destroy this (as it replaces the stream user
+ // data), so grab a shared pointer first.
+ auto self = this->shared_from_this();
+
+ // Accept the upgrade
+ auto ws = m_helper.Accept(m_stream, protocol);
+
+ // Set this as the websocket user data to keep it around
+ ws->SetData(self);
+
+ // Store in member
+ m_websocket = ws.get();
+
+ // Call derived class function
+ ProcessWsUpgrade();
+ });
+}
+
+} // namespace wpi
+
+#endif // WPIUTIL_WPI_HTTPWEBSOCKETSERVERCONNECTION_INC_
diff --git a/wpiutil/src/main/native/include/wpi/HttpWebSocketServerConnection.inl b/wpiutil/src/main/native/include/wpi/HttpWebSocketServerConnection.inl
deleted file mode 100644
index 586f189..0000000
--- a/wpiutil/src/main/native/include/wpi/HttpWebSocketServerConnection.inl
+++ /dev/null
@@ -1,54 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2020 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. */
-/*----------------------------------------------------------------------------*/
-
-#ifndef WPIUTIL_WPI_HTTPWEBSOCKETSERVERCONNECTION_INL_
-#define WPIUTIL_WPI_HTTPWEBSOCKETSERVERCONNECTION_INL_
-
-#include <memory>
-
-namespace wpi {
-
-template <typename Derived>
-HttpWebSocketServerConnection<Derived>::HttpWebSocketServerConnection(
- std::shared_ptr<uv::Stream> stream, ArrayRef<StringRef> protocols)
- : HttpServerConnection{stream},
- m_helper{m_request},
- m_protocols{protocols.begin(), protocols.end()} {
- // Handle upgrade event
- m_helper.upgrade.connect([this] {
- // Negotiate sub-protocol
- SmallVector<StringRef, 2> protocols{m_protocols.begin(), m_protocols.end()};
- StringRef protocol = m_helper.MatchProtocol(protocols).second;
-
- // Check that the upgrade is valid
- if (!IsValidWsUpgrade(protocol)) return;
-
- // Disconnect HttpServerConnection header reader
- m_dataConn.disconnect();
- m_messageCompleteConn.disconnect();
-
- // Accepting the stream may destroy this (as it replaces the stream user
- // data), so grab a shared pointer first.
- auto self = this->shared_from_this();
-
- // Accept the upgrade
- auto ws = m_helper.Accept(m_stream, protocol);
-
- // Set this as the websocket user data to keep it around
- ws->SetData(self);
-
- // Store in member
- m_websocket = ws.get();
-
- // Call derived class function
- ProcessWsUpgrade();
- });
-}
-
-} // namespace wpi
-
-#endif // WPIUTIL_WPI_HTTPWEBSOCKETSERVERCONNECTION_INL_
diff --git a/wpiutil/src/main/native/include/wpi/IntrusiveRefCntPtr.h b/wpiutil/src/main/native/include/wpi/IntrusiveRefCntPtr.h
deleted file mode 100644
index c677c10..0000000
--- a/wpiutil/src/main/native/include/wpi/IntrusiveRefCntPtr.h
+++ /dev/null
@@ -1,270 +0,0 @@
-//==- llvm/ADT/IntrusiveRefCntPtr.h - Smart Refcounting Pointer --*- C++ -*-==//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file defines the RefCountedBase, ThreadSafeRefCountedBase, and
-// IntrusiveRefCntPtr classes.
-//
-// IntrusiveRefCntPtr is a smart pointer to an object which maintains a
-// reference count. (ThreadSafe)RefCountedBase is a mixin class that adds a
-// refcount member variable and methods for updating the refcount. An object
-// that inherits from (ThreadSafe)RefCountedBase deletes itself when its
-// refcount hits zero.
-//
-// For example:
-//
-// class MyClass : public RefCountedBase<MyClass> {};
-//
-// void foo() {
-// // Constructing an IntrusiveRefCntPtr increases the pointee's refcount by
-// // 1 (from 0 in this case).
-// IntrusiveRefCntPtr<MyClass> Ptr1(new MyClass());
-//
-// // Copying an IntrusiveRefCntPtr increases the pointee's refcount by 1.
-// IntrusiveRefCntPtr<MyClass> Ptr2(Ptr1);
-//
-// // Constructing an IntrusiveRefCntPtr has no effect on the object's
-// // refcount. After a move, the moved-from pointer is null.
-// IntrusiveRefCntPtr<MyClass> Ptr3(std::move(Ptr1));
-// assert(Ptr1 == nullptr);
-//
-// // Clearing an IntrusiveRefCntPtr decreases the pointee's refcount by 1.
-// Ptr2.reset();
-//
-// // The object deletes itself when we return from the function, because
-// // Ptr3's destructor decrements its refcount to 0.
-// }
-//
-// You can use IntrusiveRefCntPtr with isa<T>(), dyn_cast<T>(), etc.:
-//
-// IntrusiveRefCntPtr<MyClass> Ptr(new MyClass());
-// OtherClass *Other = dyn_cast<OtherClass>(Ptr); // Ptr.get() not required
-//
-// IntrusiveRefCntPtr works with any class that
-//
-// - inherits from (ThreadSafe)RefCountedBase,
-// - has Retain() and Release() methods, or
-// - specializes IntrusiveRefCntPtrInfo.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef WPIUTIL_WPI_INTRUSIVEREFCNTPTR_H
-#define WPIUTIL_WPI_INTRUSIVEREFCNTPTR_H
-
-#include <atomic>
-#include <cassert>
-#include <cstddef>
-
-namespace wpi {
-
-/// A CRTP mixin class that adds reference counting to a type.
-///
-/// The lifetime of an object which inherits from RefCountedBase is managed by
-/// calls to Release() and Retain(), which increment and decrement the object's
-/// refcount, respectively. When a Release() call decrements the refcount to 0,
-/// the object deletes itself.
-template <class Derived> class RefCountedBase {
- mutable unsigned RefCount = 0;
-
-public:
- RefCountedBase() = default;
- RefCountedBase(const RefCountedBase &) {}
-
- void Retain() const { ++RefCount; }
-
- void Release() const {
- assert(RefCount > 0 && "Reference count is already zero.");
- if (--RefCount == 0)
- delete static_cast<const Derived *>(this);
- }
-};
-
-/// A thread-safe version of \c RefCountedBase.
-template <class Derived> class ThreadSafeRefCountedBase {
- mutable std::atomic<int> RefCount;
-
-protected:
- ThreadSafeRefCountedBase() : RefCount(0) {}
-
-public:
- void Retain() const { RefCount.fetch_add(1, std::memory_order_relaxed); }
-
- void Release() const {
- int NewRefCount = RefCount.fetch_sub(1, std::memory_order_acq_rel) - 1;
- assert(NewRefCount >= 0 && "Reference count was already zero.");
- if (NewRefCount == 0)
- delete static_cast<const Derived *>(this);
- }
-};
-
-/// Class you can specialize to provide custom retain/release functionality for
-/// a type.
-///
-/// Usually specializing this class is not necessary, as IntrusiveRefCntPtr
-/// works with any type which defines Retain() and Release() functions -- you
-/// can define those functions yourself if RefCountedBase doesn't work for you.
-///
-/// One case when you might want to specialize this type is if you have
-/// - Foo.h defines type Foo and includes Bar.h, and
-/// - Bar.h uses IntrusiveRefCntPtr<Foo> in inline functions.
-///
-/// Because Foo.h includes Bar.h, Bar.h can't include Foo.h in order to pull in
-/// the declaration of Foo. Without the declaration of Foo, normally Bar.h
-/// wouldn't be able to use IntrusiveRefCntPtr<Foo>, which wants to call
-/// T::Retain and T::Release.
-///
-/// To resolve this, Bar.h could include a third header, FooFwd.h, which
-/// forward-declares Foo and specializes IntrusiveRefCntPtrInfo<Foo>. Then
-/// Bar.h could use IntrusiveRefCntPtr<Foo>, although it still couldn't call any
-/// functions on Foo itself, because Foo would be an incomplete type.
-template <typename T> struct IntrusiveRefCntPtrInfo {
- static void retain(T *obj) { obj->Retain(); }
- static void release(T *obj) { obj->Release(); }
-};
-
-/// A smart pointer to a reference-counted object that inherits from
-/// RefCountedBase or ThreadSafeRefCountedBase.
-///
-/// This class increments its pointee's reference count when it is created, and
-/// decrements its refcount when it's destroyed (or is changed to point to a
-/// different object).
-template <typename T> class IntrusiveRefCntPtr {
- T *Obj = nullptr;
-
-public:
- using element_type = T;
-
- explicit IntrusiveRefCntPtr() = default;
- IntrusiveRefCntPtr(T *obj) : Obj(obj) { retain(); }
- IntrusiveRefCntPtr(const IntrusiveRefCntPtr &S) : Obj(S.Obj) { retain(); }
- IntrusiveRefCntPtr(IntrusiveRefCntPtr &&S) : Obj(S.Obj) { S.Obj = nullptr; }
-
- template <class X>
- IntrusiveRefCntPtr(IntrusiveRefCntPtr<X> &&S) : Obj(S.get()) {
- S.Obj = nullptr;
- }
-
- template <class X>
- IntrusiveRefCntPtr(const IntrusiveRefCntPtr<X> &S) : Obj(S.get()) {
- retain();
- }
-
- ~IntrusiveRefCntPtr() { release(); }
-
- IntrusiveRefCntPtr &operator=(IntrusiveRefCntPtr S) {
- swap(S);
- return *this;
- }
-
- T &operator*() const { return *Obj; }
- T *operator->() const { return Obj; }
- T *get() const { return Obj; }
- explicit operator bool() const { return Obj; }
-
- void swap(IntrusiveRefCntPtr &other) {
- T *tmp = other.Obj;
- other.Obj = Obj;
- Obj = tmp;
- }
-
- void reset() {
- release();
- Obj = nullptr;
- }
-
- void resetWithoutRelease() { Obj = nullptr; }
-
-private:
- void retain() {
- if (Obj)
- IntrusiveRefCntPtrInfo<T>::retain(Obj);
- }
-
- void release() {
- if (Obj)
- IntrusiveRefCntPtrInfo<T>::release(Obj);
- }
-
- template <typename X> friend class IntrusiveRefCntPtr;
-};
-
-template <class T, class U>
-inline bool operator==(const IntrusiveRefCntPtr<T> &A,
- const IntrusiveRefCntPtr<U> &B) {
- return A.get() == B.get();
-}
-
-template <class T, class U>
-inline bool operator!=(const IntrusiveRefCntPtr<T> &A,
- const IntrusiveRefCntPtr<U> &B) {
- return A.get() != B.get();
-}
-
-template <class T, class U>
-inline bool operator==(const IntrusiveRefCntPtr<T> &A, U *B) {
- return A.get() == B;
-}
-
-template <class T, class U>
-inline bool operator!=(const IntrusiveRefCntPtr<T> &A, U *B) {
- return A.get() != B;
-}
-
-template <class T, class U>
-inline bool operator==(T *A, const IntrusiveRefCntPtr<U> &B) {
- return A == B.get();
-}
-
-template <class T, class U>
-inline bool operator!=(T *A, const IntrusiveRefCntPtr<U> &B) {
- return A != B.get();
-}
-
-template <class T>
-bool operator==(std::nullptr_t A, const IntrusiveRefCntPtr<T> &B) {
- return !B;
-}
-
-template <class T>
-bool operator==(const IntrusiveRefCntPtr<T> &A, std::nullptr_t B) {
- return B == A;
-}
-
-template <class T>
-bool operator!=(std::nullptr_t A, const IntrusiveRefCntPtr<T> &B) {
- return !(A == B);
-}
-
-template <class T>
-bool operator!=(const IntrusiveRefCntPtr<T> &A, std::nullptr_t B) {
- return !(A == B);
-}
-
-// Make IntrusiveRefCntPtr work with dyn_cast, isa, and the other idioms from
-// Casting.h.
-template <typename From> struct simplify_type;
-
-template <class T> struct simplify_type<IntrusiveRefCntPtr<T>> {
- using SimpleType = T *;
-
- static SimpleType getSimplifiedValue(IntrusiveRefCntPtr<T> &Val) {
- return Val.get();
- }
-};
-
-template <class T> struct simplify_type<const IntrusiveRefCntPtr<T>> {
- using SimpleType = /*const*/ T *;
-
- static SimpleType getSimplifiedValue(const IntrusiveRefCntPtr<T> &Val) {
- return Val.get();
- }
-};
-
-} // end namespace wpi
-
-#endif // LLVM_ADT_INTRUSIVEREFCNTPTR_H
diff --git a/wpiutil/src/main/native/include/wpi/Logger.h b/wpiutil/src/main/native/include/wpi/Logger.h
index 20aa8ee..807e472 100644
--- a/wpiutil/src/main/native/include/wpi/Logger.h
+++ b/wpiutil/src/main/native/include/wpi/Logger.h
@@ -1,17 +1,14 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2015-2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_LOGGER_H_
#define WPIUTIL_WPI_LOGGER_H_
#include <functional>
+#include <utility>
-#include "wpi/SmallString.h"
-#include "wpi/raw_ostream.h"
+#include "fmt/format.h"
namespace wpi {
@@ -33,19 +30,28 @@
unsigned int line, const char* msg)>;
Logger() = default;
- explicit Logger(const LogFunc& func) : m_func(func) {}
- Logger(const LogFunc& func, unsigned int min_level)
- : m_func(func), m_min_level(min_level) {}
+ explicit Logger(LogFunc func) : m_func(std::move(func)) {}
+ Logger(LogFunc func, unsigned int min_level)
+ : m_func(std::move(func)), m_min_level(min_level) {}
void SetLogger(LogFunc func) { m_func = func; }
void set_min_level(unsigned int level) { m_min_level = level; }
unsigned int min_level() const { return m_min_level; }
+ void DoLog(unsigned int level, const char* file, unsigned int line,
+ const char* msg);
+
+ void LogV(unsigned int level, const char* file, unsigned int line,
+ fmt::string_view format, fmt::format_args args);
+
+ template <typename S, typename... Args>
void Log(unsigned int level, const char* file, unsigned int line,
- const char* msg) {
- if (!m_func || level < m_min_level) return;
- m_func(level, file, line, msg);
+ const S& format, Args&&... args) {
+ if (m_func && level >= m_min_level) {
+ LogV(level, file, line, format,
+ fmt::make_args_checked<Args...>(format, args...));
+ }
}
bool HasLogger() const { return m_func != nullptr; }
@@ -55,44 +61,43 @@
unsigned int m_min_level = 20;
};
-#define WPI_LOG(logger_inst, level, x) \
- do { \
- ::wpi::Logger& WPI_logger_ = logger_inst; \
- if (WPI_logger_.min_level() <= static_cast<unsigned int>(level) && \
- WPI_logger_.HasLogger()) { \
- ::wpi::SmallString<128> log_buf_; \
- ::wpi::raw_svector_ostream log_os_{log_buf_}; \
- log_os_ << x; \
- WPI_logger_.Log(level, __FILE__, __LINE__, log_buf_.c_str()); \
- } \
- } while (0)
+#define WPI_LOG(logger_inst, level, format, ...) \
+ logger_inst.Log(level, __FILE__, __LINE__, FMT_STRING(format), __VA_ARGS__)
-#define WPI_ERROR(inst, x) WPI_LOG(inst, ::wpi::WPI_LOG_ERROR, x)
-#define WPI_WARNING(inst, x) WPI_LOG(inst, ::wpi::WPI_LOG_WARNING, x)
-#define WPI_INFO(inst, x) WPI_LOG(inst, ::wpi::WPI_LOG_INFO, x)
+#define WPI_ERROR(inst, format, ...) \
+ WPI_LOG(inst, ::wpi::WPI_LOG_ERROR, format, __VA_ARGS__)
+#define WPI_WARNING(inst, format, ...) \
+ WPI_LOG(inst, ::wpi::WPI_LOG_WARNING, format, __VA_ARGS__)
+#define WPI_INFO(inst, format, ...) \
+ WPI_LOG(inst, ::wpi::WPI_LOG_INFO, format, __VA_ARGS__)
#ifdef NDEBUG
-#define WPI_DEBUG(inst, x) \
- do { \
+#define WPI_DEBUG(inst, format, ...) \
+ do { \
} while (0)
-#define WPI_DEBUG1(inst, x) \
- do { \
+#define WPI_DEBUG1(inst, format, ...) \
+ do { \
} while (0)
-#define WPI_DEBUG2(inst, x) \
- do { \
+#define WPI_DEBUG2(inst, format, ...) \
+ do { \
} while (0)
-#define WPI_DEBUG3(inst, x) \
- do { \
+#define WPI_DEBUG3(inst, format, ...) \
+ do { \
} while (0)
-#define WPI_DEBUG4(inst, x) \
- do { \
+#define WPI_DEBUG4(inst, format, ...) \
+ do { \
} while (0)
#else
-#define WPI_DEBUG(inst, x) WPI_LOG(inst, ::wpi::WPI_LOG_DEBUG, x)
-#define WPI_DEBUG1(inst, x) WPI_LOG(inst, ::wpi::WPI_LOG_DEBUG1, x)
-#define WPI_DEBUG2(inst, x) WPI_LOG(inst, ::wpi::WPI_LOG_DEBUG2, x)
-#define WPI_DEBUG3(inst, x) WPI_LOG(inst, ::wpi::WPI_LOG_DEBUG3, x)
-#define WPI_DEBUG4(inst, x) WPI_LOG(inst, ::wpi::WPI_LOG_DEBUG4, x)
+#define WPI_DEBUG(inst, format, ...) \
+ WPI_LOG(inst, ::wpi::WPI_LOG_DEBUG, format, __VA_ARGS__)
+#define WPI_DEBUG1(inst, format, ...) \
+ WPI_LOG(inst, ::wpi::WPI_LOG_DEBUG1, format, __VA_ARGS__)
+#define WPI_DEBUG2(inst, format, ...) \
+ WPI_LOG(inst, ::wpi::WPI_LOG_DEBUG2, format, __VA_ARGS__)
+#define WPI_DEBUG3(inst, format, ...) \
+ WPI_LOG(inst, ::wpi::WPI_LOG_DEBUG3, format, __VA_ARGS__)
+#define WPI_DEBUG4(inst, format, ...) \
+ WPI_LOG(inst, ::wpi::WPI_LOG_DEBUG4, format, __VA_ARGS__)
#endif
} // namespace wpi
diff --git a/wpiutil/src/main/native/include/wpi/MimeTypes.h b/wpiutil/src/main/native/include/wpi/MimeTypes.h
index b3dc17b..ce36ea2 100644
--- a/wpiutil/src/main/native/include/wpi/MimeTypes.h
+++ b/wpiutil/src/main/native/include/wpi/MimeTypes.h
@@ -1,18 +1,15 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2020 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. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_MIMETYPES_H_
#define WPIUTIL_WPI_MIMETYPES_H_
-#include "wpi/StringRef.h"
+#include <string_view>
namespace wpi {
-StringRef MimeTypeFromPath(StringRef path);
+std::string_view MimeTypeFromPath(std::string_view path);
} // namespace wpi
diff --git a/wpiutil/src/main/native/include/wpi/NativeFormatting.h b/wpiutil/src/main/native/include/wpi/NativeFormatting.h
deleted file mode 100644
index 976adc8..0000000
--- a/wpiutil/src/main/native/include/wpi/NativeFormatting.h
+++ /dev/null
@@ -1,49 +0,0 @@
-//===- NativeFormatting.h - Low level formatting helpers ---------*- C++-*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef WPIUTIL_WPI_NATIVE_FORMATTING_H
-#define WPIUTIL_WPI_NATIVE_FORMATTING_H
-
-#include "wpi/raw_ostream.h"
-
-#include <cstdint>
-#include <optional>
-
-namespace wpi {
-enum class FloatStyle { Exponent, ExponentUpper, Fixed, Percent };
-enum class IntegerStyle {
- Integer,
- Number,
-};
-enum class HexPrintStyle { Upper, Lower, PrefixUpper, PrefixLower };
-
-size_t getDefaultPrecision(FloatStyle Style);
-
-bool isPrefixedHexStyle(HexPrintStyle S);
-
-void write_integer(raw_ostream &S, unsigned int N, size_t MinDigits,
- IntegerStyle Style);
-void write_integer(raw_ostream &S, int N, size_t MinDigits, IntegerStyle Style);
-void write_integer(raw_ostream &S, unsigned long N, size_t MinDigits,
- IntegerStyle Style);
-void write_integer(raw_ostream &S, long N, size_t MinDigits,
- IntegerStyle Style);
-void write_integer(raw_ostream &S, unsigned long long N, size_t MinDigits,
- IntegerStyle Style);
-void write_integer(raw_ostream &S, long long N, size_t MinDigits,
- IntegerStyle Style);
-
-void write_hex(raw_ostream &S, uint64_t N, HexPrintStyle Style,
- std::optional<size_t> Width = std::nullopt);
-void write_double(raw_ostream &S, double D, FloatStyle Style,
- std::optional<size_t> Precision = std::nullopt);
-}
-
-#endif
-
diff --git a/wpiutil/src/main/native/include/wpi/NetworkAcceptor.h b/wpiutil/src/main/native/include/wpi/NetworkAcceptor.h
index dd09e2d..9982360 100644
--- a/wpiutil/src/main/native/include/wpi/NetworkAcceptor.h
+++ b/wpiutil/src/main/native/include/wpi/NetworkAcceptor.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2015-2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_NETWORKACCEPTOR_H_
#define WPIUTIL_WPI_NETWORKACCEPTOR_H_
diff --git a/wpiutil/src/main/native/include/wpi/NetworkStream.h b/wpiutil/src/main/native/include/wpi/NetworkStream.h
index 1177167..b842ecd 100644
--- a/wpiutil/src/main/native/include/wpi/NetworkStream.h
+++ b/wpiutil/src/main/native/include/wpi/NetworkStream.h
@@ -1,16 +1,12 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2015-2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_NETWORKSTREAM_H_
#define WPIUTIL_WPI_NETWORKSTREAM_H_
#include <cstddef>
-
-#include "wpi/StringRef.h"
+#include <string_view>
namespace wpi {
@@ -31,7 +27,7 @@
int timeout = 0) = 0;
virtual void close() = 0;
- virtual StringRef getPeerIP() const = 0;
+ virtual std::string_view getPeerIP() const = 0;
virtual int getPeerPort() const = 0;
virtual void setNoDelay() = 0;
diff --git a/wpiutil/src/main/native/include/wpi/NullDeleter.h b/wpiutil/src/main/native/include/wpi/NullDeleter.h
new file mode 100644
index 0000000..26146e5
--- /dev/null
+++ b/wpiutil/src/main/native/include/wpi/NullDeleter.h
@@ -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.
+
+#pragma once
+
+namespace wpi {
+
+// A struct to use as a deleter when a std::shared_ptr must wrap a raw pointer
+// that is being deleted by someone else.
+template <class T>
+struct NullDeleter {
+ void operator()(T*) const noexcept {};
+};
+
+} // namespace wpi
diff --git a/wpiutil/src/main/native/include/wpi/ParallelTcpConnector.h b/wpiutil/src/main/native/include/wpi/ParallelTcpConnector.h
new file mode 100644
index 0000000..47c29d0
--- /dev/null
+++ b/wpiutil/src/main/native/include/wpi/ParallelTcpConnector.h
@@ -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.
+
+#pragma once
+
+#include <stdint.h>
+
+#include <functional>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "wpi/span.h"
+#include "wpi/uv/Timer.h"
+
+namespace wpi {
+
+class Logger;
+
+namespace uv {
+class GetAddrInfoReq;
+class Loop;
+class Tcp;
+class Timer;
+} // namespace uv
+
+/**
+ * Parallel TCP connector. Attempts parallel resolution and connection to
+ * multiple servers with automatic retry if none connect.
+ *
+ * Each successful TCP connection results in a call to the connected callback.
+ * For correct operation, the consuming code (either the connected callback or
+ * e.g. task it starts) must call Succeeded() to indicate if the connection has
+ * succeeded prior to the reconnect rate timeout. A successful connection
+ * results in the connector terminating all other connection attempts.
+ *
+ * After the reconnect rate times out, all remaining active connection attempts
+ * are canceled and new ones started.
+ */
+class ParallelTcpConnector
+ : public std::enable_shared_from_this<ParallelTcpConnector> {
+ struct private_init {};
+
+ public:
+ /**
+ * Create.
+ *
+ * @param loop loop
+ * @param reconnectRate how long to wait after starting connection attempts
+ * to cancel and attempt connecting again
+ * @param logger logger
+ * @param connected callback function when a connection succeeds; may be
+ * called multiple times if it does not call Succeeded()
+ * before returning
+ * @return Parallel connector
+ */
+ 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) {
+ return std::make_shared<ParallelTcpConnector>(
+ loop, reconnectRate, logger, std::move(connected), private_init{});
+ }
+
+ ParallelTcpConnector(wpi::uv::Loop& loop, wpi::uv::Timer::Time reconnectRate,
+ wpi::Logger& logger,
+ std::function<void(wpi::uv::Tcp& tcp)> connected,
+ const private_init&);
+ ~ParallelTcpConnector();
+
+ ParallelTcpConnector(const ParallelTcpConnector&) = delete;
+ ParallelTcpConnector& operator=(const ParallelTcpConnector&) = delete;
+
+ /**
+ * Closes resources, canceling all pending action attempts.
+ */
+ void Close();
+
+ /**
+ * Changes the servers/ports to connect to. Starts connection attempts if not
+ * already connected.
+ *
+ * @param servers array of server/port pairs
+ */
+ void SetServers(
+ wpi::span<const std::pair<std::string, unsigned int>> servers);
+
+ /**
+ * Tells the parallel connector that the current connection has terminated and
+ * it is necessary to start reconnection attempts.
+ */
+ void Disconnected();
+
+ /**
+ * Tells the parallel connector that a particular connection has succeeded and
+ * it should stop trying to connect.
+ *
+ * @param tcp connection passed to connected callback
+ */
+ void Succeeded(wpi::uv::Tcp& tcp);
+
+ private:
+ bool IsConnected() const { return m_isConnected || m_servers.empty(); }
+ void Connect();
+ void CancelAll(wpi::uv::Tcp* except = nullptr);
+
+ wpi::uv::Loop& m_loop;
+ wpi::Logger& m_logger;
+ wpi::uv::Timer::Time m_reconnectRate;
+ std::function<void(wpi::uv::Tcp& tcp)> m_connected;
+ 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;
+ bool m_isConnected{false};
+};
+
+} // namespace wpi
diff --git a/wpiutil/src/main/native/include/wpi/Path.h b/wpiutil/src/main/native/include/wpi/Path.h
deleted file mode 100644
index 2a8e94c..0000000
--- a/wpiutil/src/main/native/include/wpi/Path.h
+++ /dev/null
@@ -1,473 +0,0 @@
-//===- llvm/Support/Path.h - Path Operating System Concept ------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file declares the wpi::sys::path namespace. It is designed after
-// TR2/boost filesystem (v3), but modified to remove exception handling and the
-// path class.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef WPIUTIL_WPI_PATH_H_
-#define WPIUTIL_WPI_PATH_H_
-
-#include "wpi/Twine.h"
-#include "wpi/iterator.h"
-#include <cstdint>
-#include <iterator>
-#include <system_error>
-
-#ifdef _WIN32
-// Disable iterator deprecation warning
-#pragma warning(push)
-#pragma warning(disable : 4996)
-#endif
-
-namespace wpi {
-namespace sys {
-namespace path {
-
-enum class Style { windows, posix, native };
-
-/// @name Lexical Component Iterator
-/// @{
-
-/// Path iterator.
-///
-/// This is an input iterator that iterates over the individual components in
-/// \a path. The traversal order is as follows:
-/// * The root-name element, if present.
-/// * The root-directory element, if present.
-/// * Each successive filename element, if present.
-/// * Dot, if one or more trailing non-root slash characters are present.
-/// Traversing backwards is possible with \a reverse_iterator
-///
-/// Iteration examples. Each component is separated by ',':
-/// @code
-/// / => /
-/// /foo => /,foo
-/// foo/ => foo,.
-/// /foo/bar => /,foo,bar
-/// ../ => ..,.
-/// C:\foo\bar => C:,/,foo,bar
-/// @endcode
-class const_iterator
- : public iterator_facade_base<const_iterator, std::input_iterator_tag,
- const StringRef> {
- StringRef Path; ///< The entire path.
- StringRef Component; ///< The current component. Not necessarily in Path.
- size_t Position; ///< The iterators current position within Path.
- Style S; ///< The path style to use.
-
- // An end iterator has Position = Path.size() + 1.
- friend const_iterator begin(StringRef path, Style style);
- friend const_iterator end(StringRef path);
-
-public:
- reference operator*() const { return Component; }
- const_iterator &operator++(); // preincrement
- bool operator==(const const_iterator &RHS) const;
-
- /// Difference in bytes between this and RHS.
- ptrdiff_t operator-(const const_iterator &RHS) const;
-};
-
-/// Reverse path iterator.
-///
-/// This is an input iterator that iterates over the individual components in
-/// \a path in reverse order. The traversal order is exactly reversed from that
-/// of \a const_iterator
-class reverse_iterator
- : public iterator_facade_base<reverse_iterator, std::input_iterator_tag,
- const StringRef> {
- StringRef Path; ///< The entire path.
- StringRef Component; ///< The current component. Not necessarily in Path.
- size_t Position; ///< The iterators current position within Path.
- Style S; ///< The path style to use.
-
- friend reverse_iterator rbegin(StringRef path, Style style);
- friend reverse_iterator rend(StringRef path);
-
-public:
- reference operator*() const { return Component; }
- reverse_iterator &operator++(); // preincrement
- bool operator==(const reverse_iterator &RHS) const;
-
- /// Difference in bytes between this and RHS.
- ptrdiff_t operator-(const reverse_iterator &RHS) const;
-};
-
-/// Get begin iterator over \a path.
-/// @param path Input path.
-/// @returns Iterator initialized with the first component of \a path.
-const_iterator begin(StringRef path, Style style = Style::native);
-
-/// Get end iterator over \a path.
-/// @param path Input path.
-/// @returns Iterator initialized to the end of \a path.
-const_iterator end(StringRef path);
-
-/// Get reverse begin iterator over \a path.
-/// @param path Input path.
-/// @returns Iterator initialized with the first reverse component of \a path.
-reverse_iterator rbegin(StringRef path, Style style = Style::native);
-
-/// Get reverse end iterator over \a path.
-/// @param path Input path.
-/// @returns Iterator initialized to the reverse end of \a path.
-reverse_iterator rend(StringRef path);
-
-/// @}
-/// @name Lexical Modifiers
-/// @{
-
-/// Remove the last component from \a path unless it is the root dir.
-///
-/// @code
-/// directory/filename.cpp => directory/
-/// directory/ => directory
-/// filename.cpp => <empty>
-/// / => /
-/// @endcode
-///
-/// @param path A path that is modified to not have a file component.
-void remove_filename(SmallVectorImpl<char> &path, Style style = Style::native);
-
-/// Replace the file extension of \a path with \a extension.
-///
-/// @code
-/// ./filename.cpp => ./filename.extension
-/// ./filename => ./filename.extension
-/// ./ => ./.extension
-/// @endcode
-///
-/// @param path A path that has its extension replaced with \a extension.
-/// @param extension The extension to be added. It may be empty. It may also
-/// optionally start with a '.', if it does not, one will be
-/// prepended.
-void replace_extension(SmallVectorImpl<char> &path, const Twine &extension,
- Style style = Style::native);
-
-/// Replace matching path prefix with another path.
-///
-/// @code
-/// /foo, /old, /new => /foo
-/// /old/foo, /old, /new => /new/foo
-/// /foo, <empty>, /new => /new/foo
-/// /old/foo, /old, <empty> => /foo
-/// @endcode
-///
-/// @param Path If \a Path starts with \a OldPrefix modify to instead
-/// start with \a NewPrefix.
-/// @param OldPrefix The path prefix to strip from \a Path.
-/// @param NewPrefix The path prefix to replace \a NewPrefix with.
-void replace_path_prefix(SmallVectorImpl<char> &Path,
- const StringRef &OldPrefix, const StringRef &NewPrefix,
- Style style = Style::native);
-
-/// Append to path.
-///
-/// @code
-/// /foo + bar/f => /foo/bar/f
-/// /foo/ + bar/f => /foo/bar/f
-/// foo + bar/f => foo/bar/f
-/// @endcode
-///
-/// @param path Set to \a path + \a component.
-/// @param a The component to be appended to \a path.
-void append(SmallVectorImpl<char> &path, const Twine &a,
- const Twine &b = "",
- const Twine &c = "",
- const Twine &d = "");
-
-void append(SmallVectorImpl<char> &path, Style style, const Twine &a,
- const Twine &b = "", const Twine &c = "", const Twine &d = "");
-
-/// Append to path.
-///
-/// @code
-/// /foo + [bar,f] => /foo/bar/f
-/// /foo/ + [bar,f] => /foo/bar/f
-/// foo + [bar,f] => foo/bar/f
-/// @endcode
-///
-/// @param path Set to \a path + [\a begin, \a end).
-/// @param begin Start of components to append.
-/// @param end One past the end of components to append.
-void append(SmallVectorImpl<char> &path, const_iterator begin,
- const_iterator end, Style style = Style::native);
-
-/// @}
-/// @name Transforms (or some other better name)
-/// @{
-
-/// Convert path to the native form. This is used to give paths to users and
-/// operating system calls in the platform's normal way. For example, on Windows
-/// all '/' are converted to '\'.
-///
-/// @param path A path that is transformed to native format.
-/// @param result Holds the result of the transformation.
-void native(const Twine &path, SmallVectorImpl<char> &result,
- Style style = Style::native);
-
-/// Convert path to the native form in place. This is used to give paths to
-/// users and operating system calls in the platform's normal way. For example,
-/// on Windows all '/' are converted to '\'.
-///
-/// @param path A path that is transformed to native format.
-void native(SmallVectorImpl<char> &path, Style style = Style::native);
-
-/// Replaces backslashes with slashes if Windows.
-///
-/// @param path processed path
-/// @result The result of replacing backslashes with forward slashes if Windows.
-/// On Unix, this function is a no-op because backslashes are valid path
-/// chracters.
-std::string convert_to_slash(StringRef path, Style style = Style::native);
-
-/// @}
-/// @name Lexical Observers
-/// @{
-
-/// Get root name.
-///
-/// @code
-/// //net/hello => //net
-/// c:/hello => c: (on Windows, on other platforms nothing)
-/// /hello => <empty>
-/// @endcode
-///
-/// @param path Input path.
-/// @result The root name of \a path if it has one, otherwise "".
-StringRef root_name(StringRef path, Style style = Style::native);
-
-/// Get root directory.
-///
-/// @code
-/// /goo/hello => /
-/// c:/hello => /
-/// d/file.txt => <empty>
-/// @endcode
-///
-/// @param path Input path.
-/// @result The root directory of \a path if it has one, otherwise
-/// "".
-StringRef root_directory(StringRef path, Style style = Style::native);
-
-/// Get root path.
-///
-/// Equivalent to root_name + root_directory.
-///
-/// @param path Input path.
-/// @result The root path of \a path if it has one, otherwise "".
-StringRef root_path(StringRef path, Style style = Style::native);
-
-/// Get relative path.
-///
-/// @code
-/// C:\hello\world => hello\world
-/// foo/bar => foo/bar
-/// /foo/bar => foo/bar
-/// @endcode
-///
-/// @param path Input path.
-/// @result The path starting after root_path if one exists, otherwise "".
-StringRef relative_path(StringRef path, Style style = Style::native);
-
-/// Get parent path.
-///
-/// @code
-/// / => <empty>
-/// /foo => /
-/// foo/../bar => foo/..
-/// @endcode
-///
-/// @param path Input path.
-/// @result The parent path of \a path if one exists, otherwise "".
-StringRef parent_path(StringRef path, Style style = Style::native);
-
-/// Get filename.
-///
-/// @code
-/// /foo.txt => foo.txt
-/// . => .
-/// .. => ..
-/// / => /
-/// @endcode
-///
-/// @param path Input path.
-/// @result The filename part of \a path. This is defined as the last component
-/// of \a path.
-StringRef filename(StringRef path, Style style = Style::native);
-
-/// Get stem.
-///
-/// If filename contains a dot but not solely one or two dots, result is the
-/// substring of filename ending at (but not including) the last dot. Otherwise
-/// it is filename.
-///
-/// @code
-/// /foo/bar.txt => bar
-/// /foo/bar => bar
-/// /foo/.txt => <empty>
-/// /foo/. => .
-/// /foo/.. => ..
-/// @endcode
-///
-/// @param path Input path.
-/// @result The stem of \a path.
-StringRef stem(StringRef path, Style style = Style::native);
-
-/// Get extension.
-///
-/// If filename contains a dot but not solely one or two dots, result is the
-/// substring of filename starting at (and including) the last dot, and ending
-/// at the end of \a path. Otherwise "".
-///
-/// @code
-/// /foo/bar.txt => .txt
-/// /foo/bar => <empty>
-/// /foo/.txt => .txt
-/// @endcode
-///
-/// @param path Input path.
-/// @result The extension of \a path.
-StringRef extension(StringRef path, Style style = Style::native);
-
-/// Check whether the given char is a path separator on the host OS.
-///
-/// @param value a character
-/// @result true if \a value is a path separator character on the host OS
-bool is_separator(char value, Style style = Style::native);
-
-/// Return the preferred separator for this platform.
-///
-/// @result StringRef of the preferred separator, null-terminated.
-StringRef get_separator(Style style = Style::native);
-
-/// Get the typical temporary directory for the system, e.g.,
-/// "/var/tmp" or "C:/TEMP"
-///
-/// @param erasedOnReboot Whether to favor a path that is erased on reboot
-/// rather than one that potentially persists longer. This parameter will be
-/// ignored if the user or system has set the typical environment variable
-/// (e.g., TEMP on Windows, TMPDIR on *nix) to specify a temporary directory.
-///
-/// @param result Holds the resulting path name.
-void system_temp_directory(bool erasedOnReboot, SmallVectorImpl<char> &result);
-
-/// Get the user's home directory.
-///
-/// @param result Holds the resulting path name.
-/// @result True if a home directory is set, false otherwise.
-bool home_directory(SmallVectorImpl<char> &result);
-
-/// Has root name?
-///
-/// root_name != ""
-///
-/// @param path Input path.
-/// @result True if the path has a root name, false otherwise.
-bool has_root_name(const Twine &path, Style style = Style::native);
-
-/// Has root directory?
-///
-/// root_directory != ""
-///
-/// @param path Input path.
-/// @result True if the path has a root directory, false otherwise.
-bool has_root_directory(const Twine &path, Style style = Style::native);
-
-/// Has root path?
-///
-/// root_path != ""
-///
-/// @param path Input path.
-/// @result True if the path has a root path, false otherwise.
-bool has_root_path(const Twine &path, Style style = Style::native);
-
-/// Has relative path?
-///
-/// relative_path != ""
-///
-/// @param path Input path.
-/// @result True if the path has a relative path, false otherwise.
-bool has_relative_path(const Twine &path, Style style = Style::native);
-
-/// Has parent path?
-///
-/// parent_path != ""
-///
-/// @param path Input path.
-/// @result True if the path has a parent path, false otherwise.
-bool has_parent_path(const Twine &path, Style style = Style::native);
-
-/// Has filename?
-///
-/// filename != ""
-///
-/// @param path Input path.
-/// @result True if the path has a filename, false otherwise.
-bool has_filename(const Twine &path, Style style = Style::native);
-
-/// Has stem?
-///
-/// stem != ""
-///
-/// @param path Input path.
-/// @result True if the path has a stem, false otherwise.
-bool has_stem(const Twine &path, Style style = Style::native);
-
-/// Has extension?
-///
-/// extension != ""
-///
-/// @param path Input path.
-/// @result True if the path has a extension, false otherwise.
-bool has_extension(const Twine &path, Style style = Style::native);
-
-/// Is path absolute?
-///
-/// @param path Input path.
-/// @result True if the path is absolute, false if it is not.
-bool is_absolute(const Twine &path, Style style = Style::native);
-
-/// Is path relative?
-///
-/// @param path Input path.
-/// @result True if the path is relative, false if it is not.
-bool is_relative(const Twine &path, Style style = Style::native);
-
-/// Remove redundant leading "./" pieces and consecutive separators.
-///
-/// @param path Input path.
-/// @result The cleaned-up \a path.
-StringRef remove_leading_dotslash(StringRef path, Style style = Style::native);
-
-/// In-place remove any './' and optionally '../' components from a path.
-///
-/// @param path processed path
-/// @param remove_dot_dot specify if '../' (except for leading "../") should be
-/// removed
-/// @result True if path was changed
-bool remove_dots(SmallVectorImpl<char> &path, bool remove_dot_dot = false,
- Style style = Style::native);
-
-#if defined(_WIN32)
-std::error_code widenPath(const Twine &Path8, SmallVectorImpl<wchar_t> &Path16);
-#endif
-
-} // end namespace path
-} // end namespace sys
-} // end namespace wpi
-
-#ifdef _WIN32
-#pragma warning(pop)
-#endif
-
-#endif
diff --git a/wpiutil/src/main/native/include/wpi/PortForwarder.h b/wpiutil/src/main/native/include/wpi/PortForwarder.h
index 9ee6ce6..9e445a0 100644
--- a/wpiutil/src/main/native/include/wpi/PortForwarder.h
+++ b/wpiutil/src/main/native/include/wpi/PortForwarder.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2019 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_PORTFORWARDER_H_
#define WPIUTIL_WPI_PORTFORWARDER_H_
@@ -11,8 +8,7 @@
#pragma once
#include <memory>
-
-#include "wpi/Twine.h"
+#include <string_view>
namespace wpi {
@@ -41,7 +37,8 @@
* @param remoteHost remote IP address / DNS name
* @param remotePort remote port number
*/
- void Add(unsigned int port, const Twine& remoteHost, unsigned int remotePort);
+ void Add(unsigned int port, std::string_view remoteHost,
+ unsigned int remotePort);
/**
* Stop TCP forwarding on a port.
diff --git a/wpiutil/src/main/native/include/wpi/STLExtras.h b/wpiutil/src/main/native/include/wpi/STLExtras.h
deleted file mode 100644
index d7ddd92..0000000
--- a/wpiutil/src/main/native/include/wpi/STLExtras.h
+++ /dev/null
@@ -1,1348 +0,0 @@
-//===- llvm/ADT/STLExtras.h - Useful STL related functions ------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file contains some templates that are useful if you are working with the
-// STL at all.
-//
-// No library is required when using these functions.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef WPIUTIL_WPI_STLEXTRAS_H
-#define WPIUTIL_WPI_STLEXTRAS_H
-
-#include "wpi/SmallVector.h"
-#include "wpi/iterator.h"
-#include "wpi/iterator_range.h"
-#include "wpi/ErrorHandling.h"
-#include <algorithm>
-#include <cassert>
-#include <cstddef>
-#include <cstdint>
-#include <cstdlib>
-#include <functional>
-#include <initializer_list>
-#include <iterator>
-#include <limits>
-#include <memory>
-#include <optional>
-#include <tuple>
-#include <type_traits>
-#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 detail {
-
-template <typename RangeT>
-using IterOfRange = decltype(std::begin(std::declval<RangeT &>()));
-
-template <typename RangeT>
-using ValueOfRange = typename std::remove_reference<decltype(
- *std::begin(std::declval<RangeT &>()))>::type;
-
-} // end namespace detail
-
-//===----------------------------------------------------------------------===//
-// Extra additions to <type_traits>
-//===----------------------------------------------------------------------===//
-
-template <typename T> struct make_const_ptr {
- using type =
- typename std::add_pointer<typename std::add_const<T>::type>::type;
-};
-
-template <typename T> struct make_const_ref {
- using type = typename std::add_lvalue_reference<
- typename std::add_const<T>::type>::type;
-};
-
-//===----------------------------------------------------------------------===//
-// Extra additions to <functional>
-//===----------------------------------------------------------------------===//
-
-template <class Ty> struct identity {
- using argument_type = Ty;
-
- Ty &operator()(Ty &self) const {
- return self;
- }
- const Ty &operator()(const Ty &self) const {
- return self;
- }
-};
-
-template <class Ty> struct less_ptr {
- bool operator()(const Ty* left, const Ty* right) const {
- return *left < *right;
- }
-};
-
-template <class Ty> struct greater_ptr {
- bool operator()(const Ty* left, const Ty* right) const {
- return *right < *left;
- }
-};
-
-/// An efficient, type-erasing, non-owning reference to a callable. This is
-/// intended for use as the type of a function parameter that is not used
-/// after the function in question returns.
-///
-/// This class does not own the callable, so it is not in general safe to store
-/// a function_ref.
-template<typename Fn> class function_ref;
-
-template<typename Ret, typename ...Params>
-class function_ref<Ret(Params...)> {
- Ret (*callback)(intptr_t callable, Params ...params) = nullptr;
- intptr_t callable;
-
- template<typename Callable>
- static Ret callback_fn(intptr_t callable, Params ...params) {
- return (*reinterpret_cast<Callable*>(callable))(
- std::forward<Params>(params)...);
- }
-
-public:
- function_ref() = default;
- function_ref(std::nullptr_t) {}
-
- template <typename Callable>
- function_ref(Callable &&callable,
- typename std::enable_if<
- !std::is_same<typename std::remove_reference<Callable>::type,
- function_ref>::value>::type * = nullptr)
- : callback(callback_fn<typename std::remove_reference<Callable>::type>),
- callable(reinterpret_cast<intptr_t>(&callable)) {}
-
- Ret operator()(Params ...params) const {
- return callback(callable, std::forward<Params>(params)...);
- }
-
- explicit operator bool() const { return callback; }
-};
-
-// deleter - Very very very simple method that is used to invoke operator
-// delete on something. It is used like this:
-//
-// for_each(V.begin(), B.end(), deleter<Interval>);
-template <class T>
-inline void deleter(T *Ptr) {
- delete Ptr;
-}
-
-//===----------------------------------------------------------------------===//
-// Extra additions to <iterator>
-//===----------------------------------------------------------------------===//
-
-namespace adl_detail {
-
-using std::begin;
-
-template <typename ContainerTy>
-auto adl_begin(ContainerTy &&container)
- -> decltype(begin(std::forward<ContainerTy>(container))) {
- return begin(std::forward<ContainerTy>(container));
-}
-
-using std::end;
-
-template <typename ContainerTy>
-auto adl_end(ContainerTy &&container)
- -> decltype(end(std::forward<ContainerTy>(container))) {
- return end(std::forward<ContainerTy>(container));
-}
-
-using std::swap;
-
-template <typename T>
-void adl_swap(T &&lhs, T &&rhs) noexcept(noexcept(swap(std::declval<T>(),
- std::declval<T>()))) {
- swap(std::forward<T>(lhs), std::forward<T>(rhs));
-}
-
-} // end namespace adl_detail
-
-template <typename ContainerTy>
-auto adl_begin(ContainerTy &&container)
- -> decltype(adl_detail::adl_begin(std::forward<ContainerTy>(container))) {
- return adl_detail::adl_begin(std::forward<ContainerTy>(container));
-}
-
-template <typename ContainerTy>
-auto adl_end(ContainerTy &&container)
- -> decltype(adl_detail::adl_end(std::forward<ContainerTy>(container))) {
- return adl_detail::adl_end(std::forward<ContainerTy>(container));
-}
-
-template <typename T>
-void adl_swap(T &&lhs, T &&rhs) noexcept(
- noexcept(adl_detail::adl_swap(std::declval<T>(), std::declval<T>()))) {
- adl_detail::adl_swap(std::forward<T>(lhs), std::forward<T>(rhs));
-}
-
-/// Test whether \p RangeOrContainer is empty. Similar to C++17 std::empty.
-template <typename T>
-constexpr bool empty(const T &RangeOrContainer) {
- return adl_begin(RangeOrContainer) == adl_end(RangeOrContainer);
-}
-
-// mapped_iterator - This is a simple iterator adapter that causes a function to
-// be applied whenever operator* is invoked on the iterator.
-
-template <typename ItTy, typename FuncTy,
- typename FuncReturnTy =
- decltype(std::declval<FuncTy>()(*std::declval<ItTy>()))>
-class mapped_iterator
- : public iterator_adaptor_base<
- mapped_iterator<ItTy, FuncTy>, ItTy,
- typename std::iterator_traits<ItTy>::iterator_category,
- typename std::remove_reference<FuncReturnTy>::type> {
-public:
- mapped_iterator(ItTy U, FuncTy F)
- : mapped_iterator::iterator_adaptor_base(std::move(U)), F(std::move(F)) {}
-
- ItTy getCurrent() { return this->I; }
-
- FuncReturnTy operator*() { return F(*this->I); }
-
-private:
- FuncTy F;
-};
-
-// map_iterator - Provide a convenient way to create mapped_iterators, just like
-// make_pair is useful for creating pairs...
-template <class ItTy, class FuncTy>
-inline mapped_iterator<ItTy, FuncTy> map_iterator(ItTy I, FuncTy F) {
- return mapped_iterator<ItTy, FuncTy>(std::move(I), std::move(F));
-}
-
-/// Helper to determine if type T has a member called rbegin().
-template <typename Ty> class has_rbegin_impl {
- using yes = char[1];
- using no = char[2];
-
- template <typename Inner>
- static yes& test(Inner *I, decltype(I->rbegin()) * = nullptr);
-
- template <typename>
- static no& test(...);
-
-public:
- static const bool value = sizeof(test<Ty>(nullptr)) == sizeof(yes);
-};
-
-/// Metafunction to determine if T& or T has a member called rbegin().
-template <typename Ty>
-struct has_rbegin : has_rbegin_impl<typename std::remove_reference<Ty>::type> {
-};
-
-// Returns an iterator_range over the given container which iterates in reverse.
-// Note that the container must have rbegin()/rend() methods for this to work.
-template <typename ContainerTy>
-auto reverse(ContainerTy &&C,
- typename std::enable_if<has_rbegin<ContainerTy>::value>::type * =
- nullptr) -> decltype(make_range(C.rbegin(), C.rend())) {
- return make_range(C.rbegin(), C.rend());
-}
-
-// Returns a std::reverse_iterator wrapped around the given iterator.
-template <typename IteratorTy>
-std::reverse_iterator<IteratorTy> make_reverse_iterator(IteratorTy It) {
- return std::reverse_iterator<IteratorTy>(It);
-}
-
-// Returns an iterator_range over the given container which iterates in reverse.
-// Note that the container must have begin()/end() methods which return
-// bidirectional iterators for this to work.
-template <typename ContainerTy>
-auto reverse(
- ContainerTy &&C,
- typename std::enable_if<!has_rbegin<ContainerTy>::value>::type * = nullptr)
- -> decltype(make_range(wpi::make_reverse_iterator(std::end(C)),
- wpi::make_reverse_iterator(std::begin(C)))) {
- return make_range(wpi::make_reverse_iterator(std::end(C)),
- wpi::make_reverse_iterator(std::begin(C)));
-}
-
-/// An iterator adaptor that filters the elements of given inner iterators.
-///
-/// The predicate parameter should be a callable object that accepts the wrapped
-/// iterator's reference type and returns a bool. When incrementing or
-/// decrementing the iterator, it will call the predicate on each element and
-/// skip any where it returns false.
-///
-/// \code
-/// int A[] = { 1, 2, 3, 4 };
-/// auto R = make_filter_range(A, [](int N) { return N % 2 == 1; });
-/// // R contains { 1, 3 }.
-/// \endcode
-///
-/// Note: filter_iterator_base implements support for forward iteration.
-/// filter_iterator_impl exists to provide support for bidirectional iteration,
-/// conditional on whether the wrapped iterator supports it.
-template <typename WrappedIteratorT, typename PredicateT, typename IterTag>
-class filter_iterator_base
- : public iterator_adaptor_base<
- filter_iterator_base<WrappedIteratorT, PredicateT, IterTag>,
- WrappedIteratorT,
- typename std::common_type<
- IterTag, typename std::iterator_traits<
- WrappedIteratorT>::iterator_category>::type> {
- using BaseT = iterator_adaptor_base<
- filter_iterator_base<WrappedIteratorT, PredicateT, IterTag>,
- WrappedIteratorT,
- typename std::common_type<
- IterTag, typename std::iterator_traits<
- WrappedIteratorT>::iterator_category>::type>;
-
-protected:
- WrappedIteratorT End;
- PredicateT Pred;
-
- void findNextValid() {
- while (this->I != End && !Pred(*this->I))
- BaseT::operator++();
- }
-
- // Construct the iterator. The begin iterator needs to know where the end
- // is, so that it can properly stop when it gets there. The end iterator only
- // needs the predicate to support bidirectional iteration.
- filter_iterator_base(WrappedIteratorT Begin, WrappedIteratorT End,
- PredicateT Pred)
- : BaseT(Begin), End(End), Pred(Pred) {
- findNextValid();
- }
-
-public:
- using BaseT::operator++;
-
- filter_iterator_base &operator++() {
- BaseT::operator++();
- findNextValid();
- return *this;
- }
-};
-
-/// Specialization of filter_iterator_base for forward iteration only.
-template <typename WrappedIteratorT, typename PredicateT,
- typename IterTag = std::forward_iterator_tag>
-class filter_iterator_impl
- : public filter_iterator_base<WrappedIteratorT, PredicateT, IterTag> {
- using BaseT = filter_iterator_base<WrappedIteratorT, PredicateT, IterTag>;
-
-public:
- filter_iterator_impl(WrappedIteratorT Begin, WrappedIteratorT End,
- PredicateT Pred)
- : BaseT(Begin, End, Pred) {}
-};
-
-/// Specialization of filter_iterator_base for bidirectional iteration.
-template <typename WrappedIteratorT, typename PredicateT>
-class filter_iterator_impl<WrappedIteratorT, PredicateT,
- std::bidirectional_iterator_tag>
- : public filter_iterator_base<WrappedIteratorT, PredicateT,
- std::bidirectional_iterator_tag> {
- using BaseT = filter_iterator_base<WrappedIteratorT, PredicateT,
- std::bidirectional_iterator_tag>;
- void findPrevValid() {
- while (!this->Pred(*this->I))
- BaseT::operator--();
- }
-
-public:
- using BaseT::operator--;
-
- filter_iterator_impl(WrappedIteratorT Begin, WrappedIteratorT End,
- PredicateT Pred)
- : BaseT(Begin, End, Pred) {}
-
- filter_iterator_impl &operator--() {
- BaseT::operator--();
- findPrevValid();
- return *this;
- }
-};
-
-namespace detail {
-
-template <bool is_bidirectional> struct fwd_or_bidi_tag_impl {
- using type = std::forward_iterator_tag;
-};
-
-template <> struct fwd_or_bidi_tag_impl<true> {
- using type = std::bidirectional_iterator_tag;
-};
-
-/// Helper which sets its type member to forward_iterator_tag if the category
-/// of \p IterT does not derive from bidirectional_iterator_tag, and to
-/// bidirectional_iterator_tag otherwise.
-template <typename IterT> struct fwd_or_bidi_tag {
- using type = typename fwd_or_bidi_tag_impl<std::is_base_of<
- std::bidirectional_iterator_tag,
- typename std::iterator_traits<IterT>::iterator_category>::value>::type;
-};
-
-} // namespace detail
-
-/// Defines filter_iterator to a suitable specialization of
-/// filter_iterator_impl, based on the underlying iterator's category.
-template <typename WrappedIteratorT, typename PredicateT>
-using filter_iterator = filter_iterator_impl<
- WrappedIteratorT, PredicateT,
- typename detail::fwd_or_bidi_tag<WrappedIteratorT>::type>;
-
-/// Convenience function that takes a range of elements and a predicate,
-/// and return a new filter_iterator range.
-///
-/// FIXME: Currently if RangeT && is a rvalue reference to a temporary, the
-/// lifetime of that temporary is not kept by the returned range object, and the
-/// temporary is going to be dropped on the floor after the make_iterator_range
-/// full expression that contains this function call.
-template <typename RangeT, typename PredicateT>
-iterator_range<filter_iterator<detail::IterOfRange<RangeT>, PredicateT>>
-make_filter_range(RangeT &&Range, PredicateT Pred) {
- using FilterIteratorT =
- filter_iterator<detail::IterOfRange<RangeT>, PredicateT>;
- return make_range(
- FilterIteratorT(std::begin(std::forward<RangeT>(Range)),
- std::end(std::forward<RangeT>(Range)), Pred),
- FilterIteratorT(std::end(std::forward<RangeT>(Range)),
- std::end(std::forward<RangeT>(Range)), Pred));
-}
-
-// forward declarations required by zip_shortest/zip_first
-template <typename R, typename UnaryPredicate>
-bool all_of(R &&range, UnaryPredicate P);
-template <typename R, typename UnaryPredicate>
-bool any_of(R &&range, UnaryPredicate P);
-
-namespace detail {
-
-using std::declval;
-
-// We have to alias this since inlining the actual type at the usage site
-// in the parameter list of iterator_facade_base<> below ICEs MSVC 2017.
-template<typename... Iters> struct ZipTupleType {
- using type = std::tuple<decltype(*declval<Iters>())...>;
-};
-
-template <typename ZipType, typename... Iters>
-using zip_traits = iterator_facade_base<
- ZipType, typename std::common_type<std::bidirectional_iterator_tag,
- typename std::iterator_traits<
- Iters>::iterator_category...>::type,
- // ^ TODO: Implement random access methods.
- typename ZipTupleType<Iters...>::type,
- typename std::iterator_traits<typename std::tuple_element<
- 0, std::tuple<Iters...>>::type>::difference_type,
- // ^ FIXME: This follows boost::make_zip_iterator's assumption that all
- // inner iterators have the same difference_type. It would fail if, for
- // instance, the second field's difference_type were non-numeric while the
- // first is.
- typename ZipTupleType<Iters...>::type *,
- typename ZipTupleType<Iters...>::type>;
-
-template <typename ZipType, typename... Iters>
-struct zip_common : public zip_traits<ZipType, Iters...> {
- using Base = zip_traits<ZipType, Iters...>;
- using value_type = typename Base::value_type;
-
- std::tuple<Iters...> iterators;
-
-protected:
- template <size_t... Ns> value_type deref(std::index_sequence<Ns...>) const {
- return value_type(*std::get<Ns>(iterators)...);
- }
-
- template <size_t... Ns>
- decltype(iterators) tup_inc(std::index_sequence<Ns...>) const {
- return std::tuple<Iters...>(std::next(std::get<Ns>(iterators))...);
- }
-
- template <size_t... Ns>
- decltype(iterators) tup_dec(std::index_sequence<Ns...>) const {
- return std::tuple<Iters...>(std::prev(std::get<Ns>(iterators))...);
- }
-
-public:
- zip_common(Iters &&... ts) : iterators(std::forward<Iters>(ts)...) {}
-
- value_type operator*() { return deref(std::index_sequence_for<Iters...>{}); }
-
- const value_type operator*() const {
- return deref(std::index_sequence_for<Iters...>{});
- }
-
- ZipType &operator++() {
- iterators = tup_inc(std::index_sequence_for<Iters...>{});
- return *reinterpret_cast<ZipType *>(this);
- }
-
- ZipType &operator--() {
- static_assert(Base::IsBidirectional,
- "All inner iterators must be at least bidirectional.");
- iterators = tup_dec(std::index_sequence_for<Iters...>{});
- return *reinterpret_cast<ZipType *>(this);
- }
-};
-
-template <typename... Iters>
-struct zip_first : public zip_common<zip_first<Iters...>, Iters...> {
- using Base = zip_common<zip_first<Iters...>, Iters...>;
-
- bool operator==(const zip_first<Iters...> &other) const {
- return std::get<0>(this->iterators) == std::get<0>(other.iterators);
- }
-
- zip_first(Iters &&... ts) : Base(std::forward<Iters>(ts)...) {}
-};
-
-template <typename... Iters>
-class zip_shortest : public zip_common<zip_shortest<Iters...>, Iters...> {
- template <size_t... Ns>
- bool test(const zip_shortest<Iters...> &other, std::index_sequence<Ns...>) const {
- return all_of(std::initializer_list<bool>{std::get<Ns>(this->iterators) !=
- std::get<Ns>(other.iterators)...},
- identity<bool>{});
- }
-
-public:
- using Base = zip_common<zip_shortest<Iters...>, Iters...>;
-
- zip_shortest(Iters &&... ts) : Base(std::forward<Iters>(ts)...) {}
-
- bool operator==(const zip_shortest<Iters...> &other) const {
- return !test(other, std::index_sequence_for<Iters...>{});
- }
-};
-
-template <template <typename...> class ItType, typename... Args> class zippy {
-public:
- using iterator = ItType<decltype(std::begin(std::declval<Args>()))...>;
- using iterator_category = typename iterator::iterator_category;
- using value_type = typename iterator::value_type;
- using difference_type = typename iterator::difference_type;
- using pointer = typename iterator::pointer;
- using reference = typename iterator::reference;
-
-private:
- std::tuple<Args...> ts;
-
- template <size_t... Ns> iterator begin_impl(std::index_sequence<Ns...>) const {
- return iterator(std::begin(std::get<Ns>(ts))...);
- }
- template <size_t... Ns> iterator end_impl(std::index_sequence<Ns...>) const {
- return iterator(std::end(std::get<Ns>(ts))...);
- }
-
-public:
- zippy(Args &&... ts_) : ts(std::forward<Args>(ts_)...) {}
-
- iterator begin() const { return begin_impl(std::index_sequence_for<Args...>{}); }
- iterator end() const { return end_impl(std::index_sequence_for<Args...>{}); }
-};
-
-} // end namespace detail
-
-/// zip iterator for two or more iteratable types.
-template <typename T, typename U, typename... Args>
-detail::zippy<detail::zip_shortest, T, U, Args...> zip(T &&t, U &&u,
- Args &&... args) {
- return detail::zippy<detail::zip_shortest, T, U, Args...>(
- std::forward<T>(t), std::forward<U>(u), std::forward<Args>(args)...);
-}
-
-/// zip iterator that, for the sake of efficiency, assumes the first iteratee to
-/// be the shortest.
-template <typename T, typename U, typename... Args>
-detail::zippy<detail::zip_first, T, U, Args...> zip_first(T &&t, U &&u,
- Args &&... args) {
- return detail::zippy<detail::zip_first, T, U, Args...>(
- std::forward<T>(t), std::forward<U>(u), std::forward<Args>(args)...);
-}
-
-namespace detail {
-template <typename Iter>
-static Iter next_or_end(const Iter &I, const Iter &End) {
- if (I == End)
- return End;
- return std::next(I);
-}
-
-template <typename Iter>
-static auto deref_or_none(const Iter &I, const Iter &End)
- -> std::optional<typename std::remove_const<
- typename std::remove_reference<decltype(*I)>::type>::type> {
- if (I == End)
- return std::nullopt;
- return *I;
-}
-
-template <typename Iter> struct ZipLongestItemType {
- using type =
- std::optional<typename std::remove_const<typename std::remove_reference<
- decltype(*std::declval<Iter>())>::type>::type>;
-};
-
-template <typename... Iters> struct ZipLongestTupleType {
- using type = std::tuple<typename ZipLongestItemType<Iters>::type...>;
-};
-
-template <typename... Iters>
-class zip_longest_iterator
- : public iterator_facade_base<
- zip_longest_iterator<Iters...>,
- typename std::common_type<
- std::forward_iterator_tag,
- typename std::iterator_traits<Iters>::iterator_category...>::type,
- typename ZipLongestTupleType<Iters...>::type,
- typename std::iterator_traits<typename std::tuple_element<
- 0, std::tuple<Iters...>>::type>::difference_type,
- typename ZipLongestTupleType<Iters...>::type *,
- typename ZipLongestTupleType<Iters...>::type> {
-public:
- using value_type = typename ZipLongestTupleType<Iters...>::type;
-
-private:
- std::tuple<Iters...> iterators;
- std::tuple<Iters...> end_iterators;
-
- template <size_t... Ns>
- bool test(const zip_longest_iterator<Iters...> &other,
- std::index_sequence<Ns...>) const {
- return wpi::any_of(
- std::initializer_list<bool>{std::get<Ns>(this->iterators) !=
- std::get<Ns>(other.iterators)...},
- identity<bool>{});
- }
-
- template <size_t... Ns> value_type deref(std::index_sequence<Ns...>) const {
- return value_type(
- deref_or_none(std::get<Ns>(iterators), std::get<Ns>(end_iterators))...);
- }
-
- template <size_t... Ns>
- decltype(iterators) tup_inc(std::index_sequence<Ns...>) const {
- return std::tuple<Iters...>(
- next_or_end(std::get<Ns>(iterators), std::get<Ns>(end_iterators))...);
- }
-
-public:
- zip_longest_iterator(std::pair<Iters &&, Iters &&>... ts)
- : iterators(std::forward<Iters>(ts.first)...),
- end_iterators(std::forward<Iters>(ts.second)...) {}
-
- value_type operator*() { return deref(std::index_sequence_for<Iters...>{}); }
-
- value_type operator*() const { return deref(std::index_sequence_for<Iters...>{}); }
-
- zip_longest_iterator<Iters...> &operator++() {
- iterators = tup_inc(std::index_sequence_for<Iters...>{});
- return *this;
- }
-
- bool operator==(const zip_longest_iterator<Iters...> &other) const {
- return !test(other, std::index_sequence_for<Iters...>{});
- }
-};
-
-template <typename... Args> class zip_longest_range {
-public:
- using iterator =
- zip_longest_iterator<decltype(adl_begin(std::declval<Args>()))...>;
- using iterator_category = typename iterator::iterator_category;
- using value_type = typename iterator::value_type;
- using difference_type = typename iterator::difference_type;
- using pointer = typename iterator::pointer;
- using reference = typename iterator::reference;
-
-private:
- std::tuple<Args...> ts;
-
- template <size_t... Ns> iterator begin_impl(std::index_sequence<Ns...>) const {
- return iterator(std::make_pair(adl_begin(std::get<Ns>(ts)),
- adl_end(std::get<Ns>(ts)))...);
- }
-
- template <size_t... Ns> iterator end_impl(std::index_sequence<Ns...>) const {
- return iterator(std::make_pair(adl_end(std::get<Ns>(ts)),
- adl_end(std::get<Ns>(ts)))...);
- }
-
-public:
- zip_longest_range(Args &&... ts_) : ts(std::forward<Args>(ts_)...) {}
-
- iterator begin() const { return begin_impl(std::index_sequence_for<Args...>{}); }
- iterator end() const { return end_impl(std::index_sequence_for<Args...>{}); }
-};
-} // namespace detail
-
-/// Iterate over two or more iterators at the same time. Iteration continues
-/// until all iterators reach the end. The std::optional only contains a value
-/// if the iterator has not reached the end.
-template <typename T, typename U, typename... Args>
-detail::zip_longest_range<T, U, Args...> zip_longest(T &&t, U &&u,
- Args &&... args) {
- return detail::zip_longest_range<T, U, Args...>(
- std::forward<T>(t), std::forward<U>(u), std::forward<Args>(args)...);
-}
-
-/// Iterator wrapper that concatenates sequences together.
-///
-/// This can concatenate different iterators, even with different types, into
-/// a single iterator provided the value types of all the concatenated
-/// iterators expose `reference` and `pointer` types that can be converted to
-/// `ValueT &` and `ValueT *` respectively. It doesn't support more
-/// interesting/customized pointer or reference types.
-///
-/// Currently this only supports forward or higher iterator categories as
-/// inputs and always exposes a forward iterator interface.
-template <typename ValueT, typename... IterTs>
-class concat_iterator
- : public iterator_facade_base<concat_iterator<ValueT, IterTs...>,
- std::forward_iterator_tag, ValueT> {
- using BaseT = typename concat_iterator::iterator_facade_base;
-
- /// We store both the current and end iterators for each concatenated
- /// sequence in a tuple of pairs.
- ///
- /// Note that something like iterator_range seems nice at first here, but the
- /// range properties are of little benefit and end up getting in the way
- /// because we need to do mutation on the current iterators.
- std::tuple<IterTs...> Begins;
- std::tuple<IterTs...> Ends;
-
- /// Attempts to increment a specific iterator.
- ///
- /// Returns true if it was able to increment the iterator. Returns false if
- /// the iterator is already at the end iterator.
- template <size_t Index> bool incrementHelper() {
- auto &Begin = std::get<Index>(Begins);
- auto &End = std::get<Index>(Ends);
- if (Begin == End)
- return false;
-
- ++Begin;
- return true;
- }
-
- /// Increments the first non-end iterator.
- ///
- /// It is an error to call this with all iterators at the end.
- template <size_t... Ns> void increment(std::index_sequence<Ns...>) {
- // Build a sequence of functions to increment each iterator if possible.
- bool (concat_iterator::*IncrementHelperFns[])() = {
- &concat_iterator::incrementHelper<Ns>...};
-
- // Loop over them, and stop as soon as we succeed at incrementing one.
- for (auto &IncrementHelperFn : IncrementHelperFns)
- if ((this->*IncrementHelperFn)())
- return;
-
- assert(false && "Attempted to increment an end concat iterator!");
- }
-
- /// Returns null if the specified iterator is at the end. Otherwise,
- /// dereferences the iterator and returns the address of the resulting
- /// reference.
- template <size_t Index> ValueT *getHelper() const {
- auto &Begin = std::get<Index>(Begins);
- auto &End = std::get<Index>(Ends);
- if (Begin == End)
- return nullptr;
-
- return &*Begin;
- }
-
- /// Finds the first non-end iterator, dereferences, and returns the resulting
- /// reference.
- ///
- /// It is an error to call this with all iterators at the end.
- template <size_t... Ns> ValueT &get(std::index_sequence<Ns...>) const {
- // Build a sequence of functions to get from iterator if possible.
- ValueT *(concat_iterator::*GetHelperFns[])() const = {
- &concat_iterator::getHelper<Ns>...};
-
- // Loop over them, and return the first result we find.
- for (auto &GetHelperFn : GetHelperFns)
- if (ValueT *P = (this->*GetHelperFn)())
- return *P;
-
- assert(false && "Attempted to get a pointer from an end concat iterator!");
- }
-
-public:
- /// Constructs an iterator from a squence of ranges.
- ///
- /// We need the full range to know how to switch between each of the
- /// iterators.
- template <typename... RangeTs>
- explicit concat_iterator(RangeTs &&... Ranges)
- : Begins(std::begin(Ranges)...), Ends(std::end(Ranges)...) {}
-
- using BaseT::operator++;
-
- concat_iterator &operator++() {
- increment(std::index_sequence_for<IterTs...>());
- return *this;
- }
-
- ValueT &operator*() const { return get(std::index_sequence_for<IterTs...>()); }
-
- bool operator==(const concat_iterator &RHS) const {
- return Begins == RHS.Begins && Ends == RHS.Ends;
- }
-};
-
-namespace detail {
-
-/// Helper to store a sequence of ranges being concatenated and access them.
-///
-/// This is designed to facilitate providing actual storage when temporaries
-/// are passed into the constructor such that we can use it as part of range
-/// based for loops.
-template <typename ValueT, typename... RangeTs> class concat_range {
-public:
- using iterator =
- concat_iterator<ValueT,
- decltype(std::begin(std::declval<RangeTs &>()))...>;
-
-private:
- std::tuple<RangeTs...> Ranges;
-
- template <size_t... Ns> iterator begin_impl(std::index_sequence<Ns...>) {
- return iterator(std::get<Ns>(Ranges)...);
- }
- template <size_t... Ns> iterator end_impl(std::index_sequence<Ns...>) {
- return iterator(make_range(std::end(std::get<Ns>(Ranges)),
- std::end(std::get<Ns>(Ranges)))...);
- }
-
-public:
- concat_range(RangeTs &&... Ranges)
- : Ranges(std::forward<RangeTs>(Ranges)...) {}
-
- iterator begin() { return begin_impl(std::index_sequence_for<RangeTs...>{}); }
- iterator end() { return end_impl(std::index_sequence_for<RangeTs...>{}); }
-};
-
-} // end namespace detail
-
-/// Concatenated range across two or more ranges.
-///
-/// The desired value type must be explicitly specified.
-template <typename ValueT, typename... RangeTs>
-detail::concat_range<ValueT, RangeTs...> concat(RangeTs &&... Ranges) {
- static_assert(sizeof...(RangeTs) > 1,
- "Need more than one range to concatenate!");
- return detail::concat_range<ValueT, RangeTs...>(
- std::forward<RangeTs>(Ranges)...);
-}
-
-//===----------------------------------------------------------------------===//
-// Extra additions to <utility>
-//===----------------------------------------------------------------------===//
-
-/// Function object to check whether the first component of a std::pair
-/// compares less than the first component of another std::pair.
-struct less_first {
- template <typename T> bool operator()(const T &lhs, const T &rhs) const {
- return lhs.first < rhs.first;
- }
-};
-
-/// Function object to check whether the second component of a std::pair
-/// compares less than the second component of another std::pair.
-struct less_second {
- template <typename T> bool operator()(const T &lhs, const T &rhs) const {
- return lhs.second < rhs.second;
- }
-};
-
-/// \brief Function object to apply a binary function to the first component of
-/// a std::pair.
-template<typename FuncTy>
-struct on_first {
- FuncTy func;
-
- template <typename T>
- auto operator()(const T &lhs, const T &rhs) const
- -> decltype(func(lhs.first, rhs.first)) {
- return func(lhs.first, rhs.first);
- }
-};
-
-/// Utility type to build an inheritance chain that makes it easy to rank
-/// overload candidates.
-template <int N> struct rank : rank<N - 1> {};
-template <> struct rank<0> {};
-
-/// traits class for checking whether type T is one of any of the given
-/// types in the variadic list.
-template <typename T, typename... Ts> struct is_one_of {
- static const bool value = false;
-};
-
-template <typename T, typename U, typename... Ts>
-struct is_one_of<T, U, Ts...> {
- static const bool value =
- std::is_same<T, U>::value || is_one_of<T, Ts...>::value;
-};
-
-/// traits class for checking whether type T is a base class for all
-/// the given types in the variadic list.
-template <typename T, typename... Ts> struct are_base_of {
- static const bool value = true;
-};
-
-template <typename T, typename U, typename... Ts>
-struct are_base_of<T, U, Ts...> {
- static const bool value =
- std::is_base_of<T, U>::value && are_base_of<T, Ts...>::value;
-};
-
-//===----------------------------------------------------------------------===//
-// Extra additions for arrays
-//===----------------------------------------------------------------------===//
-
-/// Find the length of an array.
-template <class T, std::size_t N>
-constexpr inline size_t array_lengthof(T (&)[N]) {
- return N;
-}
-
-/// Adapt std::less<T> for array_pod_sort.
-template<typename T>
-inline int array_pod_sort_comparator(const void *P1, const void *P2) {
- if (std::less<T>()(*reinterpret_cast<const T*>(P1),
- *reinterpret_cast<const T*>(P2)))
- return -1;
- if (std::less<T>()(*reinterpret_cast<const T*>(P2),
- *reinterpret_cast<const T*>(P1)))
- return 1;
- return 0;
-}
-
-/// get_array_pod_sort_comparator - This is an internal helper function used to
-/// get type deduction of T right.
-template<typename T>
-inline int (*get_array_pod_sort_comparator(const T &))
- (const void*, const void*) {
- return array_pod_sort_comparator<T>;
-}
-
-/// array_pod_sort - This sorts an array with the specified start and end
-/// extent. This is just like std::sort, except that it calls qsort instead of
-/// using an inlined template. qsort is slightly slower than std::sort, but
-/// most sorts are not performance critical in LLVM and std::sort has to be
-/// template instantiated for each type, leading to significant measured code
-/// bloat. This function should generally be used instead of std::sort where
-/// possible.
-///
-/// This function assumes that you have simple POD-like types that can be
-/// compared with std::less and can be moved with memcpy. If this isn't true,
-/// you should use std::sort.
-///
-/// NOTE: If qsort_r were portable, we could allow a custom comparator and
-/// default to std::less.
-template<class IteratorTy>
-inline void array_pod_sort(IteratorTy Start, IteratorTy End) {
- // Don't inefficiently call qsort with one element or trigger undefined
- // behavior with an empty sequence.
- auto NElts = End - Start;
- if (NElts <= 1) return;
- qsort(&*Start, NElts, sizeof(*Start), get_array_pod_sort_comparator(*Start));
-}
-
-template <class IteratorTy>
-inline void array_pod_sort(
- IteratorTy Start, IteratorTy End,
- int (*Compare)(
- const typename std::iterator_traits<IteratorTy>::value_type *,
- const typename std::iterator_traits<IteratorTy>::value_type *)) {
- // Don't inefficiently call qsort with one element or trigger undefined
- // behavior with an empty sequence.
- auto NElts = End - Start;
- if (NElts <= 1) return;
- qsort(&*Start, NElts, sizeof(*Start),
- reinterpret_cast<int (*)(const void *, const void *)>(Compare));
-}
-
-//===----------------------------------------------------------------------===//
-// Extra additions to <algorithm>
-//===----------------------------------------------------------------------===//
-
-/// For a container of pointers, deletes the pointers and then clears the
-/// container.
-template<typename Container>
-void DeleteContainerPointers(Container &C) {
- for (auto V : C)
- delete V;
- C.clear();
-}
-
-/// In a container of pairs (usually a map) whose second element is a pointer,
-/// deletes the second elements and then clears the container.
-template<typename Container>
-void DeleteContainerSeconds(Container &C) {
- for (auto &V : C)
- delete V.second;
- C.clear();
-}
-
-/// Get the size of a range. This is a wrapper function around std::distance
-/// which is only enabled when the operation is O(1).
-template <typename R>
-auto size(R &&Range, typename std::enable_if<
- std::is_same<typename std::iterator_traits<decltype(
- Range.begin())>::iterator_category,
- std::random_access_iterator_tag>::value,
- void>::type * = nullptr)
- -> decltype(std::distance(Range.begin(), Range.end())) {
- return std::distance(Range.begin(), Range.end());
-}
-
-/// Provide wrappers to std::for_each which take ranges instead of having to
-/// pass begin/end explicitly.
-template <typename R, typename UnaryPredicate>
-UnaryPredicate for_each(R &&Range, UnaryPredicate P) {
- return std::for_each(adl_begin(Range), adl_end(Range), P);
-}
-
-/// Provide wrappers to std::all_of which take ranges instead of having to pass
-/// begin/end explicitly.
-template <typename R, typename UnaryPredicate>
-bool all_of(R &&Range, UnaryPredicate P) {
- return std::all_of(adl_begin(Range), adl_end(Range), P);
-}
-
-/// Provide wrappers to std::any_of which take ranges instead of having to pass
-/// begin/end explicitly.
-template <typename R, typename UnaryPredicate>
-bool any_of(R &&Range, UnaryPredicate P) {
- return std::any_of(adl_begin(Range), adl_end(Range), P);
-}
-
-/// Provide wrappers to std::none_of which take ranges instead of having to pass
-/// begin/end explicitly.
-template <typename R, typename UnaryPredicate>
-bool none_of(R &&Range, UnaryPredicate P) {
- return std::none_of(adl_begin(Range), adl_end(Range), P);
-}
-
-/// Provide wrappers to std::find which take ranges instead of having to pass
-/// begin/end explicitly.
-template <typename R, typename T>
-auto find(R &&Range, const T &Val) -> decltype(adl_begin(Range)) {
- return std::find(adl_begin(Range), adl_end(Range), Val);
-}
-
-/// Provide wrappers to std::find_if which take ranges instead of having to pass
-/// begin/end explicitly.
-template <typename R, typename UnaryPredicate>
-auto find_if(R &&Range, UnaryPredicate P) -> decltype(adl_begin(Range)) {
- return std::find_if(adl_begin(Range), adl_end(Range), P);
-}
-
-template <typename R, typename UnaryPredicate>
-auto find_if_not(R &&Range, UnaryPredicate P) -> decltype(adl_begin(Range)) {
- return std::find_if_not(adl_begin(Range), adl_end(Range), P);
-}
-
-/// Provide wrappers to std::remove_if which take ranges instead of having to
-/// pass begin/end explicitly.
-template <typename R, typename UnaryPredicate>
-auto remove_if(R &&Range, UnaryPredicate P) -> decltype(adl_begin(Range)) {
- return std::remove_if(adl_begin(Range), adl_end(Range), P);
-}
-
-/// Provide wrappers to std::copy_if which take ranges instead of having to
-/// pass begin/end explicitly.
-template <typename R, typename OutputIt, typename UnaryPredicate>
-OutputIt copy_if(R &&Range, OutputIt Out, UnaryPredicate P) {
- return std::copy_if(adl_begin(Range), adl_end(Range), Out, P);
-}
-
-template <typename R, typename OutputIt>
-OutputIt copy(R &&Range, OutputIt Out) {
- return std::copy(adl_begin(Range), adl_end(Range), Out);
-}
-
-/// Wrapper function around std::find to detect if an element exists
-/// in a container.
-template <typename R, typename E>
-bool is_contained(R &&Range, const E &Element) {
- return std::find(adl_begin(Range), adl_end(Range), Element) != adl_end(Range);
-}
-
-/// Wrapper function around std::count to count the number of times an element
-/// \p Element occurs in the given range \p Range.
-template <typename R, typename E>
-auto count(R &&Range, const E &Element) ->
- typename std::iterator_traits<decltype(adl_begin(Range))>::difference_type {
- return std::count(adl_begin(Range), adl_end(Range), Element);
-}
-
-/// Wrapper function around std::count_if to count the number of times an
-/// element satisfying a given predicate occurs in a range.
-template <typename R, typename UnaryPredicate>
-auto count_if(R &&Range, UnaryPredicate P) ->
- typename std::iterator_traits<decltype(adl_begin(Range))>::difference_type {
- return std::count_if(adl_begin(Range), adl_end(Range), P);
-}
-
-/// Wrapper function around std::transform to apply a function to a range and
-/// store the result elsewhere.
-template <typename R, typename OutputIt, typename UnaryPredicate>
-OutputIt transform(R &&Range, OutputIt d_first, UnaryPredicate P) {
- return std::transform(adl_begin(Range), adl_end(Range), d_first, P);
-}
-
-/// Provide wrappers to std::partition which take ranges instead of having to
-/// pass begin/end explicitly.
-template <typename R, typename UnaryPredicate>
-auto partition(R &&Range, UnaryPredicate P) -> decltype(adl_begin(Range)) {
- return std::partition(adl_begin(Range), adl_end(Range), P);
-}
-
-/// Provide wrappers to std::lower_bound which take ranges instead of having to
-/// pass begin/end explicitly.
-template <typename R, typename ForwardIt>
-auto lower_bound(R &&Range, ForwardIt I) -> decltype(adl_begin(Range)) {
- return std::lower_bound(adl_begin(Range), adl_end(Range), I);
-}
-
-template <typename R, typename ForwardIt, typename Compare>
-auto lower_bound(R &&Range, ForwardIt I, Compare C)
- -> decltype(adl_begin(Range)) {
- return std::lower_bound(adl_begin(Range), adl_end(Range), I, C);
-}
-
-/// Provide wrappers to std::upper_bound which take ranges instead of having to
-/// pass begin/end explicitly.
-template <typename R, typename ForwardIt>
-auto upper_bound(R &&Range, ForwardIt I) -> decltype(adl_begin(Range)) {
- return std::upper_bound(adl_begin(Range), adl_end(Range), I);
-}
-
-template <typename R, typename ForwardIt, typename Compare>
-auto upper_bound(R &&Range, ForwardIt I, Compare C)
- -> decltype(adl_begin(Range)) {
- return std::upper_bound(adl_begin(Range), adl_end(Range), I, C);
-}
-/// Wrapper function around std::equal to detect if all elements
-/// in a container are same.
-template <typename R>
-bool is_splat(R &&Range) {
- size_t range_size = size(Range);
- return range_size != 0 && (range_size == 1 ||
- std::equal(adl_begin(Range) + 1, adl_end(Range), adl_begin(Range)));
-}
-
-/// Given a range of type R, iterate the entire range and return a
-/// SmallVector with elements of the vector. This is useful, for example,
-/// when you want to iterate a range and then sort the results.
-template <unsigned Size, typename R>
-SmallVector<typename std::remove_const<detail::ValueOfRange<R>>::type, Size>
-to_vector(R &&Range) {
- return {adl_begin(Range), adl_end(Range)};
-}
-
-/// Provide a container algorithm similar to C++ Library Fundamentals v2's
-/// `erase_if` which is equivalent to:
-///
-/// C.erase(remove_if(C, pred), C.end());
-///
-/// This version works for any container with an erase method call accepting
-/// two iterators.
-template <typename Container, typename UnaryPredicate>
-void erase_if(Container &C, UnaryPredicate P) {
- C.erase(remove_if(C, P), C.end());
-}
-
-//===----------------------------------------------------------------------===//
-// Extra additions to <memory>
-//===----------------------------------------------------------------------===//
-
-struct FreeDeleter {
- void operator()(void* v) {
- ::free(v);
- }
-};
-
-template<typename First, typename Second>
-struct pair_hash {
- size_t operator()(const std::pair<First, Second> &P) const {
- return std::hash<First>()(P.first) * 31 + std::hash<Second>()(P.second);
- }
-};
-
-/// Binary functor that adapts to any other binary functor after dereferencing
-/// operands.
-template <typename T> struct deref {
- T func;
-
- // Could be further improved to cope with non-derivable functors and
- // non-binary functors (should be a variadic template member function
- // operator()).
- template <typename A, typename B>
- auto operator()(A &lhs, B &rhs) const -> decltype(func(*lhs, *rhs)) {
- assert(lhs);
- assert(rhs);
- return func(*lhs, *rhs);
- }
-};
-
-namespace detail {
-
-template <typename R> class enumerator_iter;
-
-template <typename R> struct result_pair {
- friend class enumerator_iter<R>;
-
- result_pair() = default;
- result_pair(std::size_t Index, IterOfRange<R> Iter)
- : Index(Index), Iter(Iter) {}
-
- result_pair<R> &operator=(const result_pair<R> &Other) {
- Index = Other.Index;
- Iter = Other.Iter;
- return *this;
- }
-
- std::size_t index() const { return Index; }
- const ValueOfRange<R> &value() const { return *Iter; }
- ValueOfRange<R> &value() { return *Iter; }
-
-private:
- std::size_t Index = (std::numeric_limits<std::size_t>::max)();
- IterOfRange<R> Iter;
-};
-
-template <typename R>
-class enumerator_iter
- : public iterator_facade_base<
- enumerator_iter<R>, std::forward_iterator_tag, result_pair<R>,
- typename std::iterator_traits<IterOfRange<R>>::difference_type,
- typename std::iterator_traits<IterOfRange<R>>::pointer,
- typename std::iterator_traits<IterOfRange<R>>::reference> {
- using result_type = result_pair<R>;
-
-public:
- explicit enumerator_iter(IterOfRange<R> EndIter)
- : Result((std::numeric_limits<size_t>::max)(), EndIter) {}
-
- enumerator_iter(std::size_t Index, IterOfRange<R> Iter)
- : Result(Index, Iter) {}
-
- result_type &operator*() { return Result; }
- const result_type &operator*() const { return Result; }
-
- enumerator_iter<R> &operator++() {
- assert(Result.Index != (std::numeric_limits<size_t>::max)());
- ++Result.Iter;
- ++Result.Index;
- return *this;
- }
-
- bool operator==(const enumerator_iter<R> &RHS) const {
- // Don't compare indices here, only iterators. It's possible for an end
- // iterator to have different indices depending on whether it was created
- // by calling std::end() versus incrementing a valid iterator.
- return Result.Iter == RHS.Result.Iter;
- }
-
- enumerator_iter<R> &operator=(const enumerator_iter<R> &Other) {
- Result = Other.Result;
- return *this;
- }
-
-private:
- result_type Result;
-};
-
-template <typename R> class enumerator {
-public:
- explicit enumerator(R &&Range) : TheRange(std::forward<R>(Range)) {}
-
- enumerator_iter<R> begin() {
- return enumerator_iter<R>(0, std::begin(TheRange));
- }
-
- enumerator_iter<R> end() {
- return enumerator_iter<R>(std::end(TheRange));
- }
-
-private:
- R TheRange;
-};
-
-} // end namespace detail
-
-/// Given an input range, returns a new range whose values are are pair (A,B)
-/// such that A is the 0-based index of the item in the sequence, and B is
-/// the value from the original sequence. Example:
-///
-/// std::vector<char> Items = {'A', 'B', 'C', 'D'};
-/// for (auto X : enumerate(Items)) {
-/// printf("Item %d - %c\n", X.index(), X.value());
-/// }
-///
-/// Output:
-/// Item 0 - A
-/// Item 1 - B
-/// Item 2 - C
-/// Item 3 - D
-///
-template <typename R> detail::enumerator<R> enumerate(R &&TheRange) {
- return detail::enumerator<R>(std::forward<R>(TheRange));
-}
-
-/// Return true if the sequence [Begin, End) has exactly N items. Runs in O(N)
-/// time. Not meant for use with random-access iterators.
-template <typename IterTy>
-bool hasNItems(
- IterTy &&Begin, IterTy &&End, unsigned N,
- typename std::enable_if<
- !std::is_same<
- typename std::iterator_traits<typename std::remove_reference<
- decltype(Begin)>::type>::iterator_category,
- std::random_access_iterator_tag>::value,
- void>::type * = nullptr) {
- for (; N; --N, ++Begin)
- if (Begin == End)
- return false; // Too few.
- return Begin == End;
-}
-
-/// Return true if the sequence [Begin, End) has N or more items. Runs in O(N)
-/// time. Not meant for use with random-access iterators.
-template <typename IterTy>
-bool hasNItemsOrMore(
- IterTy &&Begin, IterTy &&End, unsigned N,
- typename std::enable_if<
- !std::is_same<
- typename std::iterator_traits<typename std::remove_reference<
- decltype(Begin)>::type>::iterator_category,
- std::random_access_iterator_tag>::value,
- void>::type * = nullptr) {
- for (; N; --N, ++Begin)
- if (Begin == End)
- return false; // Too few.
- return true;
-}
-
-} // end namespace wpi
-
-#endif // LLVM_ADT_STLEXTRAS_H
diff --git a/wpiutil/src/main/native/include/wpi/SafeThread.h b/wpiutil/src/main/native/include/wpi/SafeThread.h
index d4b48f7..bf37732 100644
--- a/wpiutil/src/main/native/include/wpi/SafeThread.h
+++ b/wpiutil/src/main/native/include/wpi/SafeThread.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2015-2019 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_SAFETHREAD_H_
#define WPIUTIL_WPI_SAFETHREAD_H_
@@ -18,7 +15,9 @@
namespace wpi {
-// Base class for SafeThreadOwner threads.
+/**
+ * Base class for SafeThreadOwner threads.
+ */
class SafeThread {
public:
virtual ~SafeThread() = default;
@@ -32,7 +31,9 @@
namespace detail {
-// Non-template proxy base class for common proxy code.
+/**
+ * Non-template proxy base class for common proxy code.
+ */
class SafeThreadProxyBase {
public:
explicit SafeThreadProxyBase(std::shared_ptr<SafeThread> thr);
@@ -44,8 +45,11 @@
std::unique_lock<wpi::mutex> m_lock;
};
-// A proxy for SafeThread.
-// Also serves as a scoped lock on SafeThread::m_mutex.
+/**
+ * A proxy for SafeThread.
+ *
+ * Also serves as a scoped lock on SafeThread::m_mutex.
+ */
template <typename T>
class SafeThreadProxy : public SafeThreadProxyBase {
public:
@@ -55,7 +59,9 @@
T* operator->() const { return static_cast<T*>(m_thread.get()); }
};
-// Non-template owner base class for common owner code.
+/**
+ * Non-template owner base class for common owner code.
+ */
class SafeThreadOwnerBase {
public:
void Stop();
diff --git a/wpiutil/src/main/native/include/wpi/Signal.h b/wpiutil/src/main/native/include/wpi/Signal.h
index 64b774c..d4993d9 100644
--- a/wpiutil/src/main/native/include/wpi/Signal.h
+++ b/wpiutil/src/main/native/include/wpi/Signal.h
@@ -565,7 +565,7 @@
* signal object, it does not cover thread safety of potentially
* shared state used in slot functions.
*
- * @param a... arguments to emit
+ * @param a arguments to emit
*/
template <typename... A>
void operator()(A && ... a) const {
@@ -581,7 +581,6 @@
* Safety: Thread-safety depends on locking policy.
*
* @param c a callable
- * @return a Connection object that can be used to interact with the slot
*/
template <typename Callable>
void connect(Callable && c) {
diff --git a/wpiutil/src/main/native/include/wpi/SmallString.h b/wpiutil/src/main/native/include/wpi/SmallString.h
index 32969b1..1d44f24 100644
--- a/wpiutil/src/main/native/include/wpi/SmallString.h
+++ b/wpiutil/src/main/native/include/wpi/SmallString.h
@@ -15,8 +15,8 @@
#define WPIUTIL_WPI_SMALLSTRING_H
#include "wpi/SmallVector.h"
-#include "wpi/StringRef.h"
#include <cstddef>
+#include <string_view>
namespace wpi {
@@ -28,8 +28,9 @@
/// 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 with a range.
template<typename ItTy>
@@ -54,8 +55,8 @@
SmallVectorImpl<char>::append(S, E);
}
- /// Assign from a StringRef.
- void assign(StringRef RHS) {
+ /// Assign from a std::string_view.
+ void assign(std::string_view RHS) {
this->clear();
SmallVectorImpl<char>::append(RHS.begin(), RHS.end());
}
@@ -80,8 +81,8 @@
SmallVectorImpl<char>::append(NumInputs, Elt);
}
- /// 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());
}
@@ -94,48 +95,12 @@
/// @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(StringRef RHS) const {
- return str().equals(RHS);
- }
-
- /// Check for string equality, ignoring case.
- bool equals_lower(StringRef RHS) const {
- return str().equals_lower(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_lower - Compare two strings, ignoring case.
- int compare_lower(StringRef RHS) const {
- return str().compare_lower(RHS);
- }
-
- /// compare_numeric - Compare two strings, treating sequences of digits as
- /// numbers.
- int compare_numeric(StringRef RHS) const {
- return str().compare_numeric(RHS);
- }
-
- /// @}
- /// @name String Predicates
- /// @{
-
- /// startswith - Check if this string starts with the given \p Prefix.
- bool startswith(StringRef Prefix) const {
- return str().startswith(Prefix);
- }
-
- /// endswith - Check if this string ends with the given \p Suffix.
- bool endswith(StringRef Suffix) const {
- return str().endswith(Suffix);
- }
-
/// @}
/// @name String Searching
/// @{
@@ -152,7 +117,7 @@
///
/// \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);
}
@@ -160,7 +125,7 @@
///
/// \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);
}
@@ -168,7 +133,7 @@
///
/// \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);
}
@@ -182,7 +147,7 @@
/// 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);
}
@@ -196,13 +161,13 @@
/// \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);
}
@@ -211,26 +176,11 @@
///
/// 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);
}
/// @}
- /// @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(StringRef Str) const {
- return str().count(Str);
- }
-
- /// @}
/// @name Substring Operations
/// @{
@@ -243,28 +193,17 @@
/// \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);
}
- /// 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.
- StringRef 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->begin(), this->size()); }
+ /// Explicit conversion to std::string_view.
+ std::string_view str() const { return {this->begin(), this->size()}; }
+
+ /// Explicit conversion to std::string.
+ std::string string() const { return {this->begin(), this->size()}; }
// TODO: Make this const, if it's safe...
const char* c_str() {
@@ -273,16 +212,19 @@
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(); }
+
+ /// Implicit conversion to std::string.
+ operator std::string() const { return string(); }
// Extra operators.
- const SmallString &operator=(StringRef RHS) {
+ const SmallString &operator=(std::string_view RHS) {
this->clear();
return *this += RHS;
}
- SmallString &operator+=(StringRef RHS) {
+ SmallString &operator+=(std::string_view RHS) {
this->append(RHS.begin(), RHS.end());
return *this;
}
diff --git a/wpiutil/src/main/native/include/wpi/SocketError.h b/wpiutil/src/main/native/include/wpi/SocketError.h
index d5f3ff0..b893ac6 100644
--- a/wpiutil/src/main/native/include/wpi/SocketError.h
+++ b/wpiutil/src/main/native/include/wpi/SocketError.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2015-2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_SOCKETERROR_H_
#define WPIUTIL_WPI_SOCKETERROR_H_
@@ -16,7 +13,7 @@
std::string SocketStrerror(int code);
-static inline std::string SocketStrerror() {
+inline std::string SocketStrerror() {
return SocketStrerror(SocketErrno());
}
diff --git a/wpiutil/src/main/native/include/wpi/SpanExtras.h b/wpiutil/src/main/native/include/wpi/SpanExtras.h
new file mode 100644
index 0000000..41e4014
--- /dev/null
+++ b/wpiutil/src/main/native/include/wpi/SpanExtras.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 <cassert>
+
+#include "wpi/span.h"
+
+namespace wpi {
+
+/// Drop the first \p N elements of the array.
+template <typename T>
+constexpr span<T> drop_front(span<T> in, typename 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 span<T> drop_back(span<T> in, typename span<T>::size_type n = 1) {
+ assert(in.size() >= n && "Dropping more elements than exist");
+ return in.subspan(0, in.size() - n);
+}
+
+} // namespace wpi
diff --git a/wpiutil/src/main/native/include/wpi/StackTrace.h b/wpiutil/src/main/native/include/wpi/StackTrace.h
index 56a3770..f9ee989 100644
--- a/wpiutil/src/main/native/include/wpi/StackTrace.h
+++ b/wpiutil/src/main/native/include/wpi/StackTrace.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-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. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_STACKTRACE_H_
#define WPIUTIL_WPI_STACKTRACE_H_
diff --git a/wpiutil/src/main/native/include/wpi/StringExtras.h b/wpiutil/src/main/native/include/wpi/StringExtras.h
index f420e2a..74874ff 100644
--- a/wpiutil/src/main/native/include/wpi/StringExtras.h
+++ b/wpiutil/src/main/native/include/wpi/StringExtras.h
@@ -1,3 +1,7 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
//===- llvm/ADT/StringExtras.h - Useful string functions --------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
@@ -11,395 +15,651 @@
//
//===----------------------------------------------------------------------===//
-#ifndef WPIUTIL_WPI_STRINGEXTRAS_H
-#define WPIUTIL_WPI_STRINGEXTRAS_H
+#pragma once
-#include "wpi/ArrayRef.h"
-#include "wpi/SmallString.h"
-#include "wpi/StringRef.h"
-#include "wpi/Twine.h"
-#include <cassert>
-#include <cstddef>
-#include <cstdint>
-#include <cstdlib>
-#include <cstring>
-#include <iterator>
+#include <limits>
+#include <optional>
#include <string>
+#include <string_view>
+#include <type_traits>
#include <utility>
namespace wpi {
-template<typename T> class SmallVectorImpl;
-class raw_ostream;
+template <typename T>
+class SmallVectorImpl;
/// hexdigit - Return the hexadecimal character for the
/// given number \p X (which should be less than 16).
-inline char hexdigit(unsigned X, bool LowerCase = false) {
+constexpr char hexdigit(unsigned X, bool LowerCase = false) noexcept {
const char HexChar = LowerCase ? 'a' : 'A';
return X < 10 ? '0' + X : HexChar + X - 10;
}
-/// Construct a string ref from a boolean.
-inline StringRef toStringRef(bool B) { return StringRef(B ? "true" : "false"); }
-
-/// Construct a string ref from an array ref of unsigned chars.
-inline StringRef toStringRef(ArrayRef<uint8_t> Input) {
- return StringRef(reinterpret_cast<const char *>(Input.begin()), Input.size());
-}
-
-/// Construct a string ref from an array ref of unsigned chars.
-inline ArrayRef<uint8_t> arrayRefFromStringRef(StringRef Input) {
- return {Input.bytes_begin(), Input.bytes_end()};
-}
-
/// Interpret the given character \p C as a hexadecimal digit and return its
/// value.
///
/// If \p C is not a valid hex digit, -1U is returned.
-inline unsigned hexDigitValue(char C) {
- if (C >= '0' && C <= '9') return C-'0';
- if (C >= 'a' && C <= 'f') return C-'a'+10U;
- if (C >= 'A' && C <= 'F') return C-'A'+10U;
+constexpr unsigned hexDigitValue(char C) noexcept {
+ if (C >= '0' && C <= '9') {
+ return C - '0';
+ }
+ if (C >= 'a' && C <= 'f') {
+ return C - 'a' + 10U;
+ }
+ if (C >= 'A' && C <= 'F') {
+ return C - 'A' + 10U;
+ }
return (std::numeric_limits<unsigned>::max)();
}
/// Checks if character \p C is one of the 10 decimal digits.
-inline bool isDigit(char C) { return C >= '0' && C <= '9'; }
+constexpr bool isDigit(char C) noexcept {
+ return C >= '0' && C <= '9';
+}
/// Checks if character \p C is a hexadecimal numeric character.
-inline bool isHexDigit(char C) { return hexDigitValue(C) != (std::numeric_limits<unsigned>::max)(); }
+constexpr bool isHexDigit(char C) noexcept {
+ return hexDigitValue(C) != (std::numeric_limits<unsigned>::max)();
+}
/// Checks if character \p C is a valid letter as classified by "C" locale.
-inline bool isAlpha(char C) {
+constexpr bool isAlpha(char C) noexcept {
return ('a' <= C && C <= 'z') || ('A' <= C && C <= 'Z');
}
/// Checks whether character \p C is either a decimal digit or an uppercase or
/// lowercase letter as classified by "C" locale.
-inline bool isAlnum(char C) { return isAlpha(C) || isDigit(C); }
+constexpr bool isAlnum(char C) noexcept {
+ return isAlpha(C) || isDigit(C);
+}
/// Checks whether character \p C is valid ASCII (high bit is zero).
-inline bool isASCII(char C) { return static_cast<unsigned char>(C) <= 127; }
-
-/// Checks whether all characters in S are ASCII.
-inline bool isASCII(wpi::StringRef S) {
- for (char C : S)
- if (LLVM_UNLIKELY(!isASCII(C)))
- return false;
- return true;
+constexpr bool isASCII(char C) noexcept {
+ return static_cast<unsigned char>(C) <= 127;
}
/// Checks whether character \p C is printable.
///
/// Locale-independent version of the C standard library isprint whose results
/// may differ on different platforms.
-inline bool isPrint(char C) {
+constexpr bool isPrint(char C) noexcept {
unsigned char UC = static_cast<unsigned char>(C);
return (0x20 <= UC) && (UC <= 0x7E);
}
/// Returns the corresponding lowercase character if \p x is uppercase.
-inline char toLower(char x) {
- if (x >= 'A' && x <= 'Z')
+constexpr char toLower(char x) noexcept {
+ if (x >= 'A' && x <= 'Z') {
return x - 'A' + 'a';
+ }
return x;
}
/// Returns the corresponding uppercase character if \p x is lowercase.
-inline char toUpper(char x) {
- if (x >= 'a' && x <= 'z')
+constexpr char toUpper(char x) noexcept {
+ if (x >= 'a' && x <= 'z') {
return x - 'a' + 'A';
+ }
return x;
}
-inline std::string utohexstr(uint64_t X, bool LowerCase = false) {
- char Buffer[17];
- char *BufPtr = std::end(Buffer);
+inline std::string utohexstr(unsigned long long val, // NOLINT(runtime/int)
+ bool lowerCase = false) {
+ char buf[17];
+ char* bufptr = std::end(buf);
- if (X == 0) *--BufPtr = '0';
-
- while (X) {
- unsigned char Mod = static_cast<unsigned char>(X) & 15;
- *--BufPtr = hexdigit(Mod, LowerCase);
- X >>= 4;
+ if (val == 0) {
+ *--bufptr = '0';
}
- return std::string(BufPtr, std::end(Buffer));
-}
-
-/// Convert buffer \p Input to its hexadecimal representation.
-/// The returned string is double the size of \p Input.
-inline std::string toHex(StringRef Input, bool LowerCase = false) {
- static const char *const LUT = "0123456789ABCDEF";
- const uint8_t Offset = LowerCase ? 32 : 0;
- size_t Length = Input.size();
-
- std::string Output;
- Output.reserve(2 * Length);
- for (size_t i = 0; i < Length; ++i) {
- const unsigned char c = Input[i];
- Output.push_back(LUT[c >> 4] | Offset);
- Output.push_back(LUT[c & 15] | Offset);
- }
- return Output;
-}
-
-inline std::string toHex(ArrayRef<uint8_t> Input, bool LowerCase = false) {
- return toHex(toStringRef(Input), LowerCase);
-}
-
-inline uint8_t hexFromNibbles(char MSB, char LSB) {
- unsigned U1 = hexDigitValue(MSB);
- unsigned U2 = hexDigitValue(LSB);
- assert(U1 != (std::numeric_limits<unsigned>::max)() && U2 != (std::numeric_limits<unsigned>::max)());
-
- return static_cast<uint8_t>((U1 << 4) | U2);
-}
-
-/// Convert hexadecimal string \p Input to its binary representation.
-/// The return string is half the size of \p Input.
-inline std::string fromHex(StringRef Input) {
- if (Input.empty())
- return std::string();
-
- std::string Output;
- Output.reserve((Input.size() + 1) / 2);
- if (Input.size() % 2 == 1) {
- Output.push_back(hexFromNibbles('0', Input.front()));
- Input = Input.drop_front();
+ while (val) {
+ unsigned char mod = static_cast<unsigned char>(val) & 15;
+ *--bufptr = hexdigit(mod, lowerCase);
+ val >>= 4;
}
- assert(Input.size() % 2 == 0);
- while (!Input.empty()) {
- uint8_t Hex = hexFromNibbles(Input[0], Input[1]);
- Output.push_back(Hex);
- Input = Input.drop_front(2);
- }
- return Output;
+ return std::string(bufptr, std::end(buf));
}
-/// Convert the string \p S to an integer of the specified type using
-/// the radix \p Base. If \p Base is 0, auto-detects the radix.
-/// Returns true if the number was successfully converted, false otherwise.
-template <typename N> bool to_integer(StringRef S, N &Num, unsigned Base = 0) {
- return !S.getAsInteger(Base, Num);
+/**
+ * equals - Check for string equality, this is more efficient than
+ * compare() when the relative ordering of inequal strings isn't needed.
+ */
+constexpr bool equals(std::string_view lhs, std::string_view rhs) noexcept {
+ auto length = lhs.size();
+ return length == rhs.size() && std::string_view::traits_type::compare(
+ lhs.data(), rhs.data(), length) == 0;
+}
+
+/**
+ * compare_lower - Compare two strings, ignoring case.
+ */
+int compare_lower(std::string_view lhs, std::string_view rhs) noexcept;
+
+/**
+ * equals_lower - Check for string equality, ignoring case.
+ */
+constexpr bool equals_lower(std::string_view lhs,
+ std::string_view rhs) noexcept {
+ return lhs.size() == rhs.size() && compare_lower(lhs, rhs) == 0;
+}
+
+/**
+ * Search for the first character @p ch in @p str, ignoring case.
+ *
+ * @returns The index of the first occurrence of @p ch, or npos if not
+ * found.
+ */
+std::string_view::size_type find_lower(
+ std::string_view str, char ch,
+ std::string_view::size_type from = 0) noexcept;
+
+/**
+ * Search for the first string @p other in @p str, ignoring case.
+ *
+ * @returns The index of the first occurrence of @p other, or npos if not
+ * found.
+ */
+std::string_view::size_type find_lower(
+ std::string_view str, std::string_view other,
+ std::string_view::size_type from = 0) noexcept;
+
+/**
+ * Search for the first string @p other in @p str, ignoring case.
+ *
+ * @returns The index of the first occurrence of @p other, or npos if not
+ * found.
+ */
+inline std::string_view::size_type find_lower(
+ std::string_view str, const char* other,
+ std::string_view::size_type from = 0) noexcept {
+ return find_lower(str, std::string_view{other}, from);
+}
+
+/**
+ * Search for the last character @p ch in @p str, ignoring case.
+ *
+ * @returns The index of the last occurrence of @p ch, or npos if not
+ * found.
+ */
+std::string_view::size_type rfind_lower(
+ std::string_view str, char ch,
+ std::string_view::size_type from = std::string_view::npos) noexcept;
+
+/**
+ * Search for the last string @p other in @p str, ignoring case.
+ *
+ * @returns The index of the last occurrence of @p other, or npos if not
+ * found.
+ */
+std::string_view::size_type rfind_lower(std::string_view str,
+ std::string_view other) noexcept;
+
+/**
+ * Search for the last string @p other in @p str, ignoring case.
+ *
+ * @returns The index of the last occurrence of @p other, or npos if not
+ * found.
+ */
+inline std::string_view::size_type rfind_lower(std::string_view str,
+ const char* other) noexcept {
+ return rfind_lower(str, std::string_view{other});
+}
+
+/**
+ * Checks if @p str starts with the given @p prefix.
+ */
+constexpr bool starts_with(std::string_view str,
+ std::string_view prefix) noexcept {
+ return str.substr(0, prefix.size()) == prefix;
+}
+
+/**
+ * Checks if @p str starts with the given @p prefix.
+ */
+constexpr bool starts_with(std::string_view str, char prefix) noexcept {
+ return !str.empty() && std::string_view::traits_type::eq(str.front(), prefix);
+}
+
+/**
+ * Checks if @p str starts with the given @p prefix.
+ */
+constexpr bool starts_with(std::string_view str, const char* prefix) noexcept {
+ return starts_with(str, std::string_view(prefix));
+}
+
+/**
+ * Checks if @p str starts with the given @p prefix, ignoring case.
+ */
+bool starts_with_lower(std::string_view str, std::string_view prefix) noexcept;
+
+/**
+ * Checks if @p str starts with the given @p prefix, ignoring case.
+ */
+constexpr bool starts_with_lower(std::string_view str, char prefix) noexcept {
+ return !str.empty() && toLower(str.front()) == toLower(prefix);
+}
+
+/**
+ * Checks if @p str starts with the given @p prefix, ignoring case.
+ */
+inline bool starts_with_lower(std::string_view str,
+ const char* prefix) noexcept {
+ return starts_with_lower(str, std::string_view(prefix));
+}
+
+/**
+ * Checks if @p str ends with the given @p suffix.
+ */
+constexpr bool ends_with(std::string_view str,
+ std::string_view suffix) noexcept {
+ return str.size() >= suffix.size() &&
+ str.compare(str.size() - suffix.size(), std::string_view::npos,
+ suffix) == 0;
+}
+
+/**
+ * Checks if @p str ends with the given @p suffix.
+ */
+constexpr bool ends_with(std::string_view str, char suffix) noexcept {
+ return !str.empty() && std::string_view::traits_type::eq(str.back(), suffix);
+}
+
+/**
+ * Checks if @p str ends with the given @p suffix.
+ */
+constexpr bool ends_with(std::string_view str, const char* suffix) noexcept {
+ return ends_with(str, std::string_view(suffix));
+}
+
+/**
+ * Checks if @p str ends with the given @p suffix, ignoring case.
+ */
+bool ends_with_lower(std::string_view str, std::string_view suffix) noexcept;
+
+/**
+ * Checks if @p str ends with the given @p suffix, ignoring case.
+ */
+constexpr bool ends_with_lower(std::string_view str, char suffix) noexcept {
+ return !str.empty() && toLower(str.back()) == toLower(suffix);
+}
+
+/**
+ * Checks if @p str ends with the given @p suffix, ignoring case.
+ */
+inline bool ends_with_lower(std::string_view str, const char* suffix) noexcept {
+ return ends_with_lower(str, std::string_view(suffix));
+}
+
+/**
+ * Checks if @p str contains the substring @p other.
+ */
+constexpr bool contains(std::string_view str, std::string_view other) noexcept {
+ return str.find(other) != std::string_view::npos;
+}
+
+/**
+ * Checks if @p str contains the substring @p other.
+ */
+constexpr bool contains(std::string_view str, char ch) noexcept {
+ return str.find(ch) != std::string_view::npos;
+}
+
+/**
+ * Checks if @p str contains the substring @p other.
+ */
+constexpr bool contains(std::string_view str, const char* other) noexcept {
+ return str.find(other) != std::string_view::npos;
+}
+
+/**
+ * Checks if @p str contains the substring @p other, ignoring case.
+ */
+inline bool contains_lower(std::string_view str,
+ std::string_view other) noexcept {
+ return find_lower(str, other) != std::string_view::npos;
+}
+
+/**
+ * Checks if @p str contains the substring @p other, ignoring case.
+ */
+inline bool contains_lower(std::string_view str, char ch) noexcept {
+ return find_lower(str, ch) != std::string_view::npos;
+}
+
+/**
+ * Checks if @p str contains the substring @p other, ignoring case.
+ */
+inline bool contains_lower(std::string_view str, const char* other) noexcept {
+ return find_lower(str, other) != std::string_view::npos;
+}
+
+/**
+ * Return a string_view equal to @p str but with the first @p n elements
+ * dropped.
+ */
+constexpr std::string_view drop_front(
+ std::string_view str, std::string_view::size_type n = 1) noexcept {
+ str.remove_prefix(n);
+ return str;
+}
+
+/**
+ * Return a string_view equal to @p str but with the last @p n elements dropped.
+ */
+constexpr std::string_view drop_back(
+ std::string_view str, std::string_view::size_type n = 1) noexcept {
+ str.remove_suffix(n);
+ return str;
+}
+
+/**
+ * Returns a reference to the substring of @p str 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 exceeds the number of characters
+ * remaining in the string, the string suffix (starting with @p start)
+ * will be returned. If this is less than @p start, an empty string will
+ * be returned.
+ */
+constexpr std::string_view slice(std::string_view str,
+ std::string_view::size_type start,
+ std::string_view::size_type end) noexcept {
+ auto length = str.size();
+ start = (std::min)(start, length);
+ end = (std::min)((std::max)(start, end), length);
+ return {str.data() + start, end - start};
+}
+
+/**
+ * Splits @p str into two substrings around the first occurrence of a separator
+ * character.
+ *
+ * If @p separator is in the string, then the result is a pair (LHS, RHS)
+ * such that (str == LHS + separator + RHS) is true and RHS is
+ * maximal. If @p separator is not in the string, then the result is a
+ * pair (LHS, RHS) where (str == LHS) and (RHS == "").
+ *
+ * @param separator The character to split on.
+ * @returns The split substrings.
+ */
+constexpr std::pair<std::string_view, std::string_view> split(
+ std::string_view str, char separator) noexcept {
+ auto idx = str.find(separator);
+ if (idx == std::string_view::npos) {
+ return {str, {}};
+ }
+ return {slice(str, 0, idx), slice(str, idx + 1, std::string_view::npos)};
+}
+
+/**
+ * Splits @p str into two substrings around the first occurrence of a separator
+ * string.
+ *
+ * If @p separator is in the string, then the result is a pair (LHS, RHS)
+ * such that (str == LHS + separator + RHS) is true and RHS is
+ * maximal. If @p separator is not in the string, then the result is a
+ * pair (LHS, RHS) where (str == LHS) and (RHS == "").
+ *
+ * @param separator The string to split on.
+ * @return The split substrings.
+ */
+constexpr std::pair<std::string_view, std::string_view> split(
+ std::string_view str, std::string_view separator) noexcept {
+ auto idx = str.find(separator);
+ if (idx == std::string_view::npos) {
+ return {str, {}};
+ }
+ return {slice(str, 0, idx),
+ slice(str, idx + separator.size(), std::string_view::npos)};
+}
+
+/**
+ * Splits @p str into two substrings around the last occurrence of a separator
+ * character.
+ *
+ * If @p separator is in the string, then the result is a pair (LHS, RHS)
+ * such that (str == LHS + separator + RHS) is true and RHS is
+ * minimal. If @p separator is not in the string, then the result is a
+ * pair (LHS, RHS) where (str == LHS) and (RHS == "").
+ *
+ * @param separator The string to split on.
+ * @return The split substrings.
+ */
+constexpr std::pair<std::string_view, std::string_view> rsplit(
+ std::string_view str, char separator) noexcept {
+ auto idx = str.rfind(separator);
+ if (idx == std::string_view::npos) {
+ return {str, {}};
+ }
+ return {slice(str, 0, idx), slice(str, idx + 1, std::string_view::npos)};
+}
+
+/**
+ * Splits @p str into two substrings around the last occurrence of a separator
+ * string.
+ *
+ * If @p separator is in the string, then the result is a pair (LHS, RHS)
+ * such that (str == LHS + separator + RHS) is true and RHS is
+ * minimal. If @p separator is not in the string, then the result is a
+ * pair (LHS, RHS) where (str == LHS) and (RHS == "").
+ *
+ * @param separator The string to split on.
+ * @return The split substrings.
+ */
+constexpr std::pair<std::string_view, std::string_view> rsplit(
+ std::string_view str, std::string_view separator) noexcept {
+ auto idx = str.rfind(separator);
+ if (idx == std::string_view::npos) {
+ return {str, {}};
+ }
+ return {slice(str, 0, idx),
+ slice(str, idx + separator.size(), std::string_view::npos)};
+}
+
+/**
+ * Splits @p str into substrings around the occurrences of a separator string.
+ *
+ * Each substring is stored in @p arr. If @p maxSplit is >= 0, at most
+ * @p maxSplit splits are done and consequently <= @p maxSplit + 1
+ * elements are added to arr.
+ * If @p keepEmpty is false, empty strings are not added to @p arr. They
+ * still count when considering @p maxSplit
+ * An useful invariant is that
+ * separator.join(arr) == str if maxSplit == -1 and keepEmpty == true
+ *
+ * @param arr Where to put the substrings.
+ * @param separator The string to split on.
+ * @param maxSplit The maximum number of times the string is split.
+ * @param keepEmpty True if empty substring should be added.
+ */
+void split(std::string_view str, SmallVectorImpl<std::string_view>& arr,
+ std::string_view separator, int maxSplit = -1,
+ bool keepEmpty = true) noexcept;
+
+/**
+ * Splits @p str into substrings around the occurrences of a separator
+ * character.
+ *
+ * Each substring is stored in @p arr. If @p maxSplit is >= 0, at most
+ * @p maxSplit splits are done and consequently <= @p maxSplit + 1
+ * elements are added to arr.
+ * If @p keepEmpty is false, empty strings are not added to @p arr. They
+ * still count when considering @p maxSplit
+ * An useful invariant is that
+ * separator.join(arr) == str if maxSplit == -1 and keepEmpty == true
+ *
+ * @param arr Where to put the substrings.
+ * @param separator The character to split on.
+ * @param maxSplit The maximum number of times the string is split.
+ * @param keepEmpty True if empty substring should be added.
+ */
+void split(std::string_view str, SmallVectorImpl<std::string_view>& arr,
+ char separator, int maxSplit = -1, bool keepEmpty = true) noexcept;
+
+/**
+ * Returns @p str with consecutive @p ch characters starting from the
+ * the left removed.
+ */
+constexpr std::string_view ltrim(std::string_view str, char ch) noexcept {
+ return drop_front(str, (std::min)(str.size(), str.find_first_not_of(ch)));
+}
+
+/**
+ * Returns @p str with consecutive characters in @p chars starting from
+ * the left removed.
+ */
+constexpr std::string_view ltrim(
+ std::string_view str, std::string_view chars = " \t\n\v\f\r") noexcept {
+ return drop_front(str, (std::min)(str.size(), str.find_first_not_of(chars)));
+}
+
+/**
+ * Returns @p str with consecutive @p Char characters starting from the
+ * right removed.
+ */
+constexpr std::string_view rtrim(std::string_view str, char ch) noexcept {
+ return drop_back(
+ str, str.size() - (std::min)(str.size(), str.find_last_not_of(ch) + 1));
+}
+
+/**
+ * Returns @p str with consecutive characters in @p chars starting from
+ * the right removed.
+ */
+constexpr std::string_view rtrim(
+ std::string_view str, std::string_view chars = " \t\n\v\f\r") noexcept {
+ return drop_back(
+ str,
+ str.size() - (std::min)(str.size(), str.find_last_not_of(chars) + 1));
+}
+
+/**
+ * Returns @p str with consecutive @p ch characters starting from the
+ * left and right removed.
+ */
+constexpr std::string_view trim(std::string_view str, char ch) noexcept {
+ return rtrim(ltrim(str, ch), ch);
+}
+
+/**
+ * Returns @p str with consecutive characters in @p chars starting from
+ * the left and right removed.
+ */
+constexpr std::string_view trim(
+ std::string_view str, std::string_view chars = " \t\n\v\f\r") noexcept {
+ return rtrim(ltrim(str, chars), chars);
}
namespace detail {
-template <typename N>
-inline bool to_float(const Twine &T, N &Num, N (*StrTo)(const char *, char **)) {
- SmallString<32> Storage;
- StringRef S = T.toNullTerminatedStringRef(Storage);
- char *End;
- N Temp = StrTo(S.data(), &End);
- if (*End != '\0')
- return false;
- Num = Temp;
- return true;
-}
-}
+bool GetAsUnsignedInteger(
+ std::string_view str, unsigned radix,
+ unsigned long long& result) noexcept; // NOLINT(runtime/int)
+bool GetAsSignedInteger(std::string_view str, unsigned radix,
+ long long& result) noexcept; // NOLINT(runtime/int)
-inline bool to_float(const Twine &T, float &Num) {
- return detail::to_float(T, Num, strtof);
-}
+bool ConsumeUnsignedInteger(
+ std::string_view& str, unsigned radix,
+ unsigned long long& result) noexcept; // NOLINT(runtime/int)
+bool ConsumeSignedInteger(std::string_view& str, unsigned radix,
+ long long& result) noexcept; // NOLINT(runtime/int)
+} // namespace detail
-inline bool to_float(const Twine &T, double &Num) {
- return detail::to_float(T, Num, strtod);
-}
-
-inline bool to_float(const Twine &T, long double &Num) {
- return detail::to_float(T, Num, strtold);
-}
-
-inline std::string utostr(uint64_t X, bool isNeg = false) {
- char Buffer[21];
- char *BufPtr = std::end(Buffer);
-
- if (X == 0) *--BufPtr = '0'; // Handle special case...
-
- while (X) {
- *--BufPtr = '0' + char(X % 10);
- X /= 10;
+/**
+ * Parses the string @p str as an integer of the specified radix. If
+ * @p radix is specified as zero, this does radix autosensing using
+ * extended C rules: 0 is octal, 0x is hex, 0b is binary.
+ *
+ * If the string is invalid or if only a subset of the string is valid,
+ * this returns nullopt to signify the error. The string is considered
+ * erroneous if empty or if it overflows T.
+ */
+template <typename T,
+ std::enable_if_t<std::numeric_limits<T>::is_signed, bool> = true>
+inline std::optional<T> parse_integer(std::string_view str,
+ unsigned radix) noexcept {
+ long long val; // NOLINT(runtime/int)
+ if (detail::GetAsSignedInteger(str, radix, val) ||
+ static_cast<T>(val) != val) {
+ return std::nullopt;
}
-
- if (isNeg) *--BufPtr = '-'; // Add negative sign...
- return std::string(BufPtr, std::end(Buffer));
+ return val;
}
-inline std::string itostr(int64_t X) {
- if (X < 0)
- return utostr(static_cast<uint64_t>(-X), true);
- else
- return utostr(static_cast<uint64_t>(X));
-}
-
-/// StrInStrNoCase - Portable version of strcasestr. Locates the first
-/// occurrence of string 's1' in string 's2', ignoring case. Returns
-/// the offset of s2 in s1 or npos if s2 cannot be found.
-StringRef::size_type StrInStrNoCase(StringRef s1, StringRef s2);
-
-/// getToken - This function extracts one token from source, ignoring any
-/// leading characters that appear in the Delimiters string, and ending the
-/// token at any of the characters that appear in the Delimiters string. If
-/// there are no tokens in the source string, an empty string is returned.
-/// The function returns a pair containing the extracted token and the
-/// remaining tail string.
-std::pair<StringRef, StringRef> getToken(StringRef Source,
- StringRef Delimiters = " \t\n\v\f\r");
-
-/// SplitString - Split up the specified string according to the specified
-/// delimiters, appending the result fragments to the output list.
-void SplitString(StringRef Source,
- SmallVectorImpl<StringRef> &OutFragments,
- StringRef Delimiters = " \t\n\v\f\r");
-
-/// HashString - Hash function for strings.
-///
-/// This is the Bernstein hash function.
-//
-// FIXME: Investigate whether a modified bernstein hash function performs
-// better: http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx
-// X*33+c -> X*33^c
-static inline unsigned HashString(StringRef Str, unsigned Result = 0) {
- for (StringRef::size_type i = 0, e = Str.size(); i != e; ++i)
- Result = Result * 33 + (unsigned char)Str[i];
- return Result;
-}
-
-/// Returns the English suffix for an ordinal integer (-st, -nd, -rd, -th).
-inline StringRef getOrdinalSuffix(unsigned Val) {
- // It is critically important that we do this perfectly for
- // user-written sequences with over 100 elements.
- switch (Val % 100) {
- case 11:
- case 12:
- case 13:
- return "th";
- default:
- switch (Val % 10) {
- case 1: return "st";
- case 2: return "nd";
- case 3: return "rd";
- default: return "th";
- }
+template <typename T,
+ std::enable_if_t<!std::numeric_limits<T>::is_signed, bool> = true>
+inline std::optional<T> parse_integer(std::string_view str,
+ unsigned radix) noexcept {
+ using Int = unsigned long long; // NOLINT(runtime/int)
+ Int val;
+ // The additional cast to unsigned long long is required to avoid the
+ // Visual C++ warning C4805: '!=' : unsafe mix of type 'bool' and type
+ // 'unsigned __int64' when instantiating getAsInteger with T = bool.
+ if (detail::GetAsUnsignedInteger(str, radix, val) ||
+ static_cast<Int>(static_cast<T>(val)) != val) {
+ return std::nullopt;
}
+ return val;
}
-/// Print each character of the specified string, escaping it if it is not
-/// printable or if it is an escape char.
-void printEscapedString(StringRef Name, raw_ostream &Out);
-
-/// Print each character of the specified string, escaping HTML special
-/// characters.
-void printHTMLEscaped(StringRef String, raw_ostream &Out);
-
-/// printLowerCase - Print each character as lowercase if it is uppercase.
-void printLowerCase(StringRef String, raw_ostream &Out);
-
-namespace detail {
-
-template <typename IteratorT>
-inline std::string join_impl(IteratorT Begin, IteratorT End,
- StringRef Separator, std::input_iterator_tag) {
- std::string S;
- if (Begin == End)
- return S;
-
- S += (*Begin);
- while (++Begin != End) {
- S += Separator;
- S += (*Begin);
+/**
+ * Parses the string @p str as an integer of the specified radix. If
+ * @p radix is specified as zero, this does radix autosensing using
+ * extended C rules: 0 is octal, 0x is hex, 0b is binary.
+ *
+ * If the string does not begin with a number of the specified radix,
+ * this returns nullopt to signify the error. The string is considered
+ * erroneous if empty or if it overflows T.
+ * The portion of the string representing the discovered numeric value
+ * is removed from the beginning of the string.
+ */
+template <typename T,
+ std::enable_if_t<std::numeric_limits<T>::is_signed, bool> = true>
+inline std::optional<T> consume_integer(std::string_view* str,
+ unsigned radix) noexcept {
+ using Int = long long; // NOLINT(runtime/int)
+ Int val;
+ if (detail::ConsumeSignedInteger(*str, radix, val) ||
+ static_cast<Int>(static_cast<T>(val)) != val) {
+ return std::nullopt;
}
- return S;
+ return val;
}
-template <typename IteratorT>
-inline std::string join_impl(IteratorT Begin, IteratorT End,
- StringRef Separator, std::forward_iterator_tag) {
- std::string S;
- if (Begin == End)
- return S;
-
- size_t Len = (std::distance(Begin, End) - 1) * Separator.size();
- for (IteratorT I = Begin; I != End; ++I)
- Len += (*Begin).size();
- S.reserve(Len);
- S += (*Begin);
- while (++Begin != End) {
- S += Separator;
- S += (*Begin);
+template <typename T,
+ std::enable_if_t<!std::numeric_limits<T>::is_signed, bool> = true>
+inline std::optional<T> consume_integer(std::string_view* str,
+ unsigned radix) noexcept {
+ using Int = unsigned long long; // NOLINT(runtime/int)
+ Int val;
+ if (detail::ConsumeUnsignedInteger(*str, radix, val) ||
+ static_cast<Int>(static_cast<T>(val)) != val) {
+ return std::nullopt;
}
- return S;
+ return val;
}
-template <typename Sep>
-inline void join_items_impl(std::string &Result, Sep Separator) {}
+/**
+ * Parses the string @p str as a floating point value.
+ *
+ * If the string is invalid or if only a subset of the string is valid,
+ * this returns nullopt to signify the error. The string is considered
+ * erroneous if empty or if it overflows T.
+ */
+template <typename T>
+std::optional<T> parse_float(std::string_view str) noexcept;
-template <typename Sep, typename Arg>
-inline void join_items_impl(std::string &Result, Sep Separator,
- const Arg &Item) {
- Result += Item;
-}
+template <>
+std::optional<float> parse_float<float>(std::string_view str) noexcept;
+template <>
+std::optional<double> parse_float<double>(std::string_view str) noexcept;
+template <>
+std::optional<long double> parse_float<long double>(
+ std::string_view str) noexcept;
-template <typename Sep, typename Arg1, typename... Args>
-inline void join_items_impl(std::string &Result, Sep Separator, const Arg1 &A1,
- Args &&... Items) {
- Result += A1;
- Result += Separator;
- join_items_impl(Result, Separator, std::forward<Args>(Items)...);
-}
-
-inline size_t join_one_item_size(char C) { return 1; }
-inline size_t join_one_item_size(const char *S) { return S ? ::strlen(S) : 0; }
-
-template <typename T> inline size_t join_one_item_size(const T &Str) {
- return Str.size();
-}
-
-inline size_t join_items_size() { return 0; }
-
-template <typename A1> inline size_t join_items_size(const A1 &A) {
- return join_one_item_size(A);
-}
-template <typename A1, typename... Args>
-inline size_t join_items_size(const A1 &A, Args &&... Items) {
- return join_one_item_size(A) + join_items_size(std::forward<Args>(Items)...);
-}
-
-} // end namespace detail
-
-/// Joins the strings in the range [Begin, End), adding Separator between
-/// the elements.
-template <typename IteratorT>
-inline std::string join(IteratorT Begin, IteratorT End, StringRef Separator) {
- using tag = typename std::iterator_traits<IteratorT>::iterator_category;
- return detail::join_impl(Begin, End, Separator, tag());
-}
-
-/// Joins the strings in the range [R.begin(), R.end()), adding Separator
-/// between the elements.
-template <typename Range>
-inline std::string join(Range &&R, StringRef Separator) {
- return join(R.begin(), R.end(), Separator);
-}
-
-/// Joins the strings in the parameter pack \p Items, adding \p Separator
-/// between the elements. All arguments must be implicitly convertible to
-/// std::string, or there should be an overload of std::string::operator+=()
-/// that accepts the argument explicitly.
-template <typename Sep, typename... Args>
-inline std::string join_items(Sep Separator, Args &&... Items) {
- std::string Result;
- if (sizeof...(Items) == 0)
- return Result;
-
- size_t NS = detail::join_one_item_size(Separator);
- size_t NI = detail::join_items_size(std::forward<Args>(Items)...);
- Result.reserve(NI + (sizeof...(Items) - 1) * NS + 1);
- detail::join_items_impl(Result, Separator, std::forward<Args>(Items)...);
- return Result;
-}
-
-} // end namespace wpi
-
-#endif // WPIUTIL_WPI_STRINGEXTRAS_H
+} // namespace wpi
diff --git a/wpiutil/src/main/native/include/wpi/StringMap.h b/wpiutil/src/main/native/include/wpi/StringMap.h
index dbfe8ed..dac2f20 100644
--- a/wpiutil/src/main/native/include/wpi/StringMap.h
+++ b/wpiutil/src/main/native/include/wpi/StringMap.h
@@ -15,13 +15,11 @@
#define WPIUTIL_WPI_STRINGMAP_H
#include "wpi/SmallVector.h"
-#include "wpi/StringRef.h"
#include "wpi/iterator.h"
#include "wpi/iterator_range.h"
#include "wpi/MemAlloc.h"
#include "wpi/PointerLikeTypeTraits.h"
#include "wpi/ErrorHandling.h"
-#include "wpi/deprecated.h"
#include <algorithm>
#include <cassert>
#include <cstdint>
@@ -29,6 +27,7 @@
#include <cstring>
#include <initializer_list>
#include <iterator>
+#include <string_view>
#include <utility>
namespace wpi {
@@ -82,12 +81,12 @@
/// 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.
@@ -95,7 +94,7 @@
/// 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.
@@ -137,8 +136,8 @@
: StringMapEntryBase(strLen), second(std::forward<InitTy>(InitVals)...) {}
StringMapEntry(StringMapEntry &E) = delete;
- StringRef getKey() const {
- return StringRef(getKeyData(), getKeyLength());
+ std::string_view getKey() const {
+ return {getKeyData(), getKeyLength()};
}
const ValueTy &getValue() const { return second; }
@@ -151,12 +150,12 @@
/// StringMapEntry object.
const char *getKeyData() const {return reinterpret_cast<const char*>(this+1);}
- StringRef first() const { return StringRef(getKeyData(), getKeyLength()); }
+ std::string_view first() const { return {getKeyData(), getKeyLength()}; }
/// Create a StringMapEntry for the specified key construct the value using
/// \p InitiVals.
template <typename... InitTy>
- static StringMapEntry *Create(StringRef Key, InitTy &&... InitVals) {
+ static StringMapEntry *Create(std::string_view Key, InitTy &&... InitVals) {
size_t KeyLength = Key.size();
// Allocate a new item with space for the string at the end and a null
@@ -177,7 +176,7 @@
return NewItem;
}
- static StringMapEntry *Create(StringRef Key) {
+ static StringMapEntry *Create(std::string_view Key) {
return Create(Key, ValueTy());
}
@@ -212,7 +211,7 @@
explicit StringMap(unsigned InitialSize)
: StringMapImpl(InitialSize, static_cast<unsigned>(sizeof(MapEntryTy))) {}
- 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))) {
for (const auto &P : List) {
insert(P);
@@ -302,13 +301,13 @@
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();
return const_iterator(TheTable+Bucket, true);
@@ -316,7 +315,7 @@
/// 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;
@@ -325,10 +324,10 @@
/// 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 {
+ size_type count(std::string_view Key) const {
return find(Key) == end() ? 0 : 1;
}
@@ -355,7 +354,7 @@
/// 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));
}
@@ -364,7 +363,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(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())
@@ -411,7 +410,7 @@
V.Destroy();
}
- bool erase(StringRef Key) {
+ bool erase(std::string_view Key) {
iterator I = find(Key);
if (I == end()) return false;
erase(I);
@@ -440,7 +439,13 @@
return static_cast<DerivedTy &>(*this);
}
+#if __cplusplus < 202002L
bool operator==(const DerivedTy &RHS) const { return Ptr == RHS.Ptr; }
+#else
+ friend bool operator==(const DerivedTy &LHS, const DerivedTy &RHS) {
+ return LHS.Ptr == RHS.Ptr;
+ }
+#endif
DerivedTy &operator++() { // Preincrement
++Ptr;
@@ -524,23 +529,23 @@
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*() {
+ std::string_view &operator*() {
Key = this->wrapped()->getKey();
return Key;
}
private:
- StringRef Key;
+ std::string_view Key;
};
template <typename ValueTy>
@@ -594,13 +599,13 @@
if (&lhs == &rhs) return false;
// copy into vectors and sort by key
- SmallVector<StringRef, 16> lhs_keys;
+ 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<StringRef, 16> rhs_keys;
+ 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());
diff --git a/wpiutil/src/main/native/include/wpi/StringRef.h b/wpiutil/src/main/native/include/wpi/StringRef.h
deleted file mode 100644
index 60fb789..0000000
--- a/wpiutil/src/main/native/include/wpi/StringRef.h
+++ /dev/null
@@ -1,947 +0,0 @@
-//===- StringRef.h - Constant String Reference Wrapper ----------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef WPIUTIL_WPI_STRINGREF_H
-#define WPIUTIL_WPI_STRINGREF_H
-
-#include "wpi/STLExtras.h"
-#include "wpi/iterator_range.h"
-#include "wpi/Compiler.h"
-#include <algorithm>
-#include <cassert>
-#include <cstddef>
-#include <cstring>
-#include <iosfwd>
-#include <limits>
-#include <string>
-#include <type_traits>
-#include <utility>
-
-namespace wpi {
-
- class hash_code;
- template <typename T> class SmallVectorImpl;
- class StringRef;
-
- /// Helper functions for StringRef::getAsInteger.
- bool getAsUnsignedInteger(StringRef Str, unsigned Radix,
- unsigned long long &Result) noexcept;
-
- bool getAsSignedInteger(StringRef Str, unsigned Radix, long long &Result) noexcept;
-
- bool consumeUnsignedInteger(StringRef &Str, unsigned Radix,
- unsigned long long &Result) noexcept;
- bool consumeSignedInteger(StringRef &Str, unsigned Radix, long long &Result) noexcept;
-
- /// StringRef - Represent a constant reference to a string, i.e. a character
- /// array and a length, which need not be null terminated.
- ///
- /// This class does not own the string data, it is expected to be used in
- /// situations where the character data resides in some other buffer, whose
- /// lifetime extends past that of the StringRef. For this reason, it is not in
- /// general safe to store a StringRef.
- class StringRef {
- public:
- static const size_t npos = ~size_t(0);
-
- using iterator = const char *;
- using const_iterator = const char *;
- using size_type = size_t;
-
- private:
- /// The start of the string, in an external buffer.
- const char *Data = nullptr;
-
- /// The length of the string.
- size_t Length = 0;
-
- // Workaround memcmp issue with null pointers (undefined behavior)
- // by providing a specialized version
- LLVM_ATTRIBUTE_ALWAYS_INLINE
- static int compareMemory(const char *Lhs, const char *Rhs, size_t Length) noexcept {
- if (Length == 0) { return 0; }
- return ::memcmp(Lhs,Rhs,Length);
- }
-
- public:
- /// @name Constructors
- /// @{
-
- /// Construct an empty string ref.
- /*implicit*/ StringRef() = default;
-
- /// Disable conversion from nullptr. This prevents things like
- /// if (S == nullptr)
- StringRef(std::nullptr_t) = delete;
-
- /// Construct a string ref from a cstring.
- LLVM_ATTRIBUTE_ALWAYS_INLINE
- /*implicit*/ StringRef(const char *Str)
- : Data(Str), Length(Str ? ::strlen(Str) : 0) {}
-
- /// Construct a string ref from a pointer and length.
- LLVM_ATTRIBUTE_ALWAYS_INLINE
- /*implicit*/ constexpr StringRef(const char *data, size_t length)
- : Data(data), Length(length) {}
-
- /// Construct a string ref from an std::string.
- LLVM_ATTRIBUTE_ALWAYS_INLINE
- /*implicit*/ StringRef(const std::string &Str)
- : Data(Str.data()), Length(Str.length()) {}
-
- static StringRef withNullAsEmpty(const char *data) {
- return StringRef(data ? data : "");
- }
-
- /// @}
- /// @name Iterators
- /// @{
-
- iterator begin() const noexcept { return Data; }
-
- iterator end() const noexcept { return Data + Length; }
-
- const unsigned char *bytes_begin() const noexcept {
- return reinterpret_cast<const unsigned char *>(begin());
- }
- const unsigned char *bytes_end() const noexcept {
- return reinterpret_cast<const unsigned char *>(end());
- }
- iterator_range<const unsigned char *> bytes() const noexcept {
- return make_range(bytes_begin(), bytes_end());
- }
-
- /// @}
- /// @name String Operations
- /// @{
-
- /// data - Get a pointer to the start of the string (which may not be null
- /// terminated).
- LLVM_NODISCARD
- LLVM_ATTRIBUTE_ALWAYS_INLINE
- const char *data() const noexcept { return Data; }
-
- /// empty - Check if the string is empty.
- LLVM_NODISCARD
- LLVM_ATTRIBUTE_ALWAYS_INLINE
- bool empty() const noexcept { return Length == 0; }
-
- /// size - Get the string size.
- LLVM_NODISCARD
- LLVM_ATTRIBUTE_ALWAYS_INLINE
- size_t size() const noexcept { return Length; }
-
- /// front - Get the first character in the string.
- LLVM_NODISCARD
- char front() const noexcept {
- assert(!empty());
- return Data[0];
- }
-
- /// back - Get the last character in the string.
- LLVM_NODISCARD
- char back() const noexcept {
- assert(!empty());
- return Data[Length-1];
- }
-
- // copy - Allocate copy in Allocator and return StringRef to it.
- template <typename Allocator>
- LLVM_NODISCARD StringRef copy(Allocator &A) const {
- // Don't request a length 0 copy from the allocator.
- if (empty())
- return StringRef();
- char *S = A.template Allocate<char>(Length);
- std::copy(begin(), end(), S);
- return StringRef(S, Length);
- }
-
- /// equals - Check for string equality, this is more efficient than
- /// compare() when the relative ordering of inequal strings isn't needed.
- LLVM_NODISCARD
- LLVM_ATTRIBUTE_ALWAYS_INLINE
- bool equals(StringRef RHS) const noexcept {
- return (Length == RHS.Length &&
- compareMemory(Data, RHS.Data, RHS.Length) == 0);
- }
-
- /// equals_lower - Check for string equality, ignoring case.
- LLVM_NODISCARD
- bool equals_lower(StringRef RHS) const noexcept {
- return Length == RHS.Length && compare_lower(RHS) == 0;
- }
-
- /// compare - 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.
- LLVM_NODISCARD
- LLVM_ATTRIBUTE_ALWAYS_INLINE
- int compare(StringRef RHS) const noexcept {
- // Check the prefix for a mismatch.
- if (int Res = compareMemory(Data, RHS.Data, (std::min)(Length, RHS.Length)))
- return Res < 0 ? -1 : 1;
-
- // Otherwise the prefixes match, so we only need to check the lengths.
- if (Length == RHS.Length)
- return 0;
- return Length < RHS.Length ? -1 : 1;
- }
-
- /// compare_lower - Compare two strings, ignoring case.
- LLVM_NODISCARD
- int compare_lower(StringRef RHS) const noexcept;
-
- /// compare_numeric - Compare two strings, treating sequences of digits as
- /// numbers.
- LLVM_NODISCARD
- int compare_numeric(StringRef RHS) const noexcept;
-
- /// str - Get the contents as an std::string.
- LLVM_NODISCARD
- std::string str() const {
- if (!Data) return std::string();
- return std::string(Data, Length);
- }
-
- /// @}
- /// @name Operator Overloads
- /// @{
-
- LLVM_NODISCARD
- char operator[](size_t Index) const noexcept {
- assert(Index < Length && "Invalid index!");
- return Data[Index];
- }
-
- /// Disallow accidental assignment from a temporary std::string.
- ///
- /// The declaration here is extra complicated so that `stringRef = {}`
- /// and `stringRef = "abc"` continue to select the move assignment operator.
- template <typename T>
- typename std::enable_if<std::is_same<T, std::string>::value,
- StringRef>::type &
- operator=(T &&Str) = delete;
-
- /// @}
- /// @name Type Conversions
- /// @{
-
- operator std::string() const {
- return str();
- }
-
- /// @}
- /// @name String Predicates
- /// @{
-
- /// Check if this string starts with the given \p Prefix.
- LLVM_NODISCARD
- LLVM_ATTRIBUTE_ALWAYS_INLINE
- bool startswith(StringRef Prefix) const noexcept {
- return Length >= Prefix.Length &&
- compareMemory(Data, Prefix.Data, Prefix.Length) == 0;
- }
-
- /// Check if this string starts with the given \p Prefix, ignoring case.
- LLVM_NODISCARD
- bool startswith_lower(StringRef Prefix) const noexcept;
-
- /// Check if this string ends with the given \p Suffix.
- LLVM_NODISCARD
- LLVM_ATTRIBUTE_ALWAYS_INLINE
- bool endswith(StringRef Suffix) const noexcept {
- return Length >= Suffix.Length &&
- compareMemory(end() - Suffix.Length, Suffix.Data, Suffix.Length) == 0;
- }
-
- /// Check if this string ends with the given \p Suffix, ignoring case.
- LLVM_NODISCARD
- bool endswith_lower(StringRef Suffix) const noexcept;
-
- /// @}
- /// @name String Searching
- /// @{
-
- /// Search for the first character \p C in the string.
- ///
- /// \returns The index of the first occurrence of \p C, or npos if not
- /// found.
- LLVM_NODISCARD
- LLVM_ATTRIBUTE_ALWAYS_INLINE
- size_t find(char C, size_t From = 0) const noexcept {
- size_t FindBegin = (std::min)(From, Length);
- if (FindBegin < Length) { // Avoid calling memchr with nullptr.
- // Just forward to memchr, which is faster than a hand-rolled loop.
- if (const void *P = ::memchr(Data + FindBegin, C, Length - FindBegin))
- return static_cast<const char *>(P) - Data;
- }
- return npos;
- }
-
- /// Search for the first character \p C in the string, ignoring case.
- ///
- /// \returns The index of the first occurrence of \p C, or npos if not
- /// found.
- LLVM_NODISCARD
- size_t find_lower(char C, size_t From = 0) const noexcept;
-
- /// Search for the first character satisfying the predicate \p F
- ///
- /// \returns The index of the first character satisfying \p F starting from
- /// \p From, or npos if not found.
- LLVM_NODISCARD
- LLVM_ATTRIBUTE_ALWAYS_INLINE
- size_t find_if(function_ref<bool(char)> F, size_t From = 0) const noexcept {
- StringRef S = drop_front(From);
- while (!S.empty()) {
- if (F(S.front()))
- return size() - S.size();
- S = S.drop_front();
- }
- return npos;
- }
-
- /// Search for the first character not satisfying the predicate \p F
- ///
- /// \returns The index of the first character not satisfying \p F starting
- /// from \p From, or npos if not found.
- LLVM_NODISCARD
- LLVM_ATTRIBUTE_ALWAYS_INLINE
- size_t find_if_not(function_ref<bool(char)> F, size_t From = 0) const noexcept {
- return find_if([F](char c) { return !F(c); }, From);
- }
-
- /// Search for the first string \p Str in the string.
- ///
- /// \returns The index of the first occurrence of \p Str, or npos if not
- /// found.
- LLVM_NODISCARD
- size_t find(StringRef Str, size_t From = 0) const noexcept;
-
- /// Search for the first string \p Str in the string, ignoring case.
- ///
- /// \returns The index of the first occurrence of \p Str, or npos if not
- /// found.
- LLVM_NODISCARD
- size_t find_lower(StringRef Str, size_t From = 0) const noexcept;
-
- /// Search for the last character \p C in the string.
- ///
- /// \returns The index of the last occurrence of \p C, or npos if not
- /// found.
- LLVM_NODISCARD
- size_t rfind(char C, size_t From = npos) const noexcept {
- From = (std::min)(From, Length);
- size_t i = From;
- while (i != 0) {
- --i;
- if (Data[i] == C)
- return i;
- }
- return npos;
- }
-
- /// Search for the last character \p C in the string, ignoring case.
- ///
- /// \returns The index of the last occurrence of \p C, or npos if not
- /// found.
- LLVM_NODISCARD
- size_t rfind_lower(char C, size_t From = npos) const noexcept;
-
- /// Search for the last string \p Str in the string.
- ///
- /// \returns The index of the last occurrence of \p Str, or npos if not
- /// found.
- LLVM_NODISCARD
- size_t rfind(StringRef Str) const noexcept;
-
- /// Search for the last string \p Str in the string, ignoring case.
- ///
- /// \returns The index of the last occurrence of \p Str, or npos if not
- /// found.
- LLVM_NODISCARD
- size_t rfind_lower(StringRef Str) const noexcept;
-
- /// Find the first character in the string that is \p C, or npos if not
- /// found. Same as find.
- LLVM_NODISCARD
- size_t find_first_of(char C, size_t From = 0) const noexcept {
- return find(C, From);
- }
-
- /// Find the first character in the string that is in \p Chars, or npos if
- /// not found.
- ///
- /// Complexity: O(size() + Chars.size())
- LLVM_NODISCARD
- size_t find_first_of(StringRef Chars, size_t From = 0) const noexcept;
-
- /// Find the first character in the string that is not \p C or npos if not
- /// found.
- LLVM_NODISCARD
- size_t find_first_not_of(char C, size_t From = 0) const noexcept;
-
- /// Find the first character in the string that is not in the string
- /// \p Chars, or npos if not found.
- ///
- /// Complexity: O(size() + Chars.size())
- LLVM_NODISCARD
- size_t find_first_not_of(StringRef Chars, size_t From = 0) const noexcept;
-
- /// Find the last character in the string that is \p C, or npos if not
- /// found.
- LLVM_NODISCARD
- size_t find_last_of(char C, size_t From = npos) const noexcept {
- return rfind(C, From);
- }
-
- /// Find the last character in the string that is in \p C, or npos if not
- /// found.
- ///
- /// Complexity: O(size() + Chars.size())
- LLVM_NODISCARD
- size_t find_last_of(StringRef Chars, size_t From = npos) const noexcept;
-
- /// Find the last character in the string that is not \p C, or npos if not
- /// found.
- LLVM_NODISCARD
- size_t find_last_not_of(char C, size_t From = npos) const noexcept;
-
- /// Find the last character in the string that is not in \p Chars, or
- /// npos if not found.
- ///
- /// Complexity: O(size() + Chars.size())
- LLVM_NODISCARD
- size_t find_last_not_of(StringRef Chars, size_t From = npos) const noexcept;
-
- /// Return true if the given string is a substring of *this, and false
- /// otherwise.
- LLVM_NODISCARD
- LLVM_ATTRIBUTE_ALWAYS_INLINE
- bool contains(StringRef Other) const noexcept { return find(Other) != npos; }
-
- /// Return true if the given character is contained in *this, and false
- /// otherwise.
- LLVM_NODISCARD
- LLVM_ATTRIBUTE_ALWAYS_INLINE
- bool contains(char C) const noexcept { return find_first_of(C) != npos; }
-
- /// Return true if the given string is a substring of *this, and false
- /// otherwise.
- LLVM_NODISCARD
- LLVM_ATTRIBUTE_ALWAYS_INLINE
- bool contains_lower(StringRef Other) const noexcept {
- return find_lower(Other) != npos;
- }
-
- /// Return true if the given character is contained in *this, and false
- /// otherwise.
- LLVM_NODISCARD
- LLVM_ATTRIBUTE_ALWAYS_INLINE
- bool contains_lower(char C) const noexcept { return find_lower(C) != npos; }
-
- /// @}
- /// @name Helpful Algorithms
- /// @{
-
- /// Return the number of occurrences of \p C in the string.
- LLVM_NODISCARD
- size_t count(char C) const noexcept {
- size_t Count = 0;
- for (size_t i = 0, e = Length; i != e; ++i)
- if (Data[i] == C)
- ++Count;
- return Count;
- }
-
- /// Return the number of non-overlapped occurrences of \p Str in
- /// the string.
- size_t count(StringRef Str) const noexcept;
-
- /// Parse the current string as an integer of the specified radix. If
- /// \p Radix is specified as zero, this does radix autosensing using
- /// extended C rules: 0 is octal, 0x is hex, 0b is binary.
- ///
- /// If the string is invalid or if only a subset of the string is valid,
- /// this returns true to signify the error. The string is considered
- /// erroneous if empty or if it overflows T.
- template <typename T>
- typename std::enable_if<std::numeric_limits<T>::is_signed, bool>::type
- getAsInteger(unsigned Radix, T &Result) const noexcept {
- long long LLVal;
- if (getAsSignedInteger(*this, Radix, LLVal) ||
- static_cast<T>(LLVal) != LLVal)
- return true;
- Result = LLVal;
- return false;
- }
-
- template <typename T>
- typename std::enable_if<!std::numeric_limits<T>::is_signed, bool>::type
- getAsInteger(unsigned Radix, T &Result) const noexcept {
- unsigned long long ULLVal;
- // The additional cast to unsigned long long is required to avoid the
- // Visual C++ warning C4805: '!=' : unsafe mix of type 'bool' and type
- // 'unsigned __int64' when instantiating getAsInteger with T = bool.
- if (getAsUnsignedInteger(*this, Radix, ULLVal) ||
- static_cast<unsigned long long>(static_cast<T>(ULLVal)) != ULLVal)
- return true;
- Result = ULLVal;
- return false;
- }
-
- /// Parse the current string as an integer of the specified radix. If
- /// \p Radix is specified as zero, this does radix autosensing using
- /// extended C rules: 0 is octal, 0x is hex, 0b is binary.
- ///
- /// If the string does not begin with a number of the specified radix,
- /// this returns true to signify the error. The string is considered
- /// erroneous if empty or if it overflows T.
- /// The portion of the string representing the discovered numeric value
- /// is removed from the beginning of the string.
- template <typename T>
- typename std::enable_if<std::numeric_limits<T>::is_signed, bool>::type
- consumeInteger(unsigned Radix, T &Result) noexcept {
- long long LLVal;
- if (consumeSignedInteger(*this, Radix, LLVal) ||
- static_cast<long long>(static_cast<T>(LLVal)) != LLVal)
- return true;
- Result = LLVal;
- return false;
- }
-
- template <typename T>
- typename std::enable_if<!std::numeric_limits<T>::is_signed, bool>::type
- consumeInteger(unsigned Radix, T &Result) noexcept {
- unsigned long long ULLVal;
- if (consumeUnsignedInteger(*this, Radix, ULLVal) ||
- static_cast<unsigned long long>(static_cast<T>(ULLVal)) != ULLVal)
- return true;
- Result = ULLVal;
- return false;
- }
-
- /// @}
- /// @name String Operations
- /// @{
-
- // Convert the given ASCII string to lowercase.
- LLVM_NODISCARD
- std::string lower() const;
-
- /// Convert the given ASCII string to uppercase.
- LLVM_NODISCARD
- std::string upper() const;
-
- /// @}
- /// @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 N
- /// exceeds the number of characters remaining in the string, the string
- /// suffix (starting with \p Start) will be returned.
- LLVM_NODISCARD
- LLVM_ATTRIBUTE_ALWAYS_INLINE
- StringRef substr(size_t Start, size_t N = npos) const noexcept {
- Start = (std::min)(Start, Length);
- return StringRef(Data + Start, (std::min)(N, Length - Start));
- }
-
- /// Return a StringRef equal to 'this' but with only the first \p N
- /// elements remaining. If \p N is greater than the length of the
- /// string, the entire string is returned.
- LLVM_NODISCARD
- LLVM_ATTRIBUTE_ALWAYS_INLINE
- StringRef take_front(size_t N = 1) const noexcept {
- if (N >= size())
- return *this;
- return drop_back(size() - N);
- }
-
- /// Return a StringRef equal to 'this' but with only the last \p N
- /// elements remaining. If \p N is greater than the length of the
- /// string, the entire string is returned.
- LLVM_NODISCARD
- LLVM_ATTRIBUTE_ALWAYS_INLINE
- StringRef take_back(size_t N = 1) const noexcept {
- if (N >= size())
- return *this;
- return drop_front(size() - N);
- }
-
- /// Return the longest prefix of 'this' such that every character
- /// in the prefix satisfies the given predicate.
- LLVM_NODISCARD
- LLVM_ATTRIBUTE_ALWAYS_INLINE
- StringRef take_while(function_ref<bool(char)> F) const noexcept {
- return substr(0, find_if_not(F));
- }
-
- /// Return the longest prefix of 'this' such that no character in
- /// the prefix satisfies the given predicate.
- LLVM_NODISCARD
- LLVM_ATTRIBUTE_ALWAYS_INLINE
- StringRef take_until(function_ref<bool(char)> F) const noexcept {
- return substr(0, find_if(F));
- }
-
- /// Return a StringRef equal to 'this' but with the first \p N elements
- /// dropped.
- LLVM_NODISCARD
- LLVM_ATTRIBUTE_ALWAYS_INLINE
- StringRef drop_front(size_t N = 1) const noexcept {
- assert(size() >= N && "Dropping more elements than exist");
- return substr(N);
- }
-
- /// Return a StringRef equal to 'this' but with the last \p N elements
- /// dropped.
- LLVM_NODISCARD
- LLVM_ATTRIBUTE_ALWAYS_INLINE
- StringRef drop_back(size_t N = 1) const noexcept {
- assert(size() >= N && "Dropping more elements than exist");
- return substr(0, size()-N);
- }
-
- /// Return a StringRef equal to 'this', but with all characters satisfying
- /// the given predicate dropped from the beginning of the string.
- LLVM_NODISCARD
- LLVM_ATTRIBUTE_ALWAYS_INLINE
- StringRef drop_while(function_ref<bool(char)> F) const noexcept {
- return substr(find_if_not(F));
- }
-
- /// Return a StringRef equal to 'this', but with all characters not
- /// satisfying the given predicate dropped from the beginning of the string.
- LLVM_NODISCARD
- LLVM_ATTRIBUTE_ALWAYS_INLINE
- StringRef drop_until(function_ref<bool(char)> F) const noexcept {
- return substr(find_if(F));
- }
-
- /// Returns true if this StringRef has the given prefix and removes that
- /// prefix.
- LLVM_ATTRIBUTE_ALWAYS_INLINE
- bool consume_front(StringRef Prefix) noexcept {
- if (!startswith(Prefix))
- return false;
-
- *this = drop_front(Prefix.size());
- return true;
- }
-
- /// Returns true if this StringRef has the given suffix and removes that
- /// suffix.
- LLVM_ATTRIBUTE_ALWAYS_INLINE
- bool consume_back(StringRef Suffix) noexcept {
- if (!endswith(Suffix))
- return false;
-
- *this = drop_back(Suffix.size());
- return true;
- }
-
- /// 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 exceeds the number of characters
- /// remaining in the string, the string suffix (starting with \p Start)
- /// will be returned. If this is less than \p Start, an empty string will
- /// be returned.
- LLVM_NODISCARD
- LLVM_ATTRIBUTE_ALWAYS_INLINE
- StringRef slice(size_t Start, size_t End) const noexcept {
- Start = (std::min)(Start, Length);
- End = (std::min)((std::max)(Start, End), Length);
- return StringRef(Data + Start, End - Start);
- }
-
- /// Split into two substrings around the first occurrence of a separator
- /// character.
- ///
- /// If \p Separator is in the string, then the result is a pair (LHS, RHS)
- /// such that (*this == LHS + Separator + RHS) is true and RHS is
- /// maximal. If \p Separator is not in the string, then the result is a
- /// pair (LHS, RHS) where (*this == LHS) and (RHS == "").
- ///
- /// \param Separator The character to split on.
- /// \returns The split substrings.
- LLVM_NODISCARD
- std::pair<StringRef, StringRef> split(char Separator) const {
- return split(StringRef(&Separator, 1));
- }
-
- /// Split into two substrings around the first occurrence of a separator
- /// string.
- ///
- /// If \p Separator is in the string, then the result is a pair (LHS, RHS)
- /// such that (*this == LHS + Separator + RHS) is true and RHS is
- /// maximal. If \p Separator is not in the string, then the result is a
- /// pair (LHS, RHS) where (*this == LHS) and (RHS == "").
- ///
- /// \param Separator - The string to split on.
- /// \return - The split substrings.
- LLVM_NODISCARD
- std::pair<StringRef, StringRef> split(StringRef Separator) const {
- size_t Idx = find(Separator);
- if (Idx == npos)
- return std::make_pair(*this, StringRef());
- return std::make_pair(slice(0, Idx), slice(Idx + Separator.size(), npos));
- }
-
- /// Split into two substrings around the last occurrence of a separator
- /// string.
- ///
- /// If \p Separator is in the string, then the result is a pair (LHS, RHS)
- /// such that (*this == LHS + Separator + RHS) is true and RHS is
- /// minimal. If \p Separator is not in the string, then the result is a
- /// pair (LHS, RHS) where (*this == LHS) and (RHS == "").
- ///
- /// \param Separator - The string to split on.
- /// \return - The split substrings.
- LLVM_NODISCARD
- std::pair<StringRef, StringRef> rsplit(StringRef Separator) const {
- size_t Idx = rfind(Separator);
- if (Idx == npos)
- return std::make_pair(*this, StringRef());
- return std::make_pair(slice(0, Idx), slice(Idx + Separator.size(), npos));
- }
-
- /// Split into substrings around the occurrences of a separator string.
- ///
- /// Each substring is stored in \p A. If \p MaxSplit is >= 0, at most
- /// \p MaxSplit splits are done and consequently <= \p MaxSplit + 1
- /// elements are added to A.
- /// If \p KeepEmpty is false, empty strings are not added to \p A. They
- /// still count when considering \p MaxSplit
- /// An useful invariant is that
- /// Separator.join(A) == *this if MaxSplit == -1 and KeepEmpty == true
- ///
- /// \param A - Where to put the substrings.
- /// \param Separator - The string to split on.
- /// \param MaxSplit - The maximum number of times the string is split.
- /// \param KeepEmpty - True if empty substring should be added.
- void split(SmallVectorImpl<StringRef> &A,
- StringRef Separator, int MaxSplit = -1,
- bool KeepEmpty = true) const;
-
- /// Split into substrings around the occurrences of a separator character.
- ///
- /// Each substring is stored in \p A. If \p MaxSplit is >= 0, at most
- /// \p MaxSplit splits are done and consequently <= \p MaxSplit + 1
- /// elements are added to A.
- /// If \p KeepEmpty is false, empty strings are not added to \p A. They
- /// still count when considering \p MaxSplit
- /// An useful invariant is that
- /// Separator.join(A) == *this if MaxSplit == -1 and KeepEmpty == true
- ///
- /// \param A - Where to put the substrings.
- /// \param Separator - The string to split on.
- /// \param MaxSplit - The maximum number of times the string is split.
- /// \param KeepEmpty - True if empty substring should be added.
- void split(SmallVectorImpl<StringRef> &A, char Separator, int MaxSplit = -1,
- bool KeepEmpty = true) const;
-
- /// Split into two substrings around the last occurrence of a separator
- /// character.
- ///
- /// If \p Separator is in the string, then the result is a pair (LHS, RHS)
- /// such that (*this == LHS + Separator + RHS) is true and RHS is
- /// minimal. If \p Separator is not in the string, then the result is a
- /// pair (LHS, RHS) where (*this == LHS) and (RHS == "").
- ///
- /// \param Separator - The character to split on.
- /// \return - The split substrings.
- LLVM_NODISCARD
- std::pair<StringRef, StringRef> rsplit(char Separator) const {
- return rsplit(StringRef(&Separator, 1));
- }
-
- /// Return string with consecutive \p Char characters starting from the
- /// the left removed.
- LLVM_NODISCARD
- StringRef ltrim(char Char) const noexcept {
- return drop_front((std::min)(Length, find_first_not_of(Char)));
- }
-
- /// Return string with consecutive characters in \p Chars starting from
- /// the left removed.
- LLVM_NODISCARD
- StringRef ltrim(StringRef Chars = " \t\n\v\f\r") const noexcept {
- return drop_front((std::min)(Length, find_first_not_of(Chars)));
- }
-
- /// Return string with consecutive \p Char characters starting from the
- /// right removed.
- LLVM_NODISCARD
- StringRef rtrim(char Char) const noexcept {
- return drop_back(size() - (std::min)(Length, find_last_not_of(Char) + 1));
- }
-
- /// Return string with consecutive characters in \p Chars starting from
- /// the right removed.
- LLVM_NODISCARD
- StringRef rtrim(StringRef Chars = " \t\n\v\f\r") const noexcept {
- return drop_back(size() - (std::min)(Length, find_last_not_of(Chars) + 1));
- }
-
- /// Return string with consecutive \p Char characters starting from the
- /// left and right removed.
- LLVM_NODISCARD
- StringRef trim(char Char) const noexcept {
- return ltrim(Char).rtrim(Char);
- }
-
- /// Return string with consecutive characters in \p Chars starting from
- /// the left and right removed.
- LLVM_NODISCARD
- StringRef trim(StringRef Chars = " \t\n\v\f\r") const noexcept {
- return ltrim(Chars).rtrim(Chars);
- }
-
- /// @}
- };
-
- /// A wrapper around a string literal that serves as a proxy for constructing
- /// global tables of StringRefs with the length computed at compile time.
- /// In order to avoid the invocation of a global constructor, StringLiteral
- /// should *only* be used in a constexpr context, as such:
- ///
- /// constexpr StringLiteral S("test");
- ///
- class StringLiteral : public StringRef {
- private:
- constexpr StringLiteral(const char *Str, size_t N) : StringRef(Str, N) {
- }
-
- public:
- template <size_t N>
- constexpr StringLiteral(const char (&Str)[N])
-#if defined(__clang__) && __has_attribute(enable_if)
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wgcc-compat"
- __attribute((enable_if(__builtin_strlen(Str) == N - 1,
- "invalid string literal")))
-#pragma clang diagnostic pop
-#endif
- : StringRef(Str, N - 1) {
- }
-
- // Explicit construction for strings like "foo\0bar".
- template <size_t N>
- static constexpr StringLiteral withInnerNUL(const char (&Str)[N]) {
- return StringLiteral(Str, N - 1);
- }
- };
-
- /// @name StringRef Comparison Operators
- /// @{
-
- LLVM_ATTRIBUTE_ALWAYS_INLINE
- bool operator==(StringRef LHS, StringRef RHS) noexcept {
- return LHS.equals(RHS);
- }
-
- LLVM_ATTRIBUTE_ALWAYS_INLINE
- bool operator!=(StringRef LHS, StringRef RHS) noexcept {
- return !(LHS == RHS);
- }
-
- inline bool operator<(StringRef LHS, StringRef RHS) noexcept {
- return LHS.compare(RHS) == -1;
- }
-
- inline bool operator<=(StringRef LHS, StringRef RHS) noexcept {
- return LHS.compare(RHS) != 1;
- }
-
- inline bool operator>(StringRef LHS, StringRef RHS) noexcept {
- return LHS.compare(RHS) == 1;
- }
-
- inline bool operator>=(StringRef LHS, StringRef RHS) noexcept {
- return LHS.compare(RHS) != -1;
- }
-
- inline bool operator==(StringRef LHS, const char *RHS) noexcept {
- return LHS.equals(StringRef(RHS));
- }
-
- inline bool operator!=(StringRef LHS, const char *RHS) noexcept {
- return !(LHS == StringRef(RHS));
- }
-
- inline bool operator<(StringRef LHS, const char *RHS) noexcept {
- return LHS.compare(StringRef(RHS)) == -1;
- }
-
- inline bool operator<=(StringRef LHS, const char *RHS) noexcept {
- return LHS.compare(StringRef(RHS)) != 1;
- }
-
- inline bool operator>(StringRef LHS, const char *RHS) noexcept {
- return LHS.compare(StringRef(RHS)) == 1;
- }
-
- inline bool operator>=(StringRef LHS, const char *RHS) noexcept {
- return LHS.compare(StringRef(RHS)) != -1;
- }
-
- inline bool operator==(const char *LHS, StringRef RHS) noexcept {
- return StringRef(LHS).equals(RHS);
- }
-
- inline bool operator!=(const char *LHS, StringRef RHS) noexcept {
- return !(StringRef(LHS) == RHS);
- }
-
- inline bool operator<(const char *LHS, StringRef RHS) noexcept {
- return StringRef(LHS).compare(RHS) == -1;
- }
-
- inline bool operator<=(const char *LHS, StringRef RHS) noexcept {
- return StringRef(LHS).compare(RHS) != 1;
- }
-
- inline bool operator>(const char *LHS, StringRef RHS) noexcept {
- return StringRef(LHS).compare(RHS) == 1;
- }
-
- inline bool operator>=(const char *LHS, StringRef RHS) noexcept {
- return StringRef(LHS).compare(RHS) != -1;
- }
-
- inline std::string &operator+=(std::string &buffer, StringRef string) {
- return buffer.append(string.data(), string.size());
- }
-
- std::ostream &operator<<(std::ostream &os, StringRef string);
-
- /// @}
-
- /// Compute a hash_code for a StringRef.
- LLVM_NODISCARD
- hash_code hash_value(StringRef S);
-
- // StringRefs can be treated like a POD type.
- template <typename T> struct isPodLike;
- template <> struct isPodLike<StringRef> { static const bool value = true; };
-
-} // end namespace wpi
-
-#endif // LLVM_ADT_STRINGREF_H
diff --git a/wpiutil/src/main/native/include/wpi/SymbolExports.h b/wpiutil/src/main/native/include/wpi/SymbolExports.h
new file mode 100644
index 0000000..4e84825
--- /dev/null
+++ b/wpiutil/src/main/native/include/wpi/SymbolExports.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
+
+#ifdef _WIN32
+#pragma warning(disable : 4251)
+
+#ifdef WPILIB_EXPORTS
+#ifdef __GNUC__
+#define WPILIB_DLLEXPORT __attribute__((dllexport))
+#else
+#define WPILIB_DLLEXPORT __declspec(dllexport)
+#endif
+
+#else
+#define WPILIB_DLLEXPORT
+#endif
+
+#else
+#define WPILIB_DLLEXPORT __attribute__((visibility("default")))
+#endif
diff --git a/wpiutil/src/main/native/include/wpi/Synchronization.h b/wpiutil/src/main/native/include/wpi/Synchronization.h
new file mode 100644
index 0000000..8050358
--- /dev/null
+++ b/wpiutil/src/main/native/include/wpi/Synchronization.h
@@ -0,0 +1,610 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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 <limits.h> // NOLINT
+
+#ifdef __cplusplus
+#include <initializer_list>
+
+#include "wpi/span.h"
+#endif
+
+/**
+ * Generic handle for all WPI handle-based interfaces.
+ *
+ * Handle data layout:
+ * - Bits 0-23: Type-specific
+ * - Bits 24-30: Type
+ * - Bit 31: Error
+ */
+typedef unsigned int WPI_Handle; // NOLINT
+
+/** An event handle. */
+typedef WPI_Handle WPI_EventHandle; // NOLINT
+
+/** A semaphore handle. */
+typedef WPI_Handle WPI_SemaphoreHandle; // NOLINT
+
+#ifdef __cplusplus
+
+namespace wpi {
+
+/** Constant representing an invalid handle. */
+constexpr unsigned int kInvalidHandle = 0;
+
+/**
+ * Standard types for handles.
+ * @{
+ */
+constexpr int kHandleTypeEvent = 1;
+constexpr int kHandleTypeSemaphore = 2;
+constexpr int kHandleTypeCSBase = 3;
+constexpr int kHandleTypeNTBase = 16;
+constexpr int kHandleTypeHALBase = 32;
+constexpr int kHandleTypeUserBase = 64;
+/** @} */
+
+/**
+ * Creates an event. Events have binary state (signaled or not signaled) and
+ * may be either automatically reset or manually reset. Automatic-reset events
+ * go to non-signaled state when a WaitForObject is woken up by the event;
+ * manual-reset events require ResetEvent() to be called to set the event to
+ * non-signaled state; if ResetEvent() is not called, any waiter on that event
+ * will immediately wake when called.
+ *
+ * @param manualReset true for manual reset, false for automatic reset
+ * @param initialState true to make the event initially in signaled state
+ * @return Event handle
+ */
+WPI_EventHandle CreateEvent(bool manualReset = false,
+ bool initialState = false);
+
+/**
+ * Destroys an event. Destruction wakes up any waiters.
+ *
+ * @param handle event handle
+ */
+void DestroyEvent(WPI_EventHandle handle);
+
+/**
+ * Sets an event to signaled state.
+ *
+ * @param handle event handle
+ */
+void SetEvent(WPI_EventHandle handle);
+
+/**
+ * Sets an event to non-signaled state.
+ *
+ * @param handle event handle
+ */
+void ResetEvent(WPI_EventHandle handle);
+
+/**
+ * Creates a semaphore. Semaphores keep an internal counter. Releasing the
+ * semaphore increases the count. A semaphore with a non-zero count is
+ * considered signaled. When a waiter wakes up it atomically decrements the
+ * count by 1. This is generally useful in a single-supplier,
+ * multiple-consumer scenario.
+ *
+ * @param initialCount initial value for the semaphore's internal counter
+ * @param maximumCount maximum value for the samephore's internal counter
+ * @return Semaphore handle
+ */
+WPI_SemaphoreHandle CreateSemaphore(int initialCount = 0,
+ int maximumCount = INT_MAX);
+
+/**
+ * Destroys a semaphore. Destruction wakes up any waiters.
+ *
+ * @param handle semaphore handle
+ */
+void DestroySemaphore(WPI_SemaphoreHandle handle);
+
+/**
+ * Releases N counts of a semaphore.
+ *
+ * @param handle semaphore handle
+ * @param releaseCount amount to add to semaphore's internal counter;
+ * must be positive
+ * @param prevCount if non-null, previous count (output parameter)
+ * @return True on successful release, false on failure (e.g. release count
+ * would exceed maximum value, or handle invalid)
+ */
+bool ReleaseSemaphore(WPI_SemaphoreHandle handle, int releaseCount = 1,
+ int* prevCount = nullptr);
+
+/**
+ * Waits for an handle to be signaled.
+ *
+ * @param handle handle to wait on
+ * @return True if handle was signaled, false otherwise (e.g. object was
+ * destroyed)
+ */
+bool WaitForObject(WPI_Handle handle);
+
+/**
+ * Waits for an handle to be signaled, with timeout.
+ *
+ * @param handle handle to wait on
+ * @param timeout timeout in seconds
+ * @param timedOut if non-null, set to true if timeout reached without handle
+ * being signaled; set to false otherwise (output)
+ * @return True if handle was signaled, false otherwise (e.g. object was
+ * destroyed or timed out)
+ */
+bool WaitForObject(WPI_Handle handle, double timeout, bool* timedOut);
+
+/**
+ * Waits for one or more handles to be signaled.
+ *
+ * Invalid handles are treated as signaled; the returned array will have the
+ * handle error bit set for any invalid handles.
+ *
+ * @param handles array of handles to wait on
+ * @param signaled output array for storage of signaled handles; must be at
+ * least the size of the handles input array
+ * @return array of signaled handles (points into signaled array)
+ */
+wpi::span<WPI_Handle> WaitForObjects(wpi::span<const WPI_Handle> handles,
+ wpi::span<WPI_Handle> signaled);
+
+/**
+ * Waits for one or more handles to be signaled.
+ *
+ * Invalid handles are treated as signaled; the returned array will have the
+ * handle error bit set for any invalid handles.
+ *
+ * @param handles array of handles to wait on
+ * @param signaled output array for storage of signaled handles; must be at
+ * least the size of the handles input array
+ * @return array of signaled handles (points into signaled array)
+ */
+inline wpi::span<WPI_Handle> WaitForObjects(
+ std::initializer_list<WPI_Handle> handles, wpi::span<WPI_Handle> signaled) {
+ return WaitForObjects(wpi::span{handles.begin(), handles.size()}, signaled);
+}
+
+/**
+ * Waits for one or more handles to be signaled, with timeout.
+ *
+ * Invalid handles are treated as signaled; the returned array will have the
+ * handle error bit set for any invalid handles.
+ *
+ * @param handles array of handles to wait on
+ * @param signaled output array for storage of signaled handles; must be at
+ * least the size of the handles input array
+ * @param timeout timeout in seconds
+ * @param timedOut if non-null, set to true if timeout reached without any
+ * handle being signaled; set to false otherwise (output)
+ * @return array of signaled handles (points into signaled array)
+ */
+wpi::span<WPI_Handle> WaitForObjects(wpi::span<const WPI_Handle> handles,
+ wpi::span<WPI_Handle> signaled,
+ double timeout, bool* timedOut);
+/**
+ * Waits for one or more handles to be signaled, with timeout.
+ *
+ * Invalid handles are treated as signaled; the returned array will have the
+ * handle error bit set for any invalid handles.
+ *
+ * @param handles array of handles to wait on
+ * @param signaled output array for storage of signaled handles; must be at
+ * least the size of the handles input array
+ * @param timeout timeout in seconds
+ * @param timedOut if non-null, set to true if timeout reached without any
+ * handle being signaled; set to false otherwise (output)
+ * @return array of signaled handles (points into signaled array)
+ */
+inline wpi::span<WPI_Handle> WaitForObjects(
+ std::initializer_list<WPI_Handle> handles, wpi::span<WPI_Handle> signaled,
+ double timeout, bool* timedOut) {
+ return WaitForObjects(wpi::span{handles.begin(), handles.size()}, signaled,
+ timeout, timedOut);
+}
+
+/**
+ * Sets up signaling for an arbitrary handle. With this function, any handle
+ * can operate like an event handle.
+ *
+ * @param handle 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);
+
+/**
+ * Sets a handle to signaled state.
+ *
+ * @param handle handle
+ */
+void SetSignalObject(WPI_Handle handle);
+
+/**
+ * Sets a handle to non-signaled state.
+ *
+ * @param handle handle
+ */
+void ResetSignalObject(WPI_Handle handle);
+
+/**
+ * Cleans up signaling for a handle. Destruction wakes up any waiters.
+ *
+ * @param handle handle
+ */
+void DestroySignalObject(WPI_Handle handle);
+
+/**
+ * An atomic signaling event for synchronization.
+ *
+ * Events have binary state (signaled or not signaled) and may be either
+ * automatically reset or manually reset. Automatic-reset events go to
+ * non-signaled state when a WaitForObject is woken up by the event;
+ * manual-reset events require Reset() to be called to set the event to
+ * non-signaled state; if Reset() is not called, any waiter on that event
+ * will immediately wake when called.
+ */
+class Event final {
+ public:
+ /**
+ * Constructor.
+ *
+ * @param manualReset true for manual reset, false for automatic reset
+ * @param initialState true to make the event initially in signaled state
+ */
+ explicit Event(bool manualReset = false, bool initialState = false)
+ : m_handle{CreateEvent(manualReset, initialState)} {}
+ ~Event() {
+ if (m_handle != 0) {
+ DestroyEvent(m_handle);
+ }
+ }
+
+ Event(const Event&) = delete;
+ Event& operator=(const Event&) = delete;
+
+ Event(Event&& rhs) : m_handle{rhs.m_handle} { rhs.m_handle = 0; }
+ Event& operator=(Event&& rhs) {
+ if (m_handle != 0) {
+ DestroyEvent(m_handle);
+ }
+ m_handle = rhs.m_handle;
+ rhs.m_handle = 0;
+ return *this;
+ }
+
+ /**
+ * Gets the event handle (e.g. for WaitForObject).
+ *
+ * @return handle
+ */
+ explicit operator WPI_Handle() const { return m_handle; }
+
+ /**
+ * Gets the event handle (e.g. for WaitForObject).
+ *
+ * @return handle
+ */
+ WPI_EventHandle GetHandle() const { return m_handle; }
+
+ /**
+ * Sets the event to signaled state.
+ */
+ void Set() { SetEvent(m_handle); }
+
+ /**
+ * Sets the event to non-signaled state.
+ */
+ void Reset() { ResetEvent(m_handle); }
+
+ private:
+ WPI_EventHandle m_handle;
+};
+
+/**
+ * A semaphore for synchronization.
+ *
+ * Semaphores keep an internal counter. Releasing the semaphore increases
+ * the count. A semaphore with a non-zero count is considered signaled.
+ * When a waiter wakes up it atomically decrements the count by 1. This
+ * is generally useful in a single-supplier, multiple-consumer scenario.
+ */
+class Semaphore final {
+ public:
+ /**
+ * Constructor.
+ *
+ * @param initialCount initial value for the semaphore's internal counter
+ * @param maximumCount maximum value for the samephore's internal counter
+ */
+ explicit Semaphore(int initialCount = 0, int maximumCount = INT_MAX)
+ : m_handle{CreateSemaphore(initialCount, maximumCount)} {}
+ ~Semaphore() {
+ if (m_handle != 0) {
+ DestroySemaphore(m_handle);
+ }
+ }
+
+ Semaphore(const Semaphore&) = delete;
+ Semaphore& operator=(const Semaphore&) = delete;
+
+ Semaphore(Semaphore&& rhs) : m_handle{rhs.m_handle} { rhs.m_handle = 0; }
+ Semaphore& operator=(Semaphore&& rhs) {
+ if (m_handle != 0) {
+ DestroySemaphore(m_handle);
+ }
+ m_handle = rhs.m_handle;
+ rhs.m_handle = 0;
+ return *this;
+ }
+
+ /**
+ * Gets the semaphore handle (e.g. for WaitForObject).
+ *
+ * @return handle
+ */
+ explicit operator WPI_Handle() const { return m_handle; }
+
+ /**
+ * Gets the semaphore handle (e.g. for WaitForObject).
+ *
+ * @return handle
+ */
+ WPI_SemaphoreHandle GetHandle() const { return m_handle; }
+
+ /**
+ * Releases N counts of the semaphore.
+ *
+ * @param releaseCount amount to add to semaphore's internal counter;
+ * must be positive
+ * @param prevCount if non-null, previous count (output parameter)
+ * @return True on successful release, false on failure (e.g. release count
+ * would exceed maximum value, or handle invalid)
+ */
+ bool Release(int releaseCount = 1, int* prevCount = nullptr) {
+ return ReleaseSemaphore(m_handle, releaseCount, prevCount);
+ }
+
+ private:
+ WPI_SemaphoreHandle m_handle;
+};
+
+/**
+ * RAII wrapper for signaling handles.
+ *
+ * Sets up signaling for an arbitrary handle. This enables any handle
+ * to operate like an event handle.
+ */
+template <typename T>
+class SignalObject final {
+ public:
+ /**
+ * Constructor.
+ *
+ * @param handle handle
+ * @param manualReset true for manual reset, false for automatic reset
+ * @param initialState true to make the handle initially in signaled state
+ */
+ explicit SignalObject(T handle = 0, bool manualReset = false,
+ bool initialState = false)
+ : m_handle{handle} {
+ CreateSignalObject(handle, manualReset, initialState);
+ }
+ ~SignalObject() {
+ if (m_handle != 0) {
+ DestroySignalObject(m_handle);
+ }
+ }
+
+ SignalObject(const SignalObject&) = delete;
+ SignalObject& operator=(const SignalObject&) = delete;
+
+ SignalObject(SignalObject&& rhs) : m_handle{rhs.m_handle} {
+ rhs.m_handle = 0;
+ }
+ SignalObject& operator=(SignalObject&& rhs) {
+ if (m_handle != 0) {
+ DestroySemaphore(m_handle);
+ }
+ m_handle = rhs.m_handle;
+ rhs.m_handle = 0;
+ return *this;
+ }
+
+ /**
+ * Gets the handle.
+ *
+ * @return handle
+ */
+ /*implicit*/ operator T() const { return m_handle; } // NOLINT
+
+ /**
+ * Gets the handle (e.g. for WaitForObject).
+ *
+ * @return handle
+ */
+ T GetHandle() const { return m_handle; }
+
+ /**
+ * Sets the handle to signaled state.
+ */
+ void Set() { SetSignalObject(m_handle); }
+
+ /**
+ * Sets the handle to non-signaled state.
+ */
+ void Reset() { ResetSignalObject(m_handle); }
+
+ private:
+ T m_handle;
+};
+
+} // namespace wpi
+
+extern "C" {
+
+#endif // __cplusplus
+
+/**
+ * Creates an event. Events have binary state (signaled or not signaled) and
+ * may be either automatically reset or manually reset. Automatic-reset events
+ * go to non-signaled state when a WaitForObject is woken up by the event;
+ * manual-reset events require ResetEvent() to be called to set the event to
+ * non-signaled state; if ResetEvent() is not called, any waiter on that event
+ * will immediately wake when called.
+ *
+ * @param manual_reset true for manual reset, false for automatic reset
+ * @param initial_state true to make the event initially in signaled state
+ * @return Event handle
+ */
+WPI_EventHandle WPI_CreateEvent(int manual_reset, int initial_state);
+
+/**
+ * Destroys an event. Destruction wakes up any waiters.
+ *
+ * @param handle event handle
+ */
+void WPI_DestroyEvent(WPI_EventHandle handle);
+
+/**
+ * Sets an event to signaled state.
+ *
+ * @param handle event handle
+ */
+void WPI_SetEvent(WPI_EventHandle handle);
+
+/**
+ * Sets an event to non-signaled state.
+ *
+ * @param handle event handle
+ */
+void WPI_ResetEvent(WPI_EventHandle handle);
+
+/**
+ * Creates a semaphore. Semaphores keep an internal counter. Releasing the
+ * semaphore increases the count. A semaphore with a non-zero count is
+ * considered signaled. When a waiter wakes up it atomically decrements the
+ * count by 1. This is generally useful in a single-supplier,
+ * multiple-consumer scenario.
+ *
+ * @param initial_count initial value for the semaphore's internal counter
+ * @param maximum_count maximum value for the samephore's internal counter
+ * @return Semaphore handle
+ */
+WPI_SemaphoreHandle WPI_CreateSemaphore(int initial_count, int maximum_count);
+
+/**
+ * Destroys a semaphore. Destruction wakes up any waiters.
+ *
+ * @param handle semaphore handle
+ */
+void WPI_DestroySemaphore(WPI_SemaphoreHandle handle);
+
+/**
+ * Releases N counts of a semaphore.
+ *
+ * @param handle semaphore handle
+ * @param release_count amount to add to semaphore's internal counter;
+ * must be positive
+ * @param prev_count if non-null, previous count (output parameter)
+ * @return Non-zero on successful release, zero on failure (e.g. release count
+ * would exceed maximum value, or handle invalid)
+ */
+int WPI_ReleaseSemaphore(WPI_SemaphoreHandle handle, int release_count,
+ int* prev_count);
+
+/**
+ * Waits for an handle to be signaled.
+ *
+ * @param handle handle to wait on
+ * @return Non-zero if handle was signaled, zero otherwise (e.g. object was
+ * destroyed)
+ */
+int WPI_WaitForObject(WPI_Handle handle);
+
+/**
+ * Waits for an handle to be signaled, with timeout.
+ *
+ * @param handle handle to wait on
+ * @param timeout timeout in seconds
+ * @param timed_out if non-null, set to non-zero if timeout reached without
+ * handle being signaled; set to zero otherwise (output)
+ * @return Non-zero if handle was signaled, zero otherwise (e.g. object was
+ * destroyed or timed out)
+ */
+int WPI_WaitForObjectTimeout(WPI_Handle handle, double timeout, int* timed_out);
+
+/**
+ * Waits for one or more handles to be signaled.
+ *
+ * Invalid handles are treated as signaled; the returned array will have the
+ * handle error bit set for any invalid handles.
+ *
+ * @param handles array of handles to wait on
+ * @param handles_count length of the handles array
+ * @param signaled output array for storage of signaled handles; must be at
+ * least the size of the handles input array
+ * @return number of signaled handles
+ */
+int WPI_WaitForObjects(const WPI_Handle* handles, int handles_count,
+ WPI_Handle* signaled);
+
+/**
+ * Waits for one or more handles to be signaled, with timeout.
+ *
+ * Invalid handles are treated as signaled; the returned array will have the
+ * handle error bit set for any invalid handles.
+ *
+ * @param handles array of handles to wait on
+ * @param handles_count length of the handles array
+ * @param signaled output array for storage of signaled handles; must be at
+ * least the size of the handles input array
+ * @param timeout timeout in seconds
+ * @param timed_out if non-null, set to non-zero if timeout reached without any
+ * handle being signaled; set to zero otherwise (output)
+ * @return number of signaled handles
+ */
+int WPI_WaitForObjectsTimeout(const WPI_Handle* handles, int handles_count,
+ WPI_Handle* signaled, double timeout,
+ int* timed_out);
+
+/**
+ * Sets up signaling for an arbitrary handle. With this function, any handle
+ * can operate like an event handle.
+ *
+ * @param handle handle
+ * @param manual_reset true for manual reset, false for automatic reset
+ * @param initial_state true to make the handle initially in signaled state
+ */
+void WPI_CreateSignalObject(WPI_Handle handle, int manual_reset,
+ int initial_state);
+
+/**
+ * Sets a handle to signaled state.
+ *
+ * @param handle handle
+ */
+void WPI_SetSignalObject(WPI_Handle handle);
+
+/**
+ * Sets a handle to non-signaled state.
+ *
+ * @param handle handle
+ */
+void WPI_ResetSignalObject(WPI_Handle handle);
+
+/**
+ * Cleans up signaling for a handle. Destruction wakes up any waiters.
+ *
+ * @param handle handle
+ */
+void WPI_DestroySignalObject(WPI_Handle handle);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/wpiutil/src/main/native/include/wpi/TCPAcceptor.h b/wpiutil/src/main/native/include/wpi/TCPAcceptor.h
index da82a8a..083897a 100644
--- a/wpiutil/src/main/native/include/wpi/TCPAcceptor.h
+++ b/wpiutil/src/main/native/include/wpi/TCPAcceptor.h
@@ -27,6 +27,7 @@
#include <atomic>
#include <memory>
#include <string>
+#include <string_view>
#include "wpi/NetworkAcceptor.h"
#include "wpi/TCPStream.h"
@@ -44,11 +45,11 @@
Logger& m_logger;
public:
- TCPAcceptor(int port, const char* address, Logger& logger);
- ~TCPAcceptor();
+ TCPAcceptor(int port, std::string_view address, Logger& logger);
+ ~TCPAcceptor() override;
int start() override;
- void shutdown() override;
+ void shutdown() final;
std::unique_ptr<NetworkStream> accept() override;
};
diff --git a/wpiutil/src/main/native/include/wpi/TCPConnector.h b/wpiutil/src/main/native/include/wpi/TCPConnector.h
index a05342b..63e8906 100644
--- a/wpiutil/src/main/native/include/wpi/TCPConnector.h
+++ b/wpiutil/src/main/native/include/wpi/TCPConnector.h
@@ -27,8 +27,8 @@
#include <memory>
#include <utility>
-#include "wpi/ArrayRef.h"
#include "wpi/NetworkStream.h"
+#include "wpi/span.h"
namespace wpi {
@@ -40,7 +40,7 @@
Logger& logger,
int timeout = 0);
static std::unique_ptr<NetworkStream> connect_parallel(
- ArrayRef<std::pair<const char*, int>> servers, Logger& logger,
+ span<const std::pair<const char*, int>> servers, Logger& logger,
int timeout = 0);
};
diff --git a/wpiutil/src/main/native/include/wpi/TCPStream.h b/wpiutil/src/main/native/include/wpi/TCPStream.h
index 0c6e8a9..2d54300 100644
--- a/wpiutil/src/main/native/include/wpi/TCPStream.h
+++ b/wpiutil/src/main/native/include/wpi/TCPStream.h
@@ -26,6 +26,7 @@
#include <cstddef>
#include <string>
+#include <string_view>
#include "wpi/NetworkStream.h"
@@ -43,14 +44,14 @@
friend class TCPAcceptor;
friend class TCPConnector;
- ~TCPStream();
+ ~TCPStream() override;
size_t send(const char* buffer, size_t len, Error* err) override;
size_t receive(char* buffer, size_t len, Error* err,
int timeout = 0) override;
- void close() override;
+ void close() final;
- StringRef getPeerIP() const override;
+ std::string_view getPeerIP() const override;
int getPeerPort() const override;
void setNoDelay() override;
bool setBlocking(bool enabled) override;
diff --git a/wpiutil/src/main/native/include/wpi/Twine.h b/wpiutil/src/main/native/include/wpi/Twine.h
deleted file mode 100644
index a5b7e97..0000000
--- a/wpiutil/src/main/native/include/wpi/Twine.h
+++ /dev/null
@@ -1,543 +0,0 @@
-//===- Twine.h - Fast Temporary String Concatenation ------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef WPIUTIL_WPI_TWINE_H
-#define WPIUTIL_WPI_TWINE_H
-
-#include "wpi/SmallVector.h"
-#include "wpi/StringRef.h"
-#include "wpi/ErrorHandling.h"
-#include <cassert>
-#include <cstdint>
-#include <string>
-
-#ifdef _WIN32
-#pragma warning(push)
-#pragma warning(disable : 26495)
-#endif
-
-namespace wpi {
-
- class raw_ostream;
-
- /// Twine - A lightweight data structure for efficiently representing the
- /// concatenation of temporary values as strings.
- ///
- /// A Twine is a kind of rope, it represents a concatenated string using a
- /// binary-tree, where the string is the preorder of the nodes. Since the
- /// Twine can be efficiently rendered into a buffer when its result is used,
- /// it avoids the cost of generating temporary values for intermediate string
- /// results -- particularly in cases when the Twine result is never
- /// required. By explicitly tracking the type of leaf nodes, we can also avoid
- /// the creation of temporary strings for conversions operations (such as
- /// appending an integer to a string).
- ///
- /// A Twine is not intended for use directly and should not be stored, its
- /// implementation relies on the ability to store pointers to temporary stack
- /// objects which may be deallocated at the end of a statement. Twines should
- /// only be used accepted as const references in arguments, when an API wishes
- /// to accept possibly-concatenated strings.
- ///
- /// Twines support a special 'null' value, which always concatenates to form
- /// itself, and renders as an empty string. This can be returned from APIs to
- /// effectively nullify any concatenations performed on the result.
- ///
- /// \b Implementation
- ///
- /// Given the nature of a Twine, it is not possible for the Twine's
- /// concatenation method to construct interior nodes; the result must be
- /// represented inside the returned value. For this reason a Twine object
- /// actually holds two values, the left- and right-hand sides of a
- /// concatenation. We also have nullary Twine objects, which are effectively
- /// sentinel values that represent empty strings.
- ///
- /// Thus, a Twine can effectively have zero, one, or two children. The \see
- /// isNullary(), \see isUnary(), and \see isBinary() predicates exist for
- /// testing the number of children.
- ///
- /// We maintain a number of invariants on Twine objects (FIXME: Why):
- /// - Nullary twines are always represented with their Kind on the left-hand
- /// side, and the Empty kind on the right-hand side.
- /// - Unary twines are always represented with the value on the left-hand
- /// side, and the Empty kind on the right-hand side.
- /// - If a Twine has another Twine as a child, that child should always be
- /// binary (otherwise it could have been folded into the parent).
- ///
- /// These invariants are check by \see isValid().
- ///
- /// \b Efficiency Considerations
- ///
- /// The Twine is designed to yield efficient and small code for common
- /// situations. For this reason, the concat() method is inlined so that
- /// concatenations of leaf nodes can be optimized into stores directly into a
- /// single stack allocated object.
- ///
- /// In practice, not all compilers can be trusted to optimize concat() fully,
- /// so we provide two additional methods (and accompanying operator+
- /// overloads) to guarantee that particularly important cases (cstring plus
- /// StringRef) codegen as desired.
- class Twine {
- /// NodeKind - Represent the type of an argument.
- enum NodeKind : unsigned char {
- /// An empty string; the result of concatenating anything with it is also
- /// empty.
- NullKind,
-
- /// The empty string.
- EmptyKind,
-
- /// A pointer to a Twine instance.
- TwineKind,
-
- /// A pointer to a C string instance.
- CStringKind,
-
- /// A pointer to an std::string instance.
- StdStringKind,
-
- /// A pointer to a StringRef instance.
- StringRefKind,
-
- /// A pointer to a SmallString instance.
- SmallStringKind,
-
- /// A char value, to render as a character.
- CharKind,
-
- /// An unsigned int value, to render as an unsigned decimal integer.
- DecUIKind,
-
- /// An int value, to render as a signed decimal integer.
- DecIKind,
-
- /// A pointer to an unsigned long value, to render as an unsigned decimal
- /// integer.
- DecULKind,
-
- /// A pointer to a long value, to render as a signed decimal integer.
- DecLKind,
-
- /// A pointer to an unsigned long long value, to render as an unsigned
- /// decimal integer.
- DecULLKind,
-
- /// A pointer to a long long value, to render as a signed decimal integer.
- DecLLKind,
-
- /// A pointer to a uint64_t value, to render as an unsigned hexadecimal
- /// integer.
- UHexKind
- };
-
- union Child
- {
- const Twine *twine;
- const char *cString;
- const std::string *stdString;
- const StringRef *stringRef;
- const SmallVectorImpl<char> *smallString;
- char character;
- unsigned int decUI;
- int decI;
- const unsigned long *decUL;
- const long *decL;
- const unsigned long long *decULL;
- const long long *decLL;
- const uint64_t *uHex;
- };
-
- /// LHS - The prefix in the concatenation, which may be uninitialized for
- /// Null or Empty kinds.
- Child LHS;
-
- /// RHS - The suffix in the concatenation, which may be uninitialized for
- /// Null or Empty kinds.
- Child RHS;
-
- /// LHSKind - The NodeKind of the left hand side, \see getLHSKind().
- NodeKind LHSKind = EmptyKind;
-
- /// RHSKind - The NodeKind of the right hand side, \see getRHSKind().
- NodeKind RHSKind = EmptyKind;
-
- /// Construct a nullary twine; the kind must be NullKind or EmptyKind.
- explicit Twine(NodeKind Kind) : LHSKind(Kind) {
- assert(isNullary() && "Invalid kind!");
- }
-
- /// Construct a binary twine.
- explicit Twine(const Twine &LHS, const Twine &RHS)
- : LHSKind(TwineKind), RHSKind(TwineKind) {
- this->LHS.twine = &LHS;
- this->RHS.twine = &RHS;
- assert(isValid() && "Invalid twine!");
- }
-
- /// Construct a twine from explicit values.
- explicit Twine(Child LHS, NodeKind LHSKind, Child RHS, NodeKind RHSKind)
- : LHS(LHS), RHS(RHS), LHSKind(LHSKind), RHSKind(RHSKind) {
- assert(isValid() && "Invalid twine!");
- }
-
- /// Check for the empty twine.
- bool isEmpty() const {
- return getLHSKind() == EmptyKind;
- }
-
- /// Check if this is a nullary twine (null or empty).
- bool isNullary() const {
- return isNull() || isEmpty();
- }
-
- /// Check if this is a unary twine.
- bool isUnary() const {
- return getRHSKind() == EmptyKind && !isNullary();
- }
-
- /// Check if this is a binary twine.
- bool isBinary() const {
- return getLHSKind() != NullKind && getRHSKind() != EmptyKind;
- }
-
- /// Check if this is a valid twine (satisfying the invariants on
- /// order and number of arguments).
- bool isValid() const {
- // Nullary twines always have Empty on the RHS.
- if (isNullary() && getRHSKind() != EmptyKind)
- return false;
-
- // Null should never appear on the RHS.
- if (getRHSKind() == NullKind)
- return false;
-
- // The RHS cannot be non-empty if the LHS is empty.
- if (getRHSKind() != EmptyKind && getLHSKind() == EmptyKind)
- return false;
-#if 0 // causes spurious warnings
- // A twine child should always be binary.
- if (getLHSKind() == TwineKind &&
- !LHS.twine->isBinary())
- return false;
- if (getRHSKind() == TwineKind &&
- !RHS.twine->isBinary())
- return false;
-#endif
- return true;
- }
-
- /// Get the NodeKind of the left-hand side.
- NodeKind getLHSKind() const { return LHSKind; }
-
- /// Get the NodeKind of the right-hand side.
- NodeKind getRHSKind() const { return RHSKind; }
-
- /// Print one child from a twine.
- void printOneChild(raw_ostream &OS, Child Ptr, NodeKind Kind) const;
-
- /// Print the representation of one child from a twine.
- void printOneChildRepr(raw_ostream &OS, Child Ptr,
- NodeKind Kind) const;
-
- public:
- /// @name Constructors
- /// @{
-
- /// Construct from an empty string.
- /*implicit*/ Twine() {
- assert(isValid() && "Invalid twine!");
- }
-
- Twine(const Twine &) = default;
-
- /// Construct from a C string.
- ///
- /// We take care here to optimize "" into the empty twine -- this will be
- /// optimized out for string constants. This allows Twine arguments have
- /// default "" values, without introducing unnecessary string constants.
- /*implicit*/ Twine(const char *Str) {
- if (Str[0] != '\0') {
- LHS.cString = Str;
- LHSKind = CStringKind;
- } else
- LHSKind = EmptyKind;
-
- assert(isValid() && "Invalid twine!");
- }
-
- /// Construct from an std::string.
- /*implicit*/ Twine(const std::string &Str) : LHSKind(StdStringKind) {
- LHS.stdString = &Str;
- assert(isValid() && "Invalid twine!");
- }
-
- /// Construct from a StringRef.
- /*implicit*/ Twine(const StringRef &Str) : LHSKind(StringRefKind) {
- LHS.stringRef = &Str;
- assert(isValid() && "Invalid twine!");
- }
-
- /// Construct from a SmallString.
- /*implicit*/ Twine(const SmallVectorImpl<char> &Str)
- : LHSKind(SmallStringKind) {
- LHS.smallString = &Str;
- assert(isValid() && "Invalid twine!");
- }
-
- /// Construct from a char.
- explicit Twine(char Val) : LHSKind(CharKind) {
- LHS.character = Val;
- }
-
- /// Construct from a signed char.
- explicit Twine(signed char Val) : LHSKind(CharKind) {
- LHS.character = static_cast<char>(Val);
- }
-
- /// Construct from an unsigned char.
- explicit Twine(unsigned char Val) : LHSKind(CharKind) {
- LHS.character = static_cast<char>(Val);
- }
-
- /// Construct a twine to print \p Val as an unsigned decimal integer.
- explicit Twine(unsigned Val) : LHSKind(DecUIKind) {
- LHS.decUI = Val;
- }
-
- /// Construct a twine to print \p Val as a signed decimal integer.
- explicit Twine(int Val) : LHSKind(DecIKind) {
- LHS.decI = Val;
- }
-
- /// Construct a twine to print \p Val as an unsigned decimal integer.
- explicit Twine(const unsigned long &Val) : LHSKind(DecULKind) {
- LHS.decUL = &Val;
- }
-
- /// Construct a twine to print \p Val as a signed decimal integer.
- explicit Twine(const long &Val) : LHSKind(DecLKind) {
- LHS.decL = &Val;
- }
-
- /// Construct a twine to print \p Val as an unsigned decimal integer.
- explicit Twine(const unsigned long long &Val) : LHSKind(DecULLKind) {
- LHS.decULL = &Val;
- }
-
- /// Construct a twine to print \p Val as a signed decimal integer.
- explicit Twine(const long long &Val) : LHSKind(DecLLKind) {
- LHS.decLL = &Val;
- }
-
- // FIXME: Unfortunately, to make sure this is as efficient as possible we
- // need extra binary constructors from particular types. We can't rely on
- // the compiler to be smart enough to fold operator+()/concat() down to the
- // right thing. Yet.
-
- /// Construct as the concatenation of a C string and a StringRef.
- /*implicit*/ Twine(const char *LHS, const StringRef &RHS)
- : LHSKind(CStringKind), RHSKind(StringRefKind) {
- this->LHS.cString = LHS;
- this->RHS.stringRef = &RHS;
- assert(isValid() && "Invalid twine!");
- }
-
- /// Construct as the concatenation of a StringRef and a C string.
- /*implicit*/ Twine(const StringRef &LHS, const char *RHS)
- : LHSKind(StringRefKind), RHSKind(CStringKind) {
- this->LHS.stringRef = &LHS;
- this->RHS.cString = RHS;
- assert(isValid() && "Invalid twine!");
- }
-
- /// Since the intended use of twines is as temporary objects, assignments
- /// when concatenating might cause undefined behavior or stack corruptions
- Twine &operator=(const Twine &) = delete;
-
- /// Create a 'null' string, which is an empty string that always
- /// concatenates to form another empty string.
- static Twine createNull() {
- return Twine(NullKind);
- }
-
- /// @}
- /// @name Numeric Conversions
- /// @{
-
- // Construct a twine to print \p Val as an unsigned hexadecimal integer.
- static Twine utohexstr(const uint64_t &Val) {
- Child LHS, RHS;
- LHS.uHex = &Val;
- RHS.twine = nullptr;
- return Twine(LHS, UHexKind, RHS, EmptyKind);
- }
-
- /// @}
- /// @name Predicate Operations
- /// @{
-
- /// Check for the null twine.
- bool isNull() const {
- return getLHSKind() == NullKind;
- }
-
- /// Check if this twine is trivially empty; a false return value does not
- /// necessarily mean the twine is empty.
- bool isTriviallyEmpty() const {
- return isNullary();
- }
-
- /// Return true if this twine can be dynamically accessed as a single
- /// StringRef value with getSingleStringRef().
- bool isSingleStringRef() const {
- if (getRHSKind() != EmptyKind) return false;
-
- switch (getLHSKind()) {
- case EmptyKind:
- case CStringKind:
- case StdStringKind:
- case StringRefKind:
- case SmallStringKind:
- case CharKind:
- return true;
- default:
- return false;
- }
- }
-
- /// @}
- /// @name String Operations
- /// @{
-
- Twine concat(const Twine &Suffix) const;
-
- /// @}
- /// @name Output & Conversion.
- /// @{
-
- /// Return the twine contents as a std::string.
- std::string str() const;
-
- /// Append the concatenated string into the given SmallString or SmallVector.
- void toVector(SmallVectorImpl<char> &Out) const;
-
- /// This returns the twine as a single StringRef. This method is only valid
- /// if isSingleStringRef() is true.
- StringRef getSingleStringRef() const {
- assert(isSingleStringRef() &&"This cannot be had as a single stringref!");
- switch (getLHSKind()) {
- default:
- // unreachable("Out of sync with isSingleStringRef");
- return StringRef();
- case EmptyKind: return StringRef();
- case CStringKind: return StringRef(LHS.cString);
- case StdStringKind: return StringRef(*LHS.stdString);
- case StringRefKind: return *LHS.stringRef;
- case SmallStringKind:
- return StringRef(LHS.smallString->data(), LHS.smallString->size());
- case CharKind: return StringRef(&LHS.character, 1);
- }
- }
-
- /// This returns the twine as a single StringRef if it can be
- /// represented as such. Otherwise the twine is written into the given
- /// SmallVector and a StringRef to the SmallVector's data is returned.
- StringRef toStringRef(SmallVectorImpl<char> &Out) const {
- if (isSingleStringRef())
- return getSingleStringRef();
- toVector(Out);
- return StringRef(Out.data(), Out.size());
- }
-
- /// This returns the twine as a single null terminated StringRef if it
- /// can be represented as such. Otherwise the twine is written into the
- /// given SmallVector and a StringRef to the SmallVector's data is returned.
- ///
- /// The returned StringRef's size does not include the null terminator.
- StringRef toNullTerminatedStringRef(SmallVectorImpl<char> &Out) const;
-
- /// Write the concatenated string represented by this twine to the
- /// stream \p OS.
- void print(raw_ostream &OS) const;
-
- /// Dump the concatenated string represented by this twine to stderr.
- void dump() const;
-
- /// Write the representation of this twine to the stream \p OS.
- void printRepr(raw_ostream &OS) const;
-
- /// Dump the representation of this twine to stderr.
- void dumpRepr() const;
-
- /// @}
- };
-
- /// @name Twine Inline Implementations
- /// @{
-
- inline Twine Twine::concat(const Twine &Suffix) const {
- // Concatenation with null is null.
- if (isNull() || Suffix.isNull())
- return Twine(NullKind);
-
- // Concatenation with empty yields the other side.
- if (isEmpty())
- return Suffix;
- if (Suffix.isEmpty())
- return *this;
-
- // Otherwise we need to create a new node, taking care to fold in unary
- // twines.
- Child NewLHS, NewRHS;
- NewLHS.twine = this;
- NewRHS.twine = &Suffix;
- NodeKind NewLHSKind = TwineKind, NewRHSKind = TwineKind;
- if (isUnary()) {
- NewLHS = LHS;
- NewLHSKind = getLHSKind();
- }
- if (Suffix.isUnary()) {
- NewRHS = Suffix.LHS;
- NewRHSKind = Suffix.getLHSKind();
- }
-
- return Twine(NewLHS, NewLHSKind, NewRHS, NewRHSKind);
- }
-
- inline Twine operator+(const Twine &LHS, const Twine &RHS) {
- return LHS.concat(RHS);
- }
-
- /// Additional overload to guarantee simplified codegen; this is equivalent to
- /// concat().
-
- inline Twine operator+(const char *LHS, const StringRef &RHS) {
- return Twine(LHS, RHS);
- }
-
- /// Additional overload to guarantee simplified codegen; this is equivalent to
- /// concat().
-
- inline Twine operator+(const StringRef &LHS, const char *RHS) {
- return Twine(LHS, RHS);
- }
-
- inline raw_ostream &operator<<(raw_ostream &OS, const Twine &RHS) {
- RHS.print(OS);
- return OS;
- }
-
- /// @}
-
-} // end namespace wpi
-
-#ifdef _WIN32
-#pragma warning(pop)
-#endif
-
-#endif // LLVM_ADT_TWINE_H
diff --git a/wpiutil/src/main/native/include/wpi/UDPClient.h b/wpiutil/src/main/native/include/wpi/UDPClient.h
index 635eca8..fd2e30b 100644
--- a/wpiutil/src/main/native/include/wpi/UDPClient.h
+++ b/wpiutil/src/main/native/include/wpi/UDPClient.h
@@ -1,20 +1,16 @@
-/*----------------------------------------------------------------------------*/
-/* 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. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UDPCLIENT_H_
#define WPIUTIL_WPI_UDPCLIENT_H_
#include <string>
+#include <string_view>
-#include "wpi/ArrayRef.h"
#include "wpi/SmallVector.h"
-#include "wpi/StringRef.h"
-#include "wpi/Twine.h"
#include "wpi/mutex.h"
+#include "wpi/span.h"
namespace wpi {
@@ -28,7 +24,7 @@
public:
explicit UDPClient(Logger& logger);
- UDPClient(const Twine& address, Logger& logger);
+ UDPClient(std::string_view address, Logger& logger);
UDPClient(const UDPClient& other) = delete;
UDPClient(UDPClient&& other);
~UDPClient();
@@ -40,8 +36,8 @@
int start(int port);
void shutdown();
// The passed in address MUST be a resolved IP address.
- int send(ArrayRef<uint8_t> data, const Twine& server, int port);
- int send(StringRef data, const Twine& server, int port);
+ int send(span<const uint8_t> data, std::string_view server, int port);
+ int send(std::string_view data, std::string_view server, int port);
int receive(uint8_t* data_received, int receive_len);
int receive(uint8_t* data_received, int receive_len,
SmallVectorImpl<char>* addr_received, int* port_received);
diff --git a/wpiutil/src/main/native/include/wpi/UidVector.h b/wpiutil/src/main/native/include/wpi/UidVector.h
index 077dc27..3ffd4c3 100644
--- a/wpiutil/src/main/native/include/wpi/UidVector.h
+++ b/wpiutil/src/main/native/include/wpi/UidVector.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* 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. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UIDVECTOR_H_
#define WPIUTIL_WPI_UIDVECTOR_H_
@@ -27,7 +24,9 @@
UidVectorIterator() = default;
explicit UidVectorIterator(It it, It end) : m_it(it), m_end(end) {
// advance to first non-empty element
- while (m_it != m_end && !*m_it) ++m_it;
+ while (m_it != m_end && !*m_it) {
+ ++m_it;
+ }
}
reference operator*() const noexcept { return *m_it; }
@@ -61,12 +60,15 @@
};
} // namespace impl
-// Vector which provides an integrated freelist for removal and reuse of
-// individual elements.
-// @tparam T element type; must be default-constructible and evaluate in
-// boolean context to false when "empty"
-// @tparam reuse_threshold how many free elements to store up before starting
-// to recycle them
+/**
+ * Vector which provides an integrated freelist for removal and reuse of
+ * individual elements.
+ *
+ * @tparam T element type; must be default-constructible and evaluate in
+ * boolean context to false when "empty"
+ * @tparam reuse_threshold how many free elements to store up before starting
+ * to recycle them
+ */
template <typename T, typename std::vector<T>::size_type reuse_threshold>
class UidVector {
public:
@@ -81,8 +83,8 @@
using const_iterator =
impl::UidVectorIterator<typename std::vector<T>::const_iterator>;
- bool empty() const { return m_active_count == 0; }
- size_type size() const { return m_vector.size(); }
+ bool empty() const noexcept { return m_active_count == 0; }
+ size_type size() const noexcept { return m_vector.size(); }
T& operator[](size_type i) { return m_vector[i]; }
const T& operator[](size_type i) const { return m_vector[i]; }
@@ -104,17 +106,25 @@
return uid;
}
- // Removes the identified element by replacing it with a default-constructed
- // one. The element is added to the freelist for later reuse.
- void erase(size_type uid) {
- if (uid >= m_vector.size() || !m_vector[uid]) return;
+ /**
+ * Removes the identified element by replacing it with a default-constructed
+ * one. The element is added to the freelist for later reuse.
+ */
+ T erase(size_type uid) {
+ if (uid >= m_vector.size() || !m_vector[uid]) {
+ return T();
+ }
m_free.push_back(uid);
+ auto rv = std::move(m_vector[uid]);
m_vector[uid] = T();
--m_active_count;
+ return rv;
}
- // Removes all elements.
- void clear() {
+ /**
+ * Removes all elements.
+ */
+ void clear() noexcept {
m_vector.clear();
m_free.clear();
m_active_count = 0;
diff --git a/wpiutil/src/main/native/include/wpi/UrlParser.h b/wpiutil/src/main/native/include/wpi/UrlParser.h
index e3da269..3b6107b 100644
--- a/wpiutil/src/main/native/include/wpi/UrlParser.h
+++ b/wpiutil/src/main/native/include/wpi/UrlParser.h
@@ -1,14 +1,12 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_URLPARSER_H_
#define WPIUTIL_WPI_URLPARSER_H_
-#include "wpi/StringRef.h"
+#include <string_view>
+
#include "wpi/http_parser.h"
namespace wpi {
@@ -24,7 +22,7 @@
* @param in input
* @param isConnect
*/
- UrlParser(StringRef in, bool isConnect) {
+ UrlParser(std::string_view in, bool isConnect) {
m_data = in;
http_parser_url_init(&m_url);
m_error = http_parser_parse_url(in.data(), in.size(), isConnect, &m_url);
@@ -53,41 +51,41 @@
return (m_url.field_set & (1 << UF_USERINFO)) != 0;
}
- StringRef GetSchema() const {
+ std::string_view GetSchema() const {
return m_data.substr(m_url.field_data[UF_SCHEMA].off,
m_url.field_data[UF_SCHEMA].len);
}
- StringRef GetHost() const {
+ std::string_view GetHost() const {
return m_data.substr(m_url.field_data[UF_HOST].off,
m_url.field_data[UF_HOST].len);
}
unsigned int GetPort() const { return m_url.port; }
- StringRef GetPath() const {
+ std::string_view GetPath() const {
return m_data.substr(m_url.field_data[UF_PATH].off,
m_url.field_data[UF_PATH].len);
}
- StringRef GetQuery() const {
+ std::string_view GetQuery() const {
return m_data.substr(m_url.field_data[UF_QUERY].off,
m_url.field_data[UF_QUERY].len);
}
- StringRef GetFragment() const {
+ std::string_view GetFragment() const {
return m_data.substr(m_url.field_data[UF_FRAGMENT].off,
m_url.field_data[UF_FRAGMENT].len);
}
- StringRef GetUserInfo() const {
+ std::string_view GetUserInfo() const {
return m_data.substr(m_url.field_data[UF_USERINFO].off,
m_url.field_data[UF_USERINFO].len);
}
private:
bool m_error;
- StringRef m_data;
+ std::string_view m_data;
http_parser_url m_url;
};
diff --git a/wpiutil/src/main/native/include/wpi/VersionTuple.h b/wpiutil/src/main/native/include/wpi/VersionTuple.h
index e54ca92..59df41c 100644
--- a/wpiutil/src/main/native/include/wpi/VersionTuple.h
+++ b/wpiutil/src/main/native/include/wpi/VersionTuple.h
@@ -15,11 +15,8 @@
#ifndef WPIUTIL_WPI_VERSIONTUPLE_H
#define WPIUTIL_WPI_VERSIONTUPLE_H
-#include "wpi/StringRef.h"
-#include "wpi/raw_ostream.h"
#include <optional>
#include <string>
-#include <tuple>
namespace wpi {
@@ -137,18 +134,7 @@
friend bool operator>=(const VersionTuple &X, const VersionTuple &Y) {
return !(X < Y);
}
-
- /// 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.
-raw_ostream &operator<<(raw_ostream &Out, const VersionTuple &V);
-
} // end namespace wpi
#endif // WPIUTIL_WPI_VERSIONTUPLE_H
diff --git a/wpiutil/src/main/native/include/wpi/WebSocket.h b/wpiutil/src/main/native/include/wpi/WebSocket.h
index 8347d0c..fc2ae4a 100644
--- a/wpiutil/src/main/native/include/wpi/WebSocket.h
+++ b/wpiutil/src/main/native/include/wpi/WebSocket.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_WEBSOCKET_H_
#define WPIUTIL_WPI_WEBSOCKET_H_
@@ -14,13 +11,12 @@
#include <initializer_list>
#include <memory>
#include <string>
+#include <string_view>
#include <utility>
-#include "wpi/ArrayRef.h"
#include "wpi/Signal.h"
#include "wpi/SmallVector.h"
-#include "wpi/StringRef.h"
-#include "wpi/Twine.h"
+#include "wpi/span.h"
#include "wpi/uv/Buffer.h"
#include "wpi/uv/Error.h"
#include "wpi/uv/Timer.h"
@@ -79,10 +75,10 @@
ClientOptions() : handshakeTimeout{(uv::Timer::Time::max)()} {}
/** Timeout for the handshake request. */
- uv::Timer::Time handshakeTimeout;
+ uv::Timer::Time handshakeTimeout; // NOLINT
/** Additional headers to include in handshake. */
- ArrayRef<std::pair<StringRef, StringRef>> extraHeaders;
+ span<const std::pair<std::string_view, std::string_view>> extraHeaders;
};
/**
@@ -96,9 +92,9 @@
* @param options Handshake options
*/
static std::shared_ptr<WebSocket> CreateClient(
- uv::Stream& stream, const Twine& uri, const Twine& host,
- ArrayRef<StringRef> protocols = ArrayRef<StringRef>{},
- const ClientOptions& options = ClientOptions{});
+ uv::Stream& stream, std::string_view uri, std::string_view host,
+ span<const std::string_view> protocols = {},
+ const ClientOptions& options = {});
/**
* Starts a client connection by performing the initial client handshake.
@@ -111,11 +107,10 @@
* @param options Handshake options
*/
static std::shared_ptr<WebSocket> CreateClient(
- uv::Stream& stream, const Twine& uri, const Twine& host,
- std::initializer_list<StringRef> protocols,
- const ClientOptions& options = ClientOptions{}) {
- return CreateClient(stream, uri, host,
- makeArrayRef(protocols.begin(), protocols.end()),
+ uv::Stream& stream, std::string_view uri, std::string_view host,
+ std::initializer_list<std::string_view> protocols,
+ const ClientOptions& options = {}) {
+ return CreateClient(stream, uri, host, {protocols.begin(), protocols.end()},
options);
}
@@ -133,8 +128,8 @@
* Sec-WebSocket-Protocol header field).
*/
static std::shared_ptr<WebSocket> CreateServer(
- uv::Stream& stream, StringRef key, StringRef version,
- StringRef protocol = StringRef{});
+ uv::Stream& stream, std::string_view key, std::string_view version,
+ std::string_view protocol = {});
/**
* Get connection state.
@@ -155,7 +150,7 @@
/**
* Get the selected sub-protocol. Only valid in or after the open() event.
*/
- StringRef GetProtocol() const { return m_protocol; }
+ std::string_view GetProtocol() const { return m_protocol; }
/**
* Set the maximum message size. Default is 128 KB. If configured to combine
@@ -179,17 +174,26 @@
* @param reason A human-readable string explaining why the connection is
* closing (optional).
*/
- void Close(uint16_t code = 1005, const Twine& reason = Twine{});
+ void Close(uint16_t code = 1005, std::string_view reason = {});
/**
* Send a text message.
* @param data UTF-8 encoded data to send
* @param callback Callback which is invoked when the write completes.
*/
- void SendText(
- ArrayRef<uv::Buffer> data,
- std::function<void(MutableArrayRef<uv::Buffer>, uv::Error)> callback) {
- Send(kFlagFin | kOpText, data, callback);
+ void SendText(span<const uv::Buffer> data,
+ std::function<void(span<uv::Buffer>, uv::Error)> callback) {
+ Send(kFlagFin | kOpText, data, std::move(callback));
+ }
+
+ /**
+ * Send a text message.
+ * @param data UTF-8 encoded data to send
+ * @param callback Callback which is invoked when the write completes.
+ */
+ void SendText(std::initializer_list<uv::Buffer> data,
+ std::function<void(span<uv::Buffer>, uv::Error)> callback) {
+ SendText({data.begin(), data.end()}, std::move(callback));
}
/**
@@ -197,10 +201,19 @@
* @param data Data to send
* @param callback Callback which is invoked when the write completes.
*/
- void SendBinary(
- ArrayRef<uv::Buffer> data,
- std::function<void(MutableArrayRef<uv::Buffer>, uv::Error)> callback) {
- Send(kFlagFin | kOpBinary, data, callback);
+ void SendBinary(span<const uv::Buffer> data,
+ std::function<void(span<uv::Buffer>, uv::Error)> callback) {
+ Send(kFlagFin | kOpBinary, data, std::move(callback));
+ }
+
+ /**
+ * Send a binary message.
+ * @param data Data to send
+ * @param callback Callback which is invoked when the write completes.
+ */
+ void SendBinary(std::initializer_list<uv::Buffer> data,
+ std::function<void(span<uv::Buffer>, uv::Error)> callback) {
+ SendBinary({data.begin(), data.end()}, std::move(callback));
}
/**
@@ -211,9 +224,22 @@
* @param callback Callback which is invoked when the write completes.
*/
void SendTextFragment(
- ArrayRef<uv::Buffer> data,
- std::function<void(MutableArrayRef<uv::Buffer>, uv::Error)> callback) {
- Send(kOpText, data, callback);
+ span<const uv::Buffer> data,
+ std::function<void(span<uv::Buffer>, uv::Error)> callback) {
+ Send(kOpText, data, std::move(callback));
+ }
+
+ /**
+ * Send a text message fragment. This must be followed by one or more
+ * SendFragment() calls, where the last one has fin=True, to complete the
+ * message.
+ * @param data UTF-8 encoded data to send
+ * @param callback Callback which is invoked when the write completes.
+ */
+ void SendTextFragment(
+ std::initializer_list<uv::Buffer> data,
+ std::function<void(span<uv::Buffer>, uv::Error)> callback) {
+ SendTextFragment({data.begin(), data.end()}, std::move(callback));
}
/**
@@ -224,9 +250,22 @@
* @param callback Callback which is invoked when the write completes.
*/
void SendBinaryFragment(
- ArrayRef<uv::Buffer> data,
- std::function<void(MutableArrayRef<uv::Buffer>, uv::Error)> callback) {
- Send(kOpBinary, data, callback);
+ span<const uv::Buffer> data,
+ std::function<void(span<uv::Buffer>, uv::Error)> callback) {
+ Send(kOpBinary, data, std::move(callback));
+ }
+
+ /**
+ * Send a text message fragment. This must be followed by one or more
+ * SendFragment() calls, where the last one has fin=True, to complete the
+ * message.
+ * @param data Data to send
+ * @param callback Callback which is invoked when the write completes.
+ */
+ void SendBinaryFragment(
+ std::initializer_list<uv::Buffer> data,
+ std::function<void(span<uv::Buffer>, uv::Error)> callback) {
+ SendBinaryFragment({data.begin(), data.end()}, std::move(callback));
}
/**
@@ -236,10 +275,21 @@
* @param fin Set to true if this is the final fragment of the message
* @param callback Callback which is invoked when the write completes.
*/
- void SendFragment(
- ArrayRef<uv::Buffer> data, bool fin,
- std::function<void(MutableArrayRef<uv::Buffer>, uv::Error)> callback) {
- Send(kOpCont | (fin ? kFlagFin : 0), data, callback);
+ void SendFragment(span<const uv::Buffer> data, bool fin,
+ std::function<void(span<uv::Buffer>, uv::Error)> callback) {
+ Send(kOpCont | (fin ? kFlagFin : 0), data, std::move(callback));
+ }
+
+ /**
+ * Send a continuation frame. This is used to send additional parts of a
+ * message started with SendTextFragment() or SendBinaryFragment().
+ * @param data Data to send
+ * @param fin Set to true if this is the final fragment of the message
+ * @param callback Callback which is invoked when the write completes.
+ */
+ void SendFragment(std::initializer_list<uv::Buffer> data, bool fin,
+ std::function<void(span<uv::Buffer>, uv::Error)> callback) {
+ SendFragment({data.begin(), data.end()}, fin, std::move(callback));
}
/**
@@ -248,8 +298,10 @@
* write completes.
*/
void SendPing(std::function<void(uv::Error)> callback = nullptr) {
- SendPing(ArrayRef<uv::Buffer>{}, [callback](auto bufs, uv::Error err) {
- if (callback) callback(err);
+ SendPing({}, [f = std::move(callback)](auto bufs, uv::Error err) {
+ if (f) {
+ f(err);
+ }
});
}
@@ -259,10 +311,20 @@
* @param callback Callback which is invoked when the ping frame
* write completes.
*/
- void SendPing(
- ArrayRef<uv::Buffer> data,
- std::function<void(MutableArrayRef<uv::Buffer>, uv::Error)> callback) {
- Send(kFlagFin | kOpPing, data, callback);
+ void SendPing(span<const uv::Buffer> data,
+ std::function<void(span<uv::Buffer>, uv::Error)> callback) {
+ Send(kFlagFin | kOpPing, data, std::move(callback));
+ }
+
+ /**
+ * Send a ping frame.
+ * @param data Data to send in the ping frame
+ * @param callback Callback which is invoked when the ping frame
+ * write completes.
+ */
+ void SendPing(std::initializer_list<uv::Buffer> data,
+ std::function<void(span<uv::Buffer>, uv::Error)> callback) {
+ SendPing({data.begin(), data.end()}, std::move(callback));
}
/**
@@ -271,8 +333,10 @@
* write completes.
*/
void SendPong(std::function<void(uv::Error)> callback = nullptr) {
- SendPong(ArrayRef<uv::Buffer>{}, [callback](auto bufs, uv::Error err) {
- if (callback) callback(err);
+ SendPong({}, [f = std::move(callback)](auto bufs, uv::Error err) {
+ if (f) {
+ f(err);
+ }
});
}
@@ -282,21 +346,31 @@
* @param callback Callback which is invoked when the pong frame
* write completes.
*/
- void SendPong(
- ArrayRef<uv::Buffer> data,
- std::function<void(MutableArrayRef<uv::Buffer>, uv::Error)> callback) {
- Send(kFlagFin | kOpPong, data, callback);
+ void SendPong(span<const uv::Buffer> data,
+ std::function<void(span<uv::Buffer>, uv::Error)> callback) {
+ Send(kFlagFin | kOpPong, data, std::move(callback));
+ }
+
+ /**
+ * Send a pong frame.
+ * @param data Data to send in the pong frame
+ * @param callback Callback which is invoked when the pong frame
+ * write completes.
+ */
+ void SendPong(std::initializer_list<uv::Buffer> data,
+ std::function<void(span<uv::Buffer>, uv::Error)> callback) {
+ SendPong({data.begin(), data.end()}, std::move(callback));
}
/**
* Fail the connection.
*/
- void Fail(uint16_t code = 1002, const Twine& reason = "protocol error");
+ void Fail(uint16_t code = 1002, std::string_view reason = "protocol error");
/**
* Forcibly close the connection.
*/
- void Terminate(uint16_t code = 1006, const Twine& reason = "terminated");
+ void Terminate(uint16_t code = 1006, std::string_view reason = "terminated");
/**
* Gets user-defined data.
@@ -314,10 +388,15 @@
void SetData(std::shared_ptr<void> data) { m_data = std::move(data); }
/**
+ * Shuts down and closes the underlying stream.
+ */
+ void Shutdown();
+
+ /**
* Open event. Emitted when the connection is open and ready to communicate.
* The parameter is the selected subprotocol.
*/
- sig::Signal<StringRef> open;
+ sig::Signal<std::string_view> open;
/**
* Close event. Emitted when the connection is closed. The first parameter
@@ -325,31 +404,31 @@
* has been closed. The second parameter is a human-readable string
* explaining the reason why the connection has been closed.
*/
- sig::Signal<uint16_t, StringRef> closed;
+ sig::Signal<uint16_t, std::string_view> closed;
/**
* Text message event. Emitted when a text message is received.
* The first parameter is the data, the second parameter is true if the
* data is the last fragment of the message.
*/
- sig::Signal<StringRef, bool> text;
+ sig::Signal<std::string_view, bool> text;
/**
* Binary message event. Emitted when a binary message is received.
* The first parameter is the data, the second parameter is true if the
* data is the last fragment of the message.
*/
- sig::Signal<ArrayRef<uint8_t>, bool> binary;
+ sig::Signal<span<const uint8_t>, bool> binary;
/**
* Ping event. Emitted when a ping message is received.
*/
- sig::Signal<ArrayRef<uint8_t>> ping;
+ sig::Signal<span<const uint8_t>> ping;
/**
* Pong event. Emitted when a pong message is received.
*/
- sig::Signal<ArrayRef<uint8_t>> pong;
+ sig::Signal<span<const uint8_t>> pong;
private:
// user data
@@ -381,16 +460,16 @@
class ClientHandshakeData;
std::unique_ptr<ClientHandshakeData> m_clientHandshake;
- void StartClient(const Twine& uri, const Twine& host,
- ArrayRef<StringRef> protocols, const ClientOptions& options);
- void StartServer(StringRef key, StringRef version, StringRef protocol);
- void SendClose(uint16_t code, const Twine& reason);
- void SetClosed(uint16_t code, const Twine& reason, bool failed = false);
- void Shutdown();
+ void StartClient(std::string_view uri, std::string_view host,
+ span<const std::string_view> protocols,
+ const ClientOptions& options);
+ void StartServer(std::string_view key, std::string_view version,
+ std::string_view protocol);
+ 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 Send(
- uint8_t opcode, ArrayRef<uv::Buffer> data,
- std::function<void(MutableArrayRef<uv::Buffer>, uv::Error)> callback);
+ void Send(uint8_t opcode, span<const uv::Buffer> data,
+ std::function<void(span<uv::Buffer>, uv::Error)> callback);
};
} // namespace wpi
diff --git a/wpiutil/src/main/native/include/wpi/WebSocketServer.h b/wpiutil/src/main/native/include/wpi/WebSocketServer.h
index a58a2c9..1f76d16 100644
--- a/wpiutil/src/main/native/include/wpi/WebSocketServer.h
+++ b/wpiutil/src/main/native/include/wpi/WebSocketServer.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_WEBSOCKETSERVER_H_
#define WPIUTIL_WPI_WEBSOCKETSERVER_H_
@@ -12,15 +9,15 @@
#include <initializer_list>
#include <memory>
#include <string>
+#include <string_view>
#include <utility>
-#include "wpi/ArrayRef.h"
#include "wpi/HttpParser.h"
#include "wpi/Signal.h"
#include "wpi/SmallString.h"
#include "wpi/SmallVector.h"
-#include "wpi/StringRef.h"
#include "wpi/WebSocket.h"
+#include "wpi/span.h"
namespace wpi {
@@ -55,7 +52,8 @@
* Second item is the matched protocol if a match was made, otherwise
* is empty.
*/
- std::pair<bool, StringRef> MatchProtocol(ArrayRef<StringRef> protocols);
+ std::pair<bool, std::string_view> MatchProtocol(
+ span<const std::string_view> protocols);
/**
* Try to find a match to the list of sub-protocols provided by the client.
@@ -66,9 +64,9 @@
* Second item is the matched protocol if a match was made, otherwise
* is empty.
*/
- std::pair<bool, StringRef> MatchProtocol(
- std::initializer_list<StringRef> protocols) {
- return MatchProtocol(makeArrayRef(protocols.begin(), protocols.end()));
+ std::pair<bool, std::string_view> MatchProtocol(
+ std::initializer_list<std::string_view> protocols) {
+ return MatchProtocol({protocols.begin(), protocols.end()});
}
/**
@@ -78,7 +76,7 @@
* @param protocol The subprotocol to send to the client
*/
std::shared_ptr<WebSocket> Accept(uv::Stream& stream,
- StringRef protocol = StringRef{}) {
+ std::string_view protocol = {}) {
return WebSocket::CreateServer(stream, m_key, m_version, protocol);
}
@@ -112,20 +110,20 @@
* Checker for URL. Return true if URL should be accepted. By default all
* URLs are accepted.
*/
- std::function<bool(StringRef)> checkUrl;
+ std::function<bool(std::string_view)> checkUrl;
/**
* Checker for Host header. Return true if Host should be accepted. By
* default all hosts are accepted.
*/
- std::function<bool(StringRef)> checkHost;
+ std::function<bool(std::string_view)> checkHost;
};
/**
* Private constructor.
*/
- WebSocketServer(uv::Stream& stream, ArrayRef<StringRef> protocols,
- const ServerOptions& options, const private_init&);
+ WebSocketServer(uv::Stream& stream, span<const std::string_view> protocols,
+ ServerOptions options, const private_init&);
/**
* Starts a dedicated WebSocket server on the provided connection. The
@@ -137,8 +135,8 @@
* @param options Handshake options
*/
static std::shared_ptr<WebSocketServer> Create(
- uv::Stream& stream, ArrayRef<StringRef> protocols = ArrayRef<StringRef>{},
- const ServerOptions& options = ServerOptions{});
+ uv::Stream& stream, span<const std::string_view> protocols = {},
+ const ServerOptions& options = {});
/**
* Starts a dedicated WebSocket server on the provided connection. The
@@ -150,16 +148,15 @@
* @param options Handshake options
*/
static std::shared_ptr<WebSocketServer> Create(
- uv::Stream& stream, std::initializer_list<StringRef> protocols,
- const ServerOptions& options = ServerOptions{}) {
- return Create(stream, makeArrayRef(protocols.begin(), protocols.end()),
- options);
+ uv::Stream& stream, std::initializer_list<std::string_view> protocols,
+ const ServerOptions& options = {}) {
+ return Create(stream, {protocols.begin(), protocols.end()}, options);
}
/**
* Connected event. First parameter is the URL, second is the websocket.
*/
- sig::Signal<StringRef, WebSocket&> connected;
+ sig::Signal<std::string_view, WebSocket&> connected;
private:
uv::Stream& m_stream;
@@ -172,7 +169,7 @@
sig::ScopedConnection m_errorConn;
sig::ScopedConnection m_endConn;
- void Abort(uint16_t code, StringRef reason);
+ void Abort(uint16_t code, std::string_view reason);
};
} // namespace wpi
diff --git a/wpiutil/src/main/native/include/wpi/WorkerThread.h b/wpiutil/src/main/native/include/wpi/WorkerThread.h
index 4db94c4..6c8852e 100644
--- a/wpiutil/src/main/native/include/wpi/WorkerThread.h
+++ b/wpiutil/src/main/native/include/wpi/WorkerThread.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_WORKERTHREAD_H_
#define WPIUTIL_WPI_WORKERTHREAD_H_
@@ -108,8 +105,9 @@
WorkerThreadRequest<R, T...>& req) {
R result = std::apply(req.work, std::move(req.params));
if (req.afterWork) {
- if (auto async = thr.m_async.m_async.lock())
+ if (auto async = thr.m_async.m_async.lock()) {
async->Send(std::move(req.afterWork), std::move(result));
+ }
} else {
thr.m_promises.SetValue(req.promiseId, std::move(result));
}
@@ -120,8 +118,9 @@
WorkerThreadRequest<void, T...>& req) {
std::apply(req.work, req.params);
if (req.afterWork) {
- if (auto async = thr.m_async.m_async.lock())
+ if (auto async = thr.m_async.m_async.lock()) {
async->Send(std::move(req.afterWork));
+ }
} else {
thr.m_promises.SetValue(req.promiseId);
}
@@ -133,14 +132,18 @@
while (m_active) {
std::unique_lock lock(m_mutex);
m_cond.wait(lock, [&] { return !m_active || !m_requests.empty(); });
- if (!m_active) break;
+ if (!m_active) {
+ break;
+ }
// don't want to hold the lock while executing the callbacks
requests.swap(m_requests);
lock.unlock();
for (auto&& req : requests) {
- if (!m_active) break; // requests may be long-running
+ if (!m_active) {
+ break; // requests may be long-running
+ }
RunWorkerThreadRequest(*this, req);
}
requests.clear();
@@ -172,7 +175,9 @@
* @param loop the loop to use for running afterWork routines
*/
void SetLoop(uv::Loop& loop) {
- if (auto thr = m_owner.GetThread()) thr->m_async.SetLoop(loop);
+ if (auto thr = m_owner.GetThread()) {
+ thr->m_async.SetLoop(loop);
+ }
}
/**
@@ -189,7 +194,9 @@
* Subsequent calls to QueueWorkThen will no longer run afterWork.
*/
void UnsetLoop() {
- if (auto thr = m_owner.GetThread()) thr->m_async.UnsetLoop();
+ if (auto thr = m_owner.GetThread()) {
+ thr->m_async.UnsetLoop();
+ }
}
/**
@@ -200,10 +207,11 @@
* @return The handle (if nullptr, no handle is set)
*/
std::shared_ptr<uv::Handle> GetHandle() const {
- if (auto thr = m_owner.GetThread())
+ if (auto thr = m_owner.GetThread()) {
return thr->m_async.m_async.lock();
- else
+ } else {
return nullptr;
+ }
}
/**
@@ -217,6 +225,7 @@
* destroyed while waiting for a result.
*
* @param work Work function (called on worker thread)
+ * @param u Arguments to work function
*/
template <typename... U>
future<R> QueueWork(WorkFunction work, U&&... u) {
@@ -252,6 +261,7 @@
*
* @param work Work function (called on worker thread)
* @param afterWork After work function (called on loop thread)
+ * @param u Arguments to work function
*/
template <typename... U>
void QueueWorkThen(WorkFunction work, AfterWorkFunction afterWork, U&&... u) {
diff --git a/wpiutil/src/main/native/include/wpi/array.h b/wpiutil/src/main/native/include/wpi/array.h
new file mode 100644
index 0000000..5a03371
--- /dev/null
+++ b/wpiutil/src/main/native/include/wpi/array.h
@@ -0,0 +1,101 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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 <tuple>
+#include <utility>
+
+namespace wpi {
+
+struct empty_array_t {};
+constexpr empty_array_t empty_array;
+
+/**
+ * This class is a wrapper around std::array that does compile time size
+ * checking.
+ *
+ * std::array's implicit constructor can lead result in uninitialized elements
+ * if the number of arguments doesn't match the std::array size.
+ */
+template <typename T, size_t N>
+class array : public std::array<T, N> {
+ public:
+ explicit array(empty_array_t) {}
+
+ template <typename... Ts>
+ array(T arg, Ts&&... args) // NOLINT
+ : std::array<T, N>{arg, std::forward<Ts>(args)...} {
+ static_assert(1 + sizeof...(args) == N, "Dimension mismatch");
+ }
+
+ array(const array<T, N>&) = default;
+ array& operator=(const array<T, N>&) = default;
+ array(array<T, N>&&) = default;
+ array& operator=(array<T, N>&&) = default;
+
+ array(const std::array<T, N>& rhs) { // NOLINT
+ *static_cast<std::array<T, N>*>(this) = rhs;
+ }
+
+ array& operator=(const std::array<T, N>& rhs) {
+ *static_cast<std::array<T, N>*>(this) = rhs;
+ return *this;
+ }
+
+ array(std::array<T, N>&& rhs) { // NOLINT
+ *static_cast<std::array<T, N>*>(this) = rhs;
+ }
+
+ array& operator=(std::array<T, N>&& rhs) {
+ *static_cast<std::array<T, N>*>(this) = rhs;
+ return *this;
+ }
+};
+
+template <typename T, typename... Ts>
+array(T, Ts...) -> array<std::enable_if_t<(std::is_same_v<T, Ts> && ...), T>,
+ 1 + sizeof...(Ts)>;
+
+} // namespace wpi
+
+template <size_t I, typename T, size_t 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>
+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>
+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>
+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));
+}
+
+// Enables structured bindings
+namespace std { // NOLINT
+// Partial specialization for wpi::array
+template <typename T, size_t N>
+struct tuple_size<wpi::array<T, N>> : public integral_constant<size_t, N> {};
+
+// Partial specialization for wpi::array
+template <size_t I, typename T, size_t 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/wpiutil/src/main/native/include/wpi/circular_buffer.h b/wpiutil/src/main/native/include/wpi/circular_buffer.h
index 5dc12f8..40913f9 100644
--- a/wpiutil/src/main/native/include/wpi/circular_buffer.h
+++ b/wpiutil/src/main/native/include/wpi/circular_buffer.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2015-2020 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. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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
@@ -19,44 +16,284 @@
template <class T>
class circular_buffer {
public:
- explicit circular_buffer(size_t size);
+ explicit circular_buffer(size_t size) : m_data(size, T{}) {}
- using value_type = T;
- using reference = value_type&;
- using const_reference = const value_type&;
- using pointer = value_type*;
- using size_type = size_t;
- using iterator_category = std::forward_iterator_tag;
- using difference_type = std::ptrdiff_t;
+ circular_buffer(const circular_buffer&) = default;
+ circular_buffer& operator=(const circular_buffer&) = default;
+ circular_buffer(circular_buffer&&) = default;
+ circular_buffer& operator=(circular_buffer&&) = default;
- size_type size() const;
- T& front();
- const T& front() const;
- T& back();
- const T& back() const;
- void push_front(T value);
- void push_back(T value);
- T pop_front();
- T pop_back();
+ class iterator {
+ public:
+ using iterator_category = std::forward_iterator_tag;
+ using value_type = T;
+ using difference_type = std::ptrdiff_t;
+ using pointer = T*;
+ using reference = T&;
+
+ iterator(circular_buffer* buffer, size_t index)
+ : m_buffer(buffer), m_index(index) {}
+
+ iterator& operator++() {
+ ++m_index;
+ return *this;
+ }
+ iterator operator++(int) {
+ iterator retval = *this;
+ ++(*this);
+ return retval;
+ }
+ bool operator==(const iterator& other) const {
+ return m_buffer == other.m_buffer && m_index == other.m_index;
+ }
+ bool operator!=(const iterator& other) const { return !(*this == other); }
+ reference operator*() { return (*m_buffer)[m_index]; }
+
+ private:
+ circular_buffer* m_buffer;
+ size_t m_index;
+ };
+
+ class const_iterator {
+ public:
+ using iterator_category = std::forward_iterator_tag;
+ using value_type = T;
+ using difference_type = std::ptrdiff_t;
+ using pointer = T*;
+ using const_reference = const T&;
+
+ const_iterator(const circular_buffer* buffer, size_t index)
+ : m_buffer(buffer), m_index(index) {}
+
+ const_iterator& operator++() {
+ ++m_index;
+ return *this;
+ }
+ const_iterator operator++(int) {
+ const_iterator retval = *this;
+ ++(*this);
+ return retval;
+ }
+ bool operator==(const const_iterator& other) const {
+ return m_buffer == other.m_buffer && m_index == other.m_index;
+ }
+ bool operator!=(const const_iterator& other) const {
+ return !(*this == other);
+ }
+ const_reference operator*() const { return (*m_buffer)[m_index]; }
+
+ private:
+ const circular_buffer* m_buffer;
+ size_t m_index;
+ };
+
+ iterator begin() { return iterator(this, 0); }
+ iterator end() { return iterator(this, ::wpi::circular_buffer<T>::size()); }
+
+ const_iterator begin() const { return const_iterator(this, 0); }
+ const_iterator end() const {
+ return const_iterator(this, ::wpi::circular_buffer<T>::size());
+ }
+
+ const_iterator cbegin() const { return const_iterator(this, 0); }
+ const_iterator cend() const {
+ return const_iterator(this, ::wpi::circular_buffer<T>::size());
+ }
+
+ /**
+ * Returns number of elements in buffer
+ */
+ size_t size() const { return m_length; }
+
+ /**
+ * Returns value at front of buffer
+ */
+ T& front() { return (*this)[0]; }
+
+ /**
+ * Returns value at front of buffer
+ */
+ const T& front() const { return (*this)[0]; }
+
+ /**
+ * Returns value at back of buffer
+ *
+ * If there are no elements in the buffer, calling this function results in
+ * undefined behavior.
+ */
+ T& back() { return m_data[(m_front + m_length - 1) % m_data.size()]; }
+
+ /**
+ * Returns value at back of buffer
+ *
+ * If there are no elements in the buffer, calling this function results in
+ * undefined behavior.
+ */
+ const T& back() const {
+ return m_data[(m_front + m_length - 1) % m_data.size()];
+ }
+
+ /**
+ * Push a new value onto the front of the buffer.
+ *
+ * The value at the back is overwritten if the buffer is full.
+ */
+ void push_front(T value) {
+ if (m_data.size() == 0) {
+ return;
+ }
+
+ m_front = ModuloDec(m_front);
+
+ m_data[m_front] = value;
+
+ if (m_length < m_data.size()) {
+ m_length++;
+ }
+ }
+
+ /**
+ * Push a new value onto the back of the buffer.
+ *
+ * The value at the front is overwritten if the buffer is full.
+ */
+ void push_back(T value) {
+ if (m_data.size() == 0) {
+ return;
+ }
+
+ m_data[(m_front + m_length) % m_data.size()] = value;
+
+ if (m_length < m_data.size()) {
+ m_length++;
+ } else {
+ // Increment front if buffer is full to maintain size
+ m_front = ModuloInc(m_front);
+ }
+ }
+
+ /**
+ * Push a new value onto the front of the buffer that is constructed with the
+ * provided constructor arguments.
+ *
+ * The value at the back is overwritten if the buffer is full.
+ */
+ template <class... Args>
+ void emplace_front(Args&&... args) {
+ if (m_data.size() == 0) {
+ return;
+ }
+
+ m_front = ModuloDec(m_front);
+
+ m_data[m_front] = T{args...};
+
+ if (m_length < m_data.size()) {
+ m_length++;
+ }
+ }
+
+ /**
+ * Push a new value onto the back of the buffer that is constructed with the
+ * provided constructor arguments.
+ *
+ * The value at the front is overwritten if the buffer is full.
+ */
+ template <class... Args>
+ void emplace_back(Args&&... args) {
+ if (m_data.size() == 0) {
+ return;
+ }
+
+ m_data[(m_front + m_length) % m_data.size()] = T{args...};
+
+ if (m_length < m_data.size()) {
+ m_length++;
+ } else {
+ // Increment front if buffer is full to maintain size
+ m_front = ModuloInc(m_front);
+ }
+ }
+
+ /**
+ * Pop value at front of buffer.
+ *
+ * If there are no elements in the buffer, calling this function results in
+ * undefined behavior.
+ */
+ T pop_front() {
+ T& temp = m_data[m_front];
+ m_front = ModuloInc(m_front);
+ m_length--;
+ return temp;
+ }
+
+ /**
+ * Pop value at back of buffer.
+ *
+ * If there are no elements in the buffer, calling this function results in
+ * undefined behavior.
+ */
+ T pop_back() {
+ m_length--;
+ return m_data[(m_front + m_length) % m_data.size()];
+ }
+
+ /**
+ * Resizes internal buffer to given size.
+ */
void resize(size_t size);
- void reset();
- T& operator[](size_t index);
- const T& operator[](size_t index) const;
+ /**
+ * Empties internal buffer.
+ */
+ void reset() {
+ m_front = 0;
+ m_length = 0;
+ }
+
+ /**
+ * @return Element at index starting from front of buffer.
+ */
+ T& operator[](size_t index) {
+ return m_data[(m_front + index) % m_data.size()];
+ }
+
+ /**
+ * @return Element at index starting from front of buffer.
+ */
+ const T& operator[](size_t index) const {
+ return m_data[(m_front + index) % m_data.size()];
+ }
private:
std::vector<T> m_data;
- T zero_val{0};
-
// Index of element at front of buffer
size_t m_front = 0;
// Number of elements used in buffer
size_t m_length = 0;
- size_t ModuloInc(size_t index);
- size_t ModuloDec(size_t index);
+ /**
+ * Increment an index modulo the length of the buffer.
+ *
+ * @return The result of the modulo operation.
+ */
+ size_t ModuloInc(size_t index) { return (index + 1) % m_data.size(); }
+
+ /**
+ * Decrement an index modulo the length of the buffer.
+ *
+ * @return The result of the modulo operation.
+ */
+ size_t ModuloDec(size_t index) {
+ if (index == 0) {
+ return m_data.size() - 1;
+ } else {
+ return index - 1;
+ }
+ }
};
} // namespace wpi
diff --git a/wpiutil/src/main/native/include/wpi/circular_buffer.inc b/wpiutil/src/main/native/include/wpi/circular_buffer.inc
index 20690b7..038af50 100644
--- a/wpiutil/src/main/native/include/wpi/circular_buffer.inc
+++ b/wpiutil/src/main/native/include/wpi/circular_buffer.inc
@@ -1,138 +1,13 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2015-2020 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. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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 "wpi/circular_buffer.h"
namespace wpi {
-template <class T>
-circular_buffer<T>::circular_buffer(size_t size) : m_data(size, T{0}) {}
-
-/**
- * Returns number of elements in buffer
- */
-template <class T>
-typename circular_buffer<T>::size_type circular_buffer<T>::size() const {
- return m_length;
-}
-
-/**
- * Returns value at front of buffer
- */
-template <class T>
-T& circular_buffer<T>::front() {
- return (*this)[0];
-}
-
-/**
- * Returns value at front of buffer
- */
-template <class T>
-const T& circular_buffer<T>::front() const {
- return (*this)[0];
-}
-
-/**
- * Returns value at back of buffer
- */
-template <class T>
-T& circular_buffer<T>::back() {
- // If there are no elements in the buffer, do nothing
- if (m_length == 0) {
- return zero_val;
- }
-
- return m_data[(m_front + m_length - 1) % m_data.size()];
-}
-
-/**
- * Returns value at back of buffer
- */
-template <class T>
-const T& circular_buffer<T>::back() const {
- // If there are no elements in the buffer, do nothing
- if (m_length == 0) {
- return zero_val;
- }
-
- return m_data[(m_front + m_length - 1) % m_data.size()];
-}
-
-/**
- * Push new value onto front of the buffer. The value at the back is overwritten
- * if the buffer is full.
- */
-template <class T>
-void circular_buffer<T>::push_front(T value) {
- if (m_data.size() == 0) {
- return;
- }
-
- m_front = ModuloDec(m_front);
-
- m_data[m_front] = value;
-
- if (m_length < m_data.size()) {
- m_length++;
- }
-}
-
-/**
- * Push new value onto back of the buffer. The value at the front is overwritten
- * if the buffer is full.
- */
-template <class T>
-void circular_buffer<T>::push_back(T value) {
- if (m_data.size() == 0) {
- return;
- }
-
- m_data[(m_front + m_length) % m_data.size()] = value;
-
- if (m_length < m_data.size()) {
- m_length++;
- } else {
- // Increment front if buffer is full to maintain size
- m_front = ModuloInc(m_front);
- }
-}
-
-/**
- * Pop value at front of buffer.
- */
-template <class T>
-T circular_buffer<T>::pop_front() {
- // If there are no elements in the buffer, do nothing
- if (m_length == 0) {
- return T{0};
- }
-
- T& temp = m_data[m_front];
- m_front = ModuloInc(m_front);
- m_length--;
- return temp;
-}
-
-/**
- * Pop value at back of buffer.
- */
-template <class T>
-T circular_buffer<T>::pop_back() {
- // If there are no elements in the buffer, do nothing
- if (m_length == 0) {
- return T{0};
- }
-
- m_length--;
- return m_data[(m_front + m_length) % m_data.size()];
-}
-
/**
* Resizes internal buffer to given size.
*/
@@ -186,54 +61,4 @@
}
}
-/**
- * Sets internal buffer contents to zero.
- */
-template <class T>
-void circular_buffer<T>::reset() {
- std::fill(m_data.begin(), m_data.end(), T{0});
- m_front = 0;
- m_length = 0;
-}
-
-/**
- * @return Element at index starting from front of buffer.
- */
-template <class T>
-T& circular_buffer<T>::operator[](size_t index) {
- return m_data[(m_front + index) % m_data.size()];
-}
-
-/**
- * @return Element at index starting from front of buffer.
- */
-template <class T>
-const T& circular_buffer<T>::operator[](size_t index) const {
- return m_data[(m_front + index) % m_data.size()];
-}
-
-/**
- * Increment an index modulo the length of the buffer.
- *
- * @return The result of the modulo operation.
- */
-template <class T>
-size_t circular_buffer<T>::ModuloInc(size_t index) {
- return (index + 1) % m_data.size();
-}
-
-/**
- * Decrement an index modulo the length of the buffer.
- *
- * @return The result of the modulo operation.
- */
-template <class T>
-size_t circular_buffer<T>::ModuloDec(size_t index) {
- if (index == 0) {
- return m_data.size() - 1;
- } else {
- return index - 1;
- }
-}
-
} // namespace wpi
diff --git a/wpiutil/src/main/native/include/wpi/condition_variable.h b/wpiutil/src/main/native/include/wpi/condition_variable.h
index 1599e19..7c97ec8 100644
--- a/wpiutil/src/main/native/include/wpi/condition_variable.h
+++ b/wpiutil/src/main/native/include/wpi/condition_variable.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* 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. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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
diff --git a/wpiutil/src/main/native/include/wpi/deprecated.h b/wpiutil/src/main/native/include/wpi/deprecated.h
index 6d77dde..6e6a3bf 100644
--- a/wpiutil/src/main/native/include/wpi/deprecated.h
+++ b/wpiutil/src/main/native/include/wpi/deprecated.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2015-2019 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_DEPRECATED_H_
#define WPIUTIL_WPI_DEPRECATED_H_
diff --git a/wpiutil/src/main/native/include/wpi/fmt/raw_ostream.h b/wpiutil/src/main/native/include/wpi/fmt/raw_ostream.h
new file mode 100644
index 0000000..8779bfc
--- /dev/null
+++ b/wpiutil/src/main/native/include/wpi/fmt/raw_ostream.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.
+
+#ifndef WPIUTIL_WPI_FMT_RAW_OSTREAM_H_
+#define WPIUTIL_WPI_FMT_RAW_OSTREAM_H_
+
+#include "fmt/format.h"
+#include "wpi/raw_ostream.h"
+
+FMT_BEGIN_NAMESPACE
+
+inline void vprint(wpi::raw_ostream& os, string_view format_str,
+ fmt::format_args args) {
+ memory_buffer buffer;
+ detail::vformat_to(buffer, format_str, args);
+ os.write(buffer.data(), buffer.size());
+}
+
+/**
+ * Prints formatted data to the stream *os*.
+ */
+template <typename S, typename... Args>
+void print(wpi::raw_ostream& os, const S& format_str, Args&&... args) {
+ vprint(os, format_str, fmt::make_args_checked<Args...>(format_str, args...));
+}
+
+FMT_END_NAMESPACE
+
+#endif // WPIUTIL_WPI_FMT_RAW_OSTREAM_H_
diff --git a/wpiutil/src/main/native/include/wpi/fs.h b/wpiutil/src/main/native/include/wpi/fs.h
new file mode 100644
index 0000000..fca8069
--- /dev/null
+++ b/wpiutil/src/main/native/include/wpi/fs.h
@@ -0,0 +1,235 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+//===----------------------------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#pragma once
+
+#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 "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*
+using file_t = void*;
+#else
+using file_t = int;
+#endif
+
+extern const file_t kInvalidFile;
+
+enum CreationDisposition : unsigned {
+ /// CD_CreateAlways - When opening a file:
+ /// * If it already exists, truncate it.
+ /// * If it does not already exist, create a new file.
+ CD_CreateAlways = 0,
+
+ /// CD_CreateNew - When opening a file:
+ /// * If it already exists, fail.
+ /// * If it does not already exist, create a new file.
+ CD_CreateNew = 1,
+
+ /// CD_OpenExisting - When opening a file:
+ /// * If it already exists, open the file with the offset set to 0.
+ /// * If it does not already exist, fail.
+ CD_OpenExisting = 2,
+
+ /// CD_OpenAlways - When opening a file:
+ /// * If it already exists, open the file with the offset set to 0.
+ /// * If it does not already exist, create a new file.
+ CD_OpenAlways = 3,
+};
+
+enum FileAccess : unsigned {
+ FA_Read = 1,
+ FA_Write = 2,
+};
+
+enum OpenFlags : unsigned {
+ OF_None = 0,
+ F_None = 0, // For compatibility
+
+ /// The file should be opened in text mode on platforms that make this
+ /// distinction.
+ OF_Text = 1,
+ F_Text = 1, // For compatibility
+
+ /// The file should be opened in append mode.
+ OF_Append = 2,
+ F_Append = 2, // For compatibility
+
+ /// Delete the file on close. Only makes a difference on windows.
+ OF_Delete = 4,
+
+ /// When a child process is launched, this file should remain open in the
+ /// child process.
+ OF_ChildInherit = 8,
+
+ /// Force files Atime to be updated on access. Only makes a difference on
+ /// windows.
+ OF_UpdateAtime = 16,
+};
+
+inline OpenFlags operator|(OpenFlags A, OpenFlags B) {
+ return OpenFlags(unsigned(A) | unsigned(B));
+}
+
+inline OpenFlags& operator|=(OpenFlags& A, OpenFlags B) {
+ A = A | B;
+ return A;
+}
+
+inline FileAccess operator|(FileAccess A, FileAccess B) {
+ return FileAccess(unsigned(A) | unsigned(B));
+}
+
+inline FileAccess& operator|=(FileAccess& A, FileAccess B) {
+ A = A | B;
+ return A;
+}
+
+/**
+ * Opens a file with the specified creation disposition, access mode,
+ * and flags and returns a platform-specific file object.
+ *
+ * The caller is responsible for closing the file object once they are
+ * finished with it.
+ *
+ * @param Path The path of the file to open, relative or absolute.
+ * @param EC Error code output, set to non-zero on error
+ * @param Disp Value specifying the existing-file behavior
+ * @param Access Value specifying whether to open the file in read, write, or
+ * read-write mode.
+ * @param Flags Additional flags.
+ * @param Mode The access permissions of the file, represented in octal.
+ * @returns errc::success if \a Name has been opened, otherwise a
+ * platform-specific error_code.
+ */
+file_t OpenFile(const path& Path, std::error_code& EC, CreationDisposition Disp,
+ FileAccess Access, OpenFlags Flags, unsigned Mode = 0666);
+
+/**
+ * @brief Opens the file with the given name in a write-only or read-write
+ * mode, returning its open file descriptor. If the file does not exist, it
+ * is created.
+ *
+ * The caller is responsible for closing the freeing the file once they are
+ * finished with it.
+ *
+ * @param Path The path of the file to open, relative or absolute.
+ * @param EC Error code output, set to non-zero on error
+ * @param Disp Value specifying the existing-file behavior
+ * @param Flags Additional flags used to determine whether the file should be
+ * opened in, for example, read-write or in write-only mode.
+ * @param Mode The access permissions of the file, represented in octal.
+ * @returns a platform-specific file descriptor if \a Name has been opened,
+ * otherwise kInvalidFile.
+ */
+inline file_t OpenFileForWrite(const path& Path, std::error_code& EC,
+ CreationDisposition Disp, OpenFlags Flags,
+ unsigned Mode = 0666) {
+ return OpenFile(Path, EC, Disp, FA_Write, Flags, Mode);
+}
+
+/**
+ * @brief Opens the file with the given name in a write-only or read-write
+ * mode, returning its open file descriptor. If the file does not exist, it
+ * is created.
+ *
+ * The caller is responsible for closing the freeing the file once they are
+ * finished with it.
+ *
+ * @param Path The path of the file to open, relative or absolute.
+ * @param EC Error code output, set to non-zero on error
+ * @param Disp Value specifying the existing-file behavior
+ * @param Flags Additional flags used to determine whether the file should be
+ * opened in, for example, read-write or in write-only mode.
+ * @param Mode The access permissions of the file, represented in octal.
+ * @return a platform-specific file descriptor if \a Name has been opened,
+ * otherwise kInvalidFile.
+ */
+inline file_t OpenFileForReadWrite(const path& Path, std::error_code& EC,
+ CreationDisposition Disp, OpenFlags Flags,
+ unsigned Mode = 0666) {
+ return OpenFile(Path, EC, Disp, FA_Write | FA_Read, Flags, Mode);
+}
+
+/**
+ * Opens the file with the given name in a read-only mode, returning
+ * its open file descriptor.
+ *
+ * The caller is responsible for closing the freeing the file once they are
+ * finished with it.
+ *
+ * @param Path The path of the file to open, relative or absolute.
+ * @param EC Error code output, set to non-zero on error
+ * @param Flags Additional flags
+ * @return a platform-specific file descriptor if \a Name has been opened,
+ * otherwise kInvalidFile.
+ */
+file_t OpenFileForRead(const path& Path, std::error_code& EC,
+ OpenFlags Flags = OF_None);
+
+/**
+ * Converts a file object to a file descriptor. The returned file descriptor
+ * must be closed with ::close() instead of CloseFile().
+ *
+ * @param F On input, this is the file to convert to a file descriptor.
+ * On output, the file is set to kInvalidFile.
+ * @param EC Error code output, set to non-zero on error
+ * @param Flags Flags passed to the OpenFile function that created file_t
+ * @return file descriptor, or -1 on error
+ */
+int FileToFd(file_t& F, std::error_code& EC, OpenFlags Flags);
+
+/**
+ * Closes the file object.
+ *
+ * @param F On input, this is the file to close. On output, the file is
+ * set to kInvalidFile.
+ */
+void CloseFile(file_t& F);
+
+} // namespace fs
diff --git a/wpiutil/src/main/native/include/wpi/function_ref.h b/wpiutil/src/main/native/include/wpi/function_ref.h
new file mode 100644
index 0000000..868c164
--- /dev/null
+++ b/wpiutil/src/main/native/include/wpi/function_ref.h
@@ -0,0 +1,66 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+//===- llvm/ADT/STLExtras.h - Useful STL related functions ------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef WPIUTIL_WPI_FUNCTION_REF_H_
+#define WPIUTIL_WPI_FUNCTION_REF_H_
+
+#include <stdint.h>
+
+#include <type_traits>
+#include <utility>
+
+namespace wpi {
+
+/// An efficient, type-erasing, non-owning reference to a callable. This is
+/// intended for use as the type of a function parameter that is not used
+/// after the function in question returns.
+///
+/// This class does not own the callable, so it is not in general safe to store
+/// a function_ref.
+template <typename Fn>
+class function_ref;
+
+template <typename Ret, typename... Params>
+class function_ref<Ret(Params...)> {
+ Ret (*callback)(intptr_t callable, Params... params) = nullptr;
+ intptr_t callable;
+
+ template <typename Callable>
+ static Ret callback_fn(intptr_t callable, Params... params) {
+ return (*reinterpret_cast<Callable*>(callable))(std::forward<Params>(
+ params)...);
+ }
+
+ public:
+ function_ref() = default;
+ /*implicit*/ function_ref(std::nullptr_t) {} // NOLINT
+
+ template <typename Callable>
+ /*implicit*/ function_ref( // NOLINT
+ Callable&& callable,
+ typename std::enable_if<
+ !std::is_same<typename std::remove_reference<Callable>::type,
+ function_ref>::value>::type* = nullptr)
+ : callback(callback_fn<typename std::remove_reference<Callable>::type>),
+ callable(reinterpret_cast<intptr_t>(&callable)) {}
+
+ Ret operator()(Params... params) const {
+ return callback(callable, std::forward<Params>(params)...);
+ }
+
+ explicit operator bool() const { return callback; }
+};
+
+} // namespace wpi
+
+#endif // WPIUTIL_WPI_FUNCTION_REF_H_
diff --git a/wpiutil/src/main/native/include/wpi/future.h b/wpiutil/src/main/native/include/wpi/future.h
index db46d58..5cb4a8d 100644
--- a/wpiutil/src/main/native/include/wpi/future.h
+++ b/wpiutil/src/main/native/include/wpi/future.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_FUTURE_H_
#define WPIUTIL_WPI_FUTURE_H_
@@ -290,14 +287,16 @@
future(const future&) = delete;
template <typename R>
- future(future<R>&& oth) noexcept
+ future(future<R>&& oth) noexcept // NOLINT
: future(oth.then([](R&& val) -> T { return val; })) {}
/**
* Ignores the result of the future if it has not been retrieved.
*/
~future() {
- if (m_promises) m_promises->IgnoreResult(m_request);
+ if (m_promises) {
+ m_promises->IgnoreResult(m_request);
+ }
}
future& operator=(future&& oth) noexcept {
@@ -316,10 +315,11 @@
* @return The value provided by the corresponding promise.set_value().
*/
T get() {
- if (m_promises)
+ if (m_promises) {
return m_promises->GetResult(m_request);
- else
+ } else {
return T();
+ }
}
template <typename R, typename F>
@@ -358,7 +358,9 @@
* If the value has already been provided, returns immediately.
*/
void wait() const {
- if (m_promises) m_promises->WaitResult(m_request);
+ if (m_promises) {
+ m_promises->WaitResult(m_request);
+ }
}
/**
@@ -419,7 +421,9 @@
* Ignores the result of the future if it has not been retrieved.
*/
~future() {
- if (m_promises) m_promises->IgnoreResult(m_request);
+ if (m_promises) {
+ m_promises->IgnoreResult(m_request);
+ }
}
future& operator=(future&& oth) noexcept {
@@ -436,7 +440,9 @@
* Can only be called once. The future will be marked invalid after the call.
*/
void get() {
- if (m_promises) m_promises->GetResult(m_request);
+ if (m_promises) {
+ m_promises->GetResult(m_request);
+ }
}
template <typename R, typename F>
@@ -474,7 +480,9 @@
* If the value has already been provided, returns immediately.
*/
void wait() const {
- if (m_promises) m_promises->WaitResult(m_request);
+ if (m_promises) {
+ m_promises->WaitResult(m_request);
+ }
}
/**
@@ -540,7 +548,9 @@
* Sets the promised value to a default-constructed T if not already set.
*/
~promise() {
- if (m_promises) m_promises->SetValue(m_request, T());
+ if (m_promises) {
+ m_promises->SetValue(m_request, T());
+ }
}
promise& operator=(promise&& oth) noexcept {
@@ -575,7 +585,9 @@
* @param value The value to provide to the waiting future
*/
void set_value(const T& value) {
- if (m_promises) m_promises->SetValue(m_request, value);
+ if (m_promises) {
+ m_promises->SetValue(m_request, value);
+ }
m_promises = nullptr;
}
@@ -586,7 +598,9 @@
* @param value The value to provide to the waiting future
*/
void set_value(T&& value) {
- if (m_promises) m_promises->SetValue(m_request, std::move(value));
+ if (m_promises) {
+ m_promises->SetValue(m_request, std::move(value));
+ }
m_promises = nullptr;
}
@@ -625,7 +639,9 @@
* Sets the promised value if not already set.
*/
~promise() {
- if (m_promises) m_promises->SetValue(m_request);
+ if (m_promises) {
+ m_promises->SetValue(m_request);
+ }
}
promise& operator=(promise&& oth) noexcept {
@@ -660,7 +676,9 @@
* Only effective once (subsequent calls will be ignored).
*/
void set_value() {
- if (m_promises) m_promises->SetValue(m_request);
+ if (m_promises) {
+ m_promises->SetValue(m_request);
+ }
m_promises = nullptr;
}
@@ -710,7 +728,9 @@
template <typename T>
void PromiseFactory<T>::SetValue(uint64_t request, const T& value) {
std::unique_lock lock(GetResultMutex());
- if (!EraseRequest(request)) return;
+ if (!EraseRequest(request)) {
+ return;
+ }
auto it = std::find_if(m_thens.begin(), m_thens.end(),
[=](const auto& x) { return x.request == request; });
if (it != m_thens.end()) {
@@ -729,7 +749,9 @@
template <typename T>
void PromiseFactory<T>::SetValue(uint64_t request, T&& value) {
std::unique_lock lock(GetResultMutex());
- if (!EraseRequest(request)) return;
+ if (!EraseRequest(request)) {
+ return;
+ }
auto it = std::find_if(m_thens.begin(), m_thens.end(),
[=](const auto& x) { return x.request == request; });
if (it != m_thens.end()) {
@@ -796,7 +818,9 @@
// Did we get a response to *our* request?
auto it = std::find_if(m_results.begin(), m_results.end(),
[=](const auto& r) { return r.first == request; });
- if (it != m_results.end()) return;
+ if (it != m_results.end()) {
+ return;
+ }
// No, keep waiting for a response
Wait(lock);
}
@@ -813,10 +837,16 @@
// Did we get a response to *our* request?
auto it = std::find_if(m_results.begin(), m_results.end(),
[=](const auto& r) { return r.first == request; });
- if (it != m_results.end()) return true;
- if (timeout) break;
+ if (it != m_results.end()) {
+ return true;
+ }
+ if (timeout) {
+ break;
+ }
// No, keep waiting for a response
- if (!WaitUntil(lock, timeout_time)) timeout = true;
+ if (!WaitUntil(lock, timeout_time)) {
+ timeout = true;
+ }
}
return false;
}
@@ -845,10 +875,16 @@
// Did we get a response to *our* request?
auto it = std::find_if(m_results.begin(), m_results.end(),
[=](const auto& r) { return r == request; });
- if (it != m_results.end()) return true;
- if (timeout) break;
+ if (it != m_results.end()) {
+ return true;
+ }
+ if (timeout) {
+ break;
+ }
// No, keep waiting for a response
- if (!WaitUntil(lock, timeout_time)) timeout = true;
+ if (!WaitUntil(lock, timeout_time)) {
+ timeout = true;
+ }
}
return false;
}
diff --git a/wpiutil/src/main/native/include/wpi/ghc/filesystem.hpp b/wpiutil/src/main/native/include/wpi/ghc/filesystem.hpp
new file mode 100644
index 0000000..6cbc892
--- /dev/null
+++ b/wpiutil/src/main/native/include/wpi/ghc/filesystem.hpp
@@ -0,0 +1,5942 @@
+//---------------------------------------------------------------------------------------
+//
+// 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/wpiutil/src/main/native/include/wpi/hostname.h b/wpiutil/src/main/native/include/wpi/hostname.h
index b2f9f0c..bd93b4a 100644
--- a/wpiutil/src/main/native/include/wpi/hostname.h
+++ b/wpiutil/src/main/native/include/wpi/hostname.h
@@ -1,23 +1,19 @@
-/*----------------------------------------------------------------------------*/
-/* 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. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_HOSTNAME_H_
#define WPIUTIL_WPI_HOSTNAME_H_
#include <string>
-
-#include "wpi/StringRef.h"
+#include <string_view>
namespace wpi {
template <typename T>
class SmallVectorImpl;
std::string GetHostname();
-StringRef GetHostname(SmallVectorImpl<char>& name);
+std::string_view GetHostname(SmallVectorImpl<char>& name);
} // namespace wpi
#endif // WPIUTIL_WPI_HOSTNAME_H_
diff --git a/wpiutil/src/main/native/include/wpi/jni_util.h b/wpiutil/src/main/native/include/wpi/jni_util.h
index 15f59be..195e37e 100644
--- a/wpiutil/src/main/native/include/wpi/jni_util.h
+++ b/wpiutil/src/main/native/include/wpi/jni_util.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-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. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_JNI_UTIL_H_
#define WPIUTIL_WPI_JNI_UTIL_H_
@@ -12,48 +9,61 @@
#include <queue>
#include <string>
+#include <string_view>
#include <type_traits>
#include <utility>
#include <vector>
-#include "wpi/ArrayRef.h"
#include "wpi/ConvertUTF.h"
#include "wpi/SafeThread.h"
#include "wpi/SmallString.h"
#include "wpi/SmallVector.h"
-#include "wpi/StringRef.h"
-#include "wpi/deprecated.h"
+#include "wpi/StringExtras.h"
#include "wpi/mutex.h"
#include "wpi/raw_ostream.h"
-
-/** WPILib C++ utilities (wpiutil) namespace */
-namespace wpi {
+#include "wpi/span.h"
/** Java Native Interface (JNI) utility functions */
-namespace java {
+namespace wpi::java {
-// Gets a Java stack trace. Also provides the last function
-// in the stack trace not starting with excludeFuncPrefix (useful for e.g.
-// finding the first user call to a series of library functions).
+/**
+ * Gets a Java stack trace.
+ *
+ * Also provides the last function in the stack trace not starting with
+ * excludeFuncPrefix (useful for e.g. finding the first user call to a series of
+ * library functions).
+ *
+ * @param env JRE environment.
+ * @param func Storage for last function in the stack trace not starting with
+ * excludeFuncPrefix.
+ * @param excludeFuncPrefix Prefix for functions to ignore in stack trace.
+ */
std::string GetJavaStackTrace(JNIEnv* env, std::string* func = nullptr,
- StringRef excludeFuncPrefix = StringRef());
+ std::string_view excludeFuncPrefix = {});
-// Finds a class and keep it as a global reference.
-// Use with caution, as the destructor does NOT call DeleteGlobalRef due
-// to potential shutdown issues with doing so.
+/**
+ * Finds a class and keeps it as a global reference.
+ *
+ * Use with caution, as the destructor does NOT call DeleteGlobalRef due to
+ * potential shutdown issues with doing so.
+ */
class JClass {
public:
JClass() = default;
JClass(JNIEnv* env, const char* name) {
jclass local = env->FindClass(name);
- if (!local) return;
+ if (!local) {
+ return;
+ }
m_cls = static_cast<jclass>(env->NewGlobalRef(local));
env->DeleteLocalRef(local);
}
void free(JNIEnv* env) {
- if (m_cls) env->DeleteGlobalRef(m_cls);
+ if (m_cls) {
+ env->DeleteGlobalRef(m_cls);
+ }
m_cls = nullptr;
}
@@ -80,20 +90,25 @@
}
void free(JNIEnv* env) {
- if (m_cls) env->DeleteGlobalRef(m_cls);
+ if (m_cls) {
+ env->DeleteGlobalRef(m_cls);
+ }
m_cls = nullptr;
}
explicit operator bool() const { return m_cls; }
- operator T() const { return m_cls; }
+ operator T() const { return m_cls; } // NOLINT
protected:
T m_cls = nullptr;
};
-// Container class for cleaning up Java local references.
-// The destructor calls DeleteLocalRef.
+/**
+ * Container class for cleaning up Java local references.
+ *
+ * The destructor calls DeleteLocalRef.
+ */
template <typename T>
class JLocal {
public:
@@ -110,9 +125,11 @@
return *this;
}
~JLocal() {
- if (m_obj) m_env->DeleteLocalRef(m_obj);
+ if (m_obj) {
+ m_env->DeleteLocalRef(m_obj);
+ }
}
- operator T() { return m_obj; }
+ operator T() { return m_obj; } // NOLINT
T obj() { return m_obj; }
private:
@@ -124,9 +141,12 @@
// Conversions from Java objects to C++
//
-// Java string (jstring) reference. The string is provided as UTF8.
-// This is not actually a reference, as it makes a copy of the string
-// characters, but it's named this way for consistency.
+/**
+ * Java string (jstring) reference.
+ *
+ * The string is provided as UTF8. This is not actually a reference, as it makes
+ * a copy of the string characters, but it's named this way for consistency.
+ */
class JStringRef {
public:
JStringRef(JNIEnv* env, jstring str) {
@@ -134,7 +154,7 @@
jsize size = env->GetStringLength(str);
const jchar* chars = env->GetStringCritical(str, nullptr);
if (chars) {
- convertUTF16ToUTF8String(makeArrayRef(chars, size), m_str);
+ convertUTF16ToUTF8String(wpi::span<const jchar>(chars, size), m_str);
env->ReleaseStringCritical(str, chars);
}
} else {
@@ -146,8 +166,8 @@
m_str.pop_back();
}
- operator StringRef() const { return m_str; }
- StringRef str() const { return m_str; }
+ operator std::string_view() const { return m_str.str(); } // NOLINT
+ std::string_view str() const { return m_str.str(); }
const char* c_str() const { return m_str.data(); }
size_t size() const { return m_str.size(); }
@@ -161,32 +181,44 @@
template <typename C, typename T>
class JArrayRefInner {};
-// Specialization of JArrayRefBase to provide StringRef conversion.
+/**
+ * Specialization of JArrayRefBase to provide std::string_view conversion.
+ */
template <typename C>
class JArrayRefInner<C, jbyte> {
public:
- operator StringRef() const { return str(); }
+ operator std::string_view() const { return str(); }
- StringRef str() const {
+ std::string_view str() const {
auto arr = static_cast<const C*>(this)->array();
- if (arr.empty()) return StringRef{};
- return StringRef{reinterpret_cast<const char*>(arr.data()), arr.size()};
+ if (arr.empty()) {
+ return {};
+ }
+ return {reinterpret_cast<const char*>(arr.data()), arr.size()};
}
};
-// Base class for J*ArrayRef and CriticalJ*ArrayRef
+/**
+ * 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; }
- operator ArrayRef<T>() const { return array(); }
+ operator span<const T>() const { return array(); } // NOLINT
- ArrayRef<T> array() const {
- if (!this->m_elements) return ArrayRef<T>{};
- return ArrayRef<T>{this->m_elements, this->m_size};
+ span<const T> array() const {
+ if (!this->m_elements) {
+ return {};
+ }
+ return {this->m_elements, this->m_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;
@@ -245,30 +277,34 @@
env, \
static_cast<T*>(bb ? env->GetDirectBufferAddress(bb) : nullptr), \
len) { \
- if (!bb) \
+ 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) \
+ if (jarr) { \
m_elements = env->Get##F##ArrayElements(jarr, nullptr); \
- else \
+ } 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) \
+ if (jarr) { \
m_elements = env->Get##F##ArrayElements(jarr, nullptr); \
- else \
+ } else { \
errs() << "JArrayRef was passed a null pointer at \n" \
<< GetJavaStackTrace(env); \
+ } \
} \
~J##F##ArrayRef() { \
- if (m_jarr && m_elements) \
+ if (m_jarr && m_elements) { \
m_env->Release##F##ArrayElements(static_cast<T##Array>(m_jarr), \
m_elements, JNI_ABORT); \
+ } \
} \
}; \
\
@@ -276,25 +312,28 @@
public: \
CriticalJ##F##ArrayRef(JNIEnv* env, T##Array jarr, int len) \
: detail::JArrayRefBase<T>(env, jarr, len) { \
- if (jarr) \
+ if (jarr) { \
m_elements = \
static_cast<T*>(env->GetPrimitiveArrayCritical(jarr, nullptr)); \
- else \
+ } 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) \
+ if (jarr) { \
m_elements = \
static_cast<T*>(env->GetPrimitiveArrayCritical(jarr, nullptr)); \
- else \
+ } else { \
errs() << "JArrayRef was passed a null pointer at \n" \
<< GetJavaStackTrace(env); \
+ } \
} \
~CriticalJ##F##ArrayRef() { \
- if (m_jarr && m_elements) \
+ if (m_jarr && m_elements) { \
m_env->ReleasePrimitiveArrayCritical(m_jarr, m_elements, JNI_ABORT); \
+ } \
} \
};
@@ -312,8 +351,13 @@
// Conversions from C++ to Java objects
//
-// Convert a UTF8 string into a jstring.
-inline jstring MakeJString(JNIEnv* env, StringRef str) {
+/**
+ * Convert a UTF8 string into a jstring.
+ *
+ * @param env JRE environment.
+ * @param str String to convert.
+ */
+inline jstring MakeJString(JNIEnv* env, std::string_view str) {
SmallVector<UTF16, 128> chars;
convertUTF8ToUTF16String(str, chars);
return env->NewString(chars.begin(), chars.size());
@@ -322,31 +366,43 @@
// 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).
+/**
+ * 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))>
struct ConvertIntArray {
- static jintArray ToJava(JNIEnv* env, ArrayRef<T> arr) {
+ static jintArray ToJava(JNIEnv* env, span<const T> arr) {
jintArray jarr = env->NewIntArray(arr.size());
- if (!jarr) return nullptr;
+ 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)
+ 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)
+/**
+ * Fast path (use SetIntArrayRegion).
+ */
template <typename T>
struct ConvertIntArray<T, true> {
- static jintArray ToJava(JNIEnv* env, ArrayRef<T> arr) {
+ static jintArray ToJava(JNIEnv* env, span<const T> arr) {
jintArray jarr = env->NewIntArray(arr.size());
- if (!jarr) return nullptr;
+ if (!jarr) {
+ return nullptr;
+ }
env->SetIntArrayRegion(jarr, 0, arr.size(),
reinterpret_cast<const jint*>(arr.data()));
return jarr;
@@ -355,69 +411,128 @@
} // namespace detail
-// Convert an ArrayRef to a jintArray.
+/**
+ * Convert a span to a jintArray.
+ *
+ * @param env JRE environment.
+ * @param arr Span to convert.
+ */
template <typename T>
-inline jintArray MakeJIntArray(JNIEnv* env, ArrayRef<T> arr) {
+inline jintArray MakeJIntArray(JNIEnv* env, span<const T> arr) {
return detail::ConvertIntArray<T>::ToJava(env, arr);
}
-// Convert a SmallVector to a jintArray. This is required in addition to
-// ArrayRef because template resolution occurs prior to implicit conversions.
+/**
+ * Convert a span to a jintArray.
+ *
+ * @param env JRE environment.
+ * @param arr Span to convert.
+ */
+template <typename T>
+inline jintArray MakeJIntArray(JNIEnv* env, span<T> arr) {
+ return detail::ConvertIntArray<T>::ToJava(env, arr);
+}
+
+/**
+ * Convert a SmallVector to a jintArray.
+ *
+ * This is required in addition to ArrayRef because template resolution occurs
+ * prior to implicit conversions.
+ *
+ * @param env JRE environment.
+ * @param arr SmallVector to convert.
+ */
template <typename T>
inline jintArray MakeJIntArray(JNIEnv* env, const SmallVectorImpl<T>& arr) {
return detail::ConvertIntArray<T>::ToJava(env, arr);
}
-// Convert a std::vector to a jintArray. This is required in addition to
-// ArrayRef because template resolution occurs prior to implicit conversions.
+/**
+ * Convert a std::vector to a jintArray.
+ *
+ * This is required in addition to ArrayRef because template resolution occurs
+ * prior to implicit conversions.
+ *
+ * @param env JRE environment.
+ * @param arr SmallVector to convert.
+ */
template <typename T>
inline jintArray MakeJIntArray(JNIEnv* env, const std::vector<T>& arr) {
return detail::ConvertIntArray<T>::ToJava(env, arr);
}
-// Convert a StringRef into a jbyteArray.
-inline jbyteArray MakeJByteArray(JNIEnv* env, StringRef str) {
+/**
+ * Convert a std::string_view into a jbyteArray.
+ *
+ * @param env JRE environment.
+ * @param str std::string_view to convert.
+ */
+inline jbyteArray MakeJByteArray(JNIEnv* env, std::string_view str) {
jbyteArray jarr = env->NewByteArray(str.size());
- if (!jarr) return nullptr;
+ if (!jarr) {
+ return nullptr;
+ }
env->SetByteArrayRegion(jarr, 0, str.size(),
reinterpret_cast<const jbyte*>(str.data()));
return jarr;
}
-// Convert an array of integers into a jbooleanArray.
-inline jbooleanArray MakeJBooleanArray(JNIEnv* env, ArrayRef<int> arr) {
+/**
+ * Convert an array of integers into a jbooleanArray.
+ *
+ * @param env JRE environment.
+ * @param arr Array to convert.
+ */
+inline jbooleanArray MakeJBooleanArray(JNIEnv* env, span<const int> arr) {
jbooleanArray jarr = env->NewBooleanArray(arr.size());
- if (!jarr) return nullptr;
+ if (!jarr) {
+ return nullptr;
+ }
jboolean* elements =
static_cast<jboolean*>(env->GetPrimitiveArrayCritical(jarr, nullptr));
- if (!elements) return nullptr;
- for (size_t i = 0; i < arr.size(); ++i)
+ if (!elements) {
+ return nullptr;
+ }
+ for (size_t i = 0; i < arr.size(); ++i) {
elements[i] = arr[i] ? JNI_TRUE : JNI_FALSE;
+ }
env->ReleasePrimitiveArrayCritical(jarr, elements, 0);
return jarr;
}
-// Convert an array of booleans into a jbooleanArray.
-inline jbooleanArray MakeJBooleanArray(JNIEnv* env, ArrayRef<bool> arr) {
+/**
+ * Convert an array of booleans into a jbooleanArray.
+ *
+ * @param env JRE environment.
+ * @param arr Array to convert.
+ */
+inline jbooleanArray MakeJBooleanArray(JNIEnv* env, span<const bool> arr) {
jbooleanArray jarr = env->NewBooleanArray(arr.size());
- if (!jarr) return nullptr;
+ if (!jarr) {
+ return nullptr;
+ }
jboolean* elements =
static_cast<jboolean*>(env->GetPrimitiveArrayCritical(jarr, nullptr));
- if (!elements) return nullptr;
- for (size_t i = 0; i < arr.size(); ++i)
+ if (!elements) {
+ return nullptr;
+ }
+ for (size_t i = 0; i < arr.size(); ++i) {
elements[i] = arr[i] ? JNI_TRUE : JNI_FALSE;
+ }
env->ReleasePrimitiveArrayCritical(jarr, elements, 0);
return jarr;
}
// Other MakeJ*Array conversions.
-#define WPI_JNI_MAKEJARRAY(T, F) \
- inline T##Array MakeJ##F##Array(JNIEnv* env, ArrayRef<T> arr) { \
- T##Array jarr = env->New##F##Array(arr.size()); \
- if (!jarr) return nullptr; \
- env->Set##F##ArrayRegion(jarr, 0, arr.size(), arr.data()); \
- return jarr; \
+#define WPI_JNI_MAKEJARRAY(T, F) \
+ inline T##Array MakeJ##F##Array(JNIEnv* env, span<const T> arr) { \
+ T##Array jarr = env->New##F##Array(arr.size()); \
+ if (!jarr) { \
+ return nullptr; \
+ } \
+ env->Set##F##ArrayRegion(jarr, 0, arr.size(), arr.data()); \
+ return jarr; \
}
WPI_JNI_MAKEJARRAY(jboolean, Boolean)
@@ -429,12 +544,21 @@
#undef WPI_JNI_MAKEJARRAY
-// Convert an array of std::string into a jarray of jstring.
-inline jobjectArray MakeJStringArray(JNIEnv* env, ArrayRef<std::string> arr) {
+/**
+ * Convert an array of std::string into a jarray of jstring.
+ *
+ * @param env JRE environment.
+ * @param arr Array to convert.
+ */
+inline jobjectArray MakeJStringArray(JNIEnv* env, span<const std::string> arr) {
static JClass stringCls{env, "java/lang/String"};
- if (!stringCls) return nullptr;
+ if (!stringCls) {
+ return nullptr;
+ }
jobjectArray jarr = env->NewObjectArray(arr.size(), stringCls, nullptr);
- if (!jarr) return nullptr;
+ if (!jarr) {
+ return nullptr;
+ }
for (size_t i = 0; i < arr.size(); ++i) {
JLocal<jstring> elem{env, MakeJString(env, arr[i])};
env->SetObjectArrayElement(jarr, i, elem.obj());
@@ -442,25 +566,27 @@
return jarr;
}
-// Generic callback thread implementation.
-//
-// JNI's AttachCurrentThread() creates a Java Thread object on every
-// invocation, which is both time inefficient and causes issues with Eclipse
-// (which tries to keep a thread list up-to-date and thus gets swamped).
-//
-// Instead, this class attaches just once. When a hardware notification
-// occurs, a condition variable wakes up this thread and this thread actually
-// makes the call into Java.
-//
-// The template parameter T is the message being passed to the callback, but
-// also needs to provide the following functions:
-// static JavaVM* GetJVM();
-// static const char* GetName();
-// void CallJava(JNIEnv *env, jobject func, jmethodID mid);
+/**
+ * Generic callback thread implementation.
+ *
+ * JNI's AttachCurrentThread() creates a Java Thread object on every
+ * invocation, which is both time inefficient and causes issues with Eclipse
+ * (which tries to keep a thread list up-to-date and thus gets swamped).
+ *
+ * Instead, this class attaches just once. When a hardware notification
+ * occurs, a condition variable wakes up this thread and this thread actually
+ * makes the call into Java.
+ *
+ * The template parameter T is the message being passed to the callback, but
+ * also needs to provide the following functions:
+ * static JavaVM* GetJVM();
+ * static const char* GetName();
+ * void CallJava(JNIEnv *env, jobject func, jmethodID mid);
+ */
template <typename T>
class JCallbackThread : public SafeThread {
public:
- void Main();
+ void Main() override;
std::queue<T> m_queue;
jobject m_func = nullptr;
@@ -480,9 +606,13 @@
template <typename T>
void JCallbackManager<T>::SetFunc(JNIEnv* env, jobject func, jmethodID mid) {
auto thr = this->GetThread();
- if (!thr) return;
+ if (!thr) {
+ return;
+ }
// free global reference
- if (thr->m_func) env->DeleteGlobalRef(thr->m_func);
+ if (thr->m_func) {
+ env->DeleteGlobalRef(thr->m_func);
+ }
// create global reference
thr->m_func = env->NewGlobalRef(func);
thr->m_mid = mid;
@@ -492,7 +622,9 @@
template <typename... Args>
void JCallbackManager<T>::Send(Args&&... args) {
auto thr = this->GetThread();
- if (!thr) return;
+ if (!thr) {
+ return;
+ }
thr->m_queue.emplace(std::forward<Args>(args)...);
thr->m_cond.notify_one();
}
@@ -506,14 +638,20 @@
args.group = nullptr;
jint rs = T::GetJVM()->AttachCurrentThreadAsDaemon(
reinterpret_cast<void**>(&env), &args);
- if (rs != JNI_OK) return;
+ if (rs != JNI_OK) {
+ return;
+ }
std::unique_lock lock(m_mutex);
while (m_active) {
m_cond.wait(lock, [&] { return !(m_active && m_queue.empty()); });
- if (!m_active) break;
+ if (!m_active) {
+ break;
+ }
while (!m_queue.empty()) {
- if (!m_active) break;
+ if (!m_active) {
+ break;
+ }
auto item = std::move(m_queue.front());
m_queue.pop();
auto func = m_func;
@@ -529,7 +667,9 @@
}
JavaVM* jvm = T::GetJVM();
- if (jvm) jvm->DetachCurrentThread();
+ if (jvm) {
+ jvm->DetachCurrentThread();
+ }
}
template <typename T>
@@ -541,41 +681,125 @@
}
};
-inline std::string GetJavaStackTrace(JNIEnv* env, std::string* func,
- StringRef excludeFuncPrefix) {
+inline std::string GetJavaStackTrace(JNIEnv* env, std::string_view skipPrefix) {
// create a throwable
static JClass throwableCls(env, "java/lang/Throwable");
- if (!throwableCls) return "";
+ if (!throwableCls) {
+ return "";
+ }
static jmethodID constructorId = nullptr;
- if (!constructorId)
+ if (!constructorId) {
constructorId = env->GetMethodID(throwableCls, "<init>", "()V");
+ }
JLocal<jobject> throwable(env, env->NewObject(throwableCls, constructorId));
// retrieve information from the exception.
// get method id
// getStackTrace returns an array of StackTraceElement
static jmethodID getStackTraceId = nullptr;
- if (!getStackTraceId)
+ if (!getStackTraceId) {
getStackTraceId = env->GetMethodID(throwableCls, "getStackTrace",
"()[Ljava/lang/StackTraceElement;");
+ }
// call getStackTrace
JLocal<jobjectArray> stackTrace(
env, static_cast<jobjectArray>(
env->CallObjectMethod(throwable, getStackTraceId)));
- if (!stackTrace) return "";
+ if (!stackTrace) {
+ return "";
+ }
// get length of the array
jsize stackTraceLength = env->GetArrayLength(stackTrace);
// get toString methodId of StackTraceElement class
static JClass stackTraceElementCls(env, "java/lang/StackTraceElement");
- if (!stackTraceElementCls) return "";
+ if (!stackTraceElementCls) {
+ return "";
+ }
static jmethodID toStringId = nullptr;
- if (!toStringId)
+ if (!toStringId) {
toStringId = env->GetMethodID(stackTraceElementCls, "toString",
"()Ljava/lang/String;");
+ }
+
+ bool foundFirst = false;
+ std::string buf;
+ raw_string_ostream oss(buf);
+ for (jsize i = 0; i < stackTraceLength; i++) {
+ // add the result of toString method of each element in the result
+ JLocal<jobject> curStackTraceElement(
+ env, env->GetObjectArrayElement(stackTrace, i));
+
+ // call to string on the object
+ JLocal<jstring> stackElementString(
+ env, static_cast<jstring>(
+ env->CallObjectMethod(curStackTraceElement, toStringId)));
+
+ if (!stackElementString) {
+ return "";
+ }
+
+ // add a line to res
+ JStringRef elem(env, stackElementString);
+ if (!foundFirst) {
+ if (wpi::starts_with(elem, skipPrefix)) {
+ continue;
+ }
+ foundFirst = true;
+ }
+ oss << "\tat " << elem << '\n';
+ }
+
+ return oss.str();
+}
+
+inline std::string GetJavaStackTrace(JNIEnv* env, std::string* func,
+ std::string_view excludeFuncPrefix) {
+ // create a throwable
+ static JClass throwableCls(env, "java/lang/Throwable");
+ if (!throwableCls) {
+ return "";
+ }
+ static jmethodID constructorId = nullptr;
+ if (!constructorId) {
+ constructorId = env->GetMethodID(throwableCls, "<init>", "()V");
+ }
+ JLocal<jobject> throwable(env, env->NewObject(throwableCls, constructorId));
+
+ // retrieve information from the exception.
+ // get method id
+ // getStackTrace returns an array of StackTraceElement
+ static jmethodID getStackTraceId = nullptr;
+ if (!getStackTraceId) {
+ getStackTraceId = env->GetMethodID(throwableCls, "getStackTrace",
+ "()[Ljava/lang/StackTraceElement;");
+ }
+
+ // call getStackTrace
+ JLocal<jobjectArray> stackTrace(
+ env, static_cast<jobjectArray>(
+ env->CallObjectMethod(throwable, getStackTraceId)));
+
+ if (!stackTrace) {
+ return "";
+ }
+
+ // get length of the array
+ jsize stackTraceLength = env->GetArrayLength(stackTrace);
+
+ // get toString methodId of StackTraceElement class
+ static JClass stackTraceElementCls(env, "java/lang/StackTraceElement");
+ if (!stackTraceElementCls) {
+ return "";
+ }
+ static jmethodID toStringId = nullptr;
+ if (!toStringId) {
+ toStringId = env->GetMethodID(stackTraceElementCls, "toString",
+ "()Ljava/lang/String;");
+ }
bool haveLoc = false;
std::string buf;
@@ -590,7 +814,9 @@
env, static_cast<jstring>(
env->CallObjectMethod(curStackTraceElement, toStringId)));
- if (!stackElementString) return "";
+ if (!stackElementString) {
+ return "";
+ }
// add a line to res
JStringRef elem(env, stackElementString);
@@ -602,7 +828,7 @@
if (i == 1) {
*func = elem.str();
} else if (i > 1 && !haveLoc && !excludeFuncPrefix.empty() &&
- !elem.str().startswith(excludeFuncPrefix)) {
+ !wpi::starts_with(elem, excludeFuncPrefix)) {
*func = elem.str();
haveLoc = true;
}
@@ -612,17 +838,21 @@
return oss.str();
}
-// Finds an exception class and keep it as a global reference.
-// Similar to JClass, but provides Throw methods.
-// Use with caution, as the destructor does NOT call DeleteGlobalRef due
-// to potential shutdown issues with doing so.
+/**
+ * Finds an exception class and keep it as a global reference.
+ *
+ * Similar to JClass, but provides Throw methods. Use with caution, as the
+ * destructor does NOT call DeleteGlobalRef due to potential shutdown issues
+ * with doing so.
+ */
class JException : public JClass {
public:
JException() = default;
JException(JNIEnv* env, const char* name) : JClass(env, name) {
- if (m_cls)
+ if (m_cls) {
m_constructor =
env->GetMethodID(m_cls, "<init>", "(Ljava/lang/String;)V");
+ }
}
void Throw(JNIEnv* env, jstring msg) {
@@ -630,7 +860,9 @@
env->Throw(static_cast<jthrowable>(exception));
}
- void Throw(JNIEnv* env, StringRef msg) { Throw(env, MakeJString(env, msg)); }
+ void Throw(JNIEnv* env, std::string_view msg) {
+ Throw(env, MakeJString(env, msg));
+ }
explicit operator bool() const { return m_constructor; }
@@ -643,7 +875,6 @@
JException* cls;
};
-} // namespace java
-} // namespace wpi
+} // namespace wpi::java
#endif // WPIUTIL_WPI_JNI_UTIL_H_
diff --git a/wpiutil/src/main/native/include/wpi/json.h b/wpiutil/src/main/native/include/wpi/json.h
index a6368b7..1a4e410 100644
--- a/wpiutil/src/main/native/include/wpi/json.h
+++ b/wpiutil/src/main/native/include/wpi/json.h
@@ -54,15 +54,14 @@
#include <memory> // allocator, shared_ptr, make_shared, addressof
#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>
#include <vector> // vector
-#include "wpi/ArrayRef.h"
#include "wpi/StringMap.h"
-#include "wpi/StringRef.h"
-#include "wpi/Twine.h"
+#include "wpi/span.h"
namespace wpi
{
@@ -239,7 +238,7 @@
struct is_compatible_object_type_impl<true, RealType, CompatibleObjectType>
{
static constexpr auto value =
- std::is_constructible<StringRef, typename CompatibleObjectType::key_type>::value and
+ std::is_constructible<std::string_view, typename CompatibleObjectType::key_type>::value and
std::is_constructible<typename RealType::mapped_type, typename CompatibleObjectType::mapped_type>::value;
};
@@ -269,7 +268,7 @@
std::conjunction<std::negation<std::is_same<void, CompatibleArrayType>>,
std::negation<is_compatible_object_type<
BasicJsonType, CompatibleArrayType>>,
- std::negation<std::is_constructible<StringRef,
+ std::negation<std::is_constructible<std::string_view,
CompatibleArrayType>>,
std::negation<is_json_nested_type<BasicJsonType, CompatibleArrayType>>,
has_value_type<CompatibleArrayType>,
@@ -423,7 +422,7 @@
const int id;
protected:
- exception(int id_, const Twine& what_arg);
+ exception(int id_, std::string_view what_arg);
private:
/// an exception object as storage for error messages
@@ -484,7 +483,7 @@
@param[in] what_arg the explanatory string
@return parse_error object
*/
- static parse_error create(int id_, std::size_t byte_, const Twine& what_arg);
+ static parse_error create(int id_, std::size_t byte_, std::string_view what_arg);
/*!
@brief byte index of the parse error
@@ -498,7 +497,7 @@
const std::size_t byte;
private:
- parse_error(int id_, std::size_t byte_, const Twine& what_arg)
+ parse_error(int id_, std::size_t byte_, std::string_view what_arg)
: exception(id_, what_arg), byte(byte_) {}
};
@@ -542,10 +541,11 @@
class invalid_iterator : public exception
{
public:
- static invalid_iterator create(int id_, const Twine& what_arg);
+ 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_, const Twine& what_arg)
+ invalid_iterator(int id_, std::string_view what_arg)
: exception(id_, what_arg) {}
};
@@ -590,10 +590,11 @@
class type_error : public exception
{
public:
- static type_error create(int id_, const Twine& what_arg);
+ 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_, const Twine& what_arg) : exception(id_, what_arg) {}
+ type_error(int id_, std::string_view what_arg) : exception(id_, what_arg) {}
};
/*!
@@ -631,10 +632,10 @@
class out_of_range : public exception
{
public:
- static out_of_range create(int id_, const Twine& what_arg);
+ static out_of_range create(int id_, std::string_view what_arg);
private:
- out_of_range(int id_, const Twine& what_arg) : exception(id_, what_arg) {}
+ out_of_range(int id_, std::string_view what_arg) : exception(id_, what_arg) {}
};
/*!
@@ -664,10 +665,10 @@
class other_error : public exception
{
public:
- static other_error create(int id_, const Twine& what_arg);
+ static other_error create(int id_, std::string_view what_arg);
private:
- other_error(int id_, const Twine& what_arg) : exception(id_, what_arg) {}
+ other_error(int id_, std::string_view what_arg) : exception(id_, what_arg) {}
};
///////////////////////////
@@ -760,7 +761,7 @@
}
default:
- JSON_THROW(type_error::create(302, "type must be number, but is " + Twine(j.type_name())));
+ JSON_THROW(type_error::create(302, "type must be number, but is", j.type_name()));
}
}
@@ -769,7 +770,7 @@
{
if (JSON_UNLIKELY(not j.is_boolean()))
{
- JSON_THROW(type_error::create(302, "type must be boolean, but is " + Twine(j.type_name())));
+ JSON_THROW(type_error::create(302, "type must be boolean, but is", j.type_name()));
}
b = *j.template get_ptr<const bool*>();
}
@@ -779,7 +780,7 @@
{
if (JSON_UNLIKELY(not j.is_string()))
{
- JSON_THROW(type_error::create(302, "type must be string, but is " + Twine(j.type_name())));
+ JSON_THROW(type_error::create(302, "type must be string, but is", j.type_name()));
}
s = *j.template get_ptr<const std::string*>();
}
@@ -816,7 +817,7 @@
{
if (JSON_UNLIKELY(not j.is_array()))
{
- JSON_THROW(type_error::create(302, "type must be array, but is " + Twine(j.type_name())));
+ 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*>();
}
@@ -875,8 +876,7 @@
{
if (JSON_UNLIKELY(not j.is_array()))
{
- JSON_THROW(type_error::create(302, "type must be array, but is " +
- Twine(j.type_name())));
+ JSON_THROW(type_error::create(302, "type must be array, but is", j.type_name()));
}
from_json_array_impl(j, arr, priority_tag<2> {});
@@ -888,7 +888,7 @@
{
if (!j.is_object())
{
- JSON_THROW(type_error::create(302, "type must be object, but is " + Twine(j.type_name())));
+ 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*>();
@@ -904,7 +904,7 @@
{
if (JSON_UNLIKELY(not j.is_object()))
{
- JSON_THROW(type_error::create(302, "type must be object, but is " + Twine(j.type_name())));
+ 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*>();
@@ -965,7 +965,7 @@
}
default:
- JSON_THROW(type_error::create(302, "type must be number, but is " + Twine(j.type_name())));
+ JSON_THROW(type_error::create(302, "type must be number, but is", j.type_name()));
}
}
@@ -1053,7 +1053,7 @@
struct external_constructor<value_t::string>
{
template<typename BasicJsonType>
- static void construct(BasicJsonType& j, StringRef s)
+ static void construct(BasicJsonType& j, std::string_view s)
{
j.m_type = value_t::string;
j.m_value = s;
@@ -1126,7 +1126,7 @@
}
template<typename BasicJsonType, typename T>
- static void construct(BasicJsonType& j, ArrayRef<T> arr)
+ static void construct(BasicJsonType& j, span<T> arr)
{
using std::begin;
using std::end;
@@ -1206,7 +1206,7 @@
}
template<typename BasicJsonType, typename CompatibleString,
- enable_if_t<std::is_constructible<StringRef, CompatibleString>::value, int> = 0>
+ 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);
@@ -1283,7 +1283,7 @@
}
template<typename BasicJsonType, typename T, std::size_t N,
- enable_if_t<not std::is_constructible<StringRef, T (&)[N]>::value, int> = 0>
+ 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);
@@ -2044,7 +2044,7 @@
@brief return the key of an object iterator
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
- StringRef key() const
+ std::string_view key() const
{
assert(m_object != nullptr);
@@ -2122,7 +2122,7 @@
// use key from the object
case value_t::object:
- return anchor.key();
+ return std::string{anchor.key()};
// use an empty key for all primitive types
default:
@@ -2348,7 +2348,7 @@
@since version 2.0.0
*/
- explicit json_pointer(const Twine& s = {})
+ explicit json_pointer(std::string_view s = {})
: reference_tokens(split(s))
{}
@@ -2382,7 +2382,7 @@
@throw out_of_range.404 if string @a s could not be converted to an integer
*/
- static int array_index(const Twine& s);
+ static int array_index(std::string_view s);
private:
/*!
@@ -2490,7 +2490,7 @@
@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(const Twine& reference_string);
+ static std::vector<std::string> split(std::string_view reference_string);
/*!
@brief replace all occurrences of a substring by another string
@@ -2521,7 +2521,7 @@
@note Empty objects or arrays are flattened to `null`.
*/
- static void flatten(const Twine& reference_string,
+ static void flatten(std::string_view reference_string,
const json& value,
json& result);
@@ -2678,9 +2678,10 @@
class binary_writer;
class lexer;
class parser;
- class serializer;
public:
+ class serializer;
+
using value_t = detail::value_t;
/// @copydoc wpi::json_pointer
using json_pointer = ::wpi::json_pointer;
@@ -2970,7 +2971,7 @@
json_value(value_t t);
/// constructor for strings
- json_value(StringRef value)
+ json_value(std::string_view value)
{
string = create<std::string>(value);
}
@@ -3576,8 +3577,7 @@
}
default:
- JSON_THROW(invalid_iterator::create(206, "cannot construct with iterators from " +
- Twine(first.m_object->type_name())));
+ JSON_THROW(invalid_iterator::create(206, "cannot construct with iterators from", first.m_object->type_name()));
}
assert_invariant();
@@ -4162,7 +4162,7 @@
return m_value.boolean;
}
- JSON_THROW(type_error::create(302, "type must be boolean, but is " + Twine(type_name())));
+ JSON_THROW(type_error::create(302, "type must be boolean, but is", type_name()));
}
/// get a pointer to the value (object)
@@ -4271,7 +4271,7 @@
return *ptr;
}
- JSON_THROW(type_error::create(303, "incompatible ReferenceType for get_ref, actual type is " + Twine(obj.type_name())));
+ JSON_THROW(type_error::create(303, "incompatible ReferenceType for get_ref, actual type is", obj.type_name()));
}
public:
@@ -4716,7 +4716,7 @@
written using `at()`. It also demonstrates the different exceptions that
can be thrown.,at__object_t_key_type}
*/
- reference at(StringRef key);
+ reference at(std::string_view key);
/*!
@brief access specified object element with bounds checking
@@ -4748,7 +4748,7 @@
`at()`. It also demonstrates the different exceptions that can be thrown.,
at__object_t_key_type_const}
*/
- const_reference at(StringRef key) const;
+ const_reference at(std::string_view key) const;
/*!
@brief access specified array element
@@ -4825,7 +4825,7 @@
@since version 1.0.0
*/
- reference operator[](StringRef key);
+ reference operator[](std::string_view key);
/*!
@brief read-only access specified object element
@@ -4857,7 +4857,7 @@
@since version 1.0.0
*/
- const_reference operator[](StringRef key) const;
+ const_reference operator[](std::string_view key) const;
/*!
@brief access specified object element
@@ -4903,7 +4903,7 @@
return m_value.object->operator[](key);
}
- JSON_THROW(type_error::create(305, "cannot use operator[] with " + Twine(type_name())));
+ JSON_THROW(type_error::create(305, "cannot use operator[] with", type_name()));
}
/*!
@@ -4946,7 +4946,7 @@
return m_value.object->find(key)->second;
}
- JSON_THROW(type_error::create(305, "cannot use operator[] with " + Twine(type_name())));
+ JSON_THROW(type_error::create(305, "cannot use operator[] with", type_name()));
}
/*!
@@ -4999,7 +4999,7 @@
*/
template<class ValueType, typename std::enable_if<
std::is_convertible<json_t, ValueType>::value, int>::type = 0>
- ValueType value(StringRef key, const ValueType& default_value) const
+ ValueType value(std::string_view key, const ValueType& default_value) const
{
// at only works for objects
if (JSON_LIKELY(is_object()))
@@ -5014,14 +5014,14 @@
return default_value;
}
- JSON_THROW(type_error::create(306, "cannot use value() with " + Twine(type_name())));
+ JSON_THROW(type_error::create(306, "cannot use value() with", type_name()));
}
/*!
@brief overload for a default value of type const char*
@copydoc json::value(const typename object_t::key_type&, ValueType) const
*/
- std::string value(StringRef key, const char* default_value) const
+ std::string value(std::string_view key, const char* default_value) const
{
return value(key, std::string(default_value));
}
@@ -5085,7 +5085,7 @@
}
}
- JSON_THROW(type_error::create(306, "cannot use value() with " + Twine(type_name())));
+ JSON_THROW(type_error::create(306, "cannot use value() with", type_name()));
}
/*!
@@ -5220,7 +5220,7 @@
@sa @ref erase(IteratorType, IteratorType) -- removes the elements in
the given range
- @sa @ref erase(StringRef) -- removes the element
+ @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
@@ -5278,7 +5278,7 @@
}
default:
- JSON_THROW(type_error::create(307, "cannot use erase() with " + Twine(type_name())));
+ JSON_THROW(type_error::create(307, "cannot use erase() with", type_name()));
}
}
@@ -5377,7 +5377,7 @@
}
default:
- JSON_THROW(type_error::create(307, "cannot use erase() with " + Twine(type_name())));
+ JSON_THROW(type_error::create(307, "cannot use erase() with", type_name()));
}
return result;
@@ -5412,7 +5412,7 @@
@since version 1.0.0
*/
- size_type erase(StringRef key);
+ size_type erase(std::string_view key);
/*!
@brief remove element from a JSON array given an index
@@ -5472,13 +5472,13 @@
@since version 1.0.0
*/
- iterator find(StringRef key);
+ iterator find(std::string_view key);
/*!
@brief find an element in a JSON object
@copydoc find(KeyT&&)
*/
- const_iterator find(StringRef key) const;
+ const_iterator find(std::string_view key) const;
/*!
@brief returns the number of occurrences of a key in a JSON object
@@ -5501,7 +5501,7 @@
@since version 1.0.0
*/
- size_type count(StringRef key) const;
+ size_type count(std::string_view key) const;
/// @}
@@ -6113,7 +6113,7 @@
// push_back only works for null objects or objects
if (JSON_UNLIKELY(not(is_null() or is_object())))
{
- JSON_THROW(type_error::create(308, "cannot use push_back() with " + Twine(type_name())));
+ JSON_THROW(type_error::create(308, "cannot use push_back() with", type_name()));
}
// transform null object into an object
@@ -6203,7 +6203,7 @@
// emplace_back only works for null objects or arrays
if (JSON_UNLIKELY(not(is_null() or is_array())))
{
- JSON_THROW(type_error::create(311, "cannot use emplace_back() with " + Twine(type_name())));
+ JSON_THROW(type_error::create(311, "cannot use emplace_back() with", type_name()));
}
// transform null object into an array
@@ -6246,12 +6246,12 @@
@since version 2.0.8
*/
template<class... Args>
- std::pair<iterator, bool> emplace(StringRef key, Args&& ... args)
+ std::pair<iterator, bool> emplace(std::string_view key, Args&& ... args)
{
// emplace only works for null objects or arrays
if (JSON_UNLIKELY(not(is_null() or is_object())))
{
- JSON_THROW(type_error::create(311, "cannot use emplace() with " + Twine(type_name())));
+ JSON_THROW(type_error::create(311, "cannot use emplace() with", type_name()));
}
// transform null object into an object
@@ -6521,7 +6521,7 @@
}
else
{
- JSON_THROW(type_error::create(310, "cannot use swap() with " + Twine(type_name())));
+ JSON_THROW(type_error::create(310, "cannot use swap() with", type_name()));
}
}
@@ -6554,7 +6554,7 @@
}
else
{
- JSON_THROW(type_error::create(310, "cannot use swap() with " + Twine(type_name())));
+ JSON_THROW(type_error::create(310, "cannot use swap() with", type_name()));
}
}
@@ -6587,7 +6587,7 @@
}
else
{
- JSON_THROW(type_error::create(310, "cannot use swap() with " + Twine(type_name())));
+ JSON_THROW(type_error::create(310, "cannot use swap() with", type_name()));
}
}
@@ -7011,11 +7011,11 @@
@since version 2.0.3 (contiguous containers)
*/
- static json parse(StringRef s,
+ static json parse(std::string_view s,
const parser_callback_t cb = nullptr,
const bool allow_exceptions = true);
- static json parse(ArrayRef<uint8_t> arr,
+ static json parse(span<const uint8_t> arr,
const parser_callback_t cb = nullptr,
const bool allow_exceptions = true);
@@ -7026,9 +7026,9 @@
const parser_callback_t cb = nullptr,
const bool allow_exceptions = true);
- static bool accept(StringRef s);
+ static bool accept(std::string_view s);
- static bool accept(ArrayRef<uint8_t> arr);
+ static bool accept(span<const uint8_t> arr);
static bool accept(raw_istream& i);
@@ -7206,8 +7206,8 @@
@since version 2.0.9
*/
static std::vector<uint8_t> to_cbor(const json& j);
- static ArrayRef<uint8_t> to_cbor(const json& j, std::vector<uint8_t>& buf);
- static ArrayRef<uint8_t> to_cbor(const json& j, SmallVectorImpl<uint8_t>& buf);
+ static span<uint8_t> to_cbor(const json& j, std::vector<uint8_t>& buf);
+ static span<uint8_t> to_cbor(const json& j, SmallVectorImpl<uint8_t>& buf);
static void to_cbor(raw_ostream& os, const json& j);
/*!
@@ -7291,8 +7291,8 @@
@since version 2.0.9
*/
static std::vector<uint8_t> to_msgpack(const json& j);
- static ArrayRef<uint8_t> to_msgpack(const json& j, std::vector<uint8_t>& buf);
- static ArrayRef<uint8_t> to_msgpack(const json& j, SmallVectorImpl<uint8_t>& buf);
+ static span<uint8_t> to_msgpack(const json& j, std::vector<uint8_t>& buf);
+ static span<uint8_t> to_msgpack(const json& j, SmallVectorImpl<uint8_t>& buf);
static void to_msgpack(raw_ostream& os, const json& j);
/*!
@@ -7378,10 +7378,10 @@
static std::vector<uint8_t> to_ubjson(const json& j,
const bool use_size = false,
const bool use_type = false);
- static ArrayRef<uint8_t> to_ubjson(const json& j, std::vector<uint8_t>& buf,
- const bool use_size = false, const bool use_type = false);
- static ArrayRef<uint8_t> to_ubjson(const json& j, SmallVectorImpl<uint8_t>& buf,
- const bool use_size = false, const bool use_type = false);
+ static span<uint8_t> to_ubjson(const json& j, std::vector<uint8_t>& buf,
+ const bool use_size = false, const bool use_type = false);
+ static 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);
@@ -7484,7 +7484,7 @@
/*!
@copydoc from_cbor(raw_istream&, const bool)
*/
- static json from_cbor(ArrayRef<uint8_t> arr, const bool strict = true);
+ static json from_cbor(span<const uint8_t> arr, const bool strict = true);
/*!
@brief create a JSON value from an input in MessagePack format
@@ -7563,9 +7563,9 @@
const bool strict = true);
/*!
- @copydoc from_msgpack(raw_istream, const bool)
+ @copydoc from_msgpack(raw_istream&, const bool)
*/
- static json from_msgpack(ArrayRef<uint8_t> arr, const bool strict = true);
+ static json from_msgpack(span<const uint8_t> arr, const bool strict = true);
/*!
@brief create a JSON value from an input in UBJSON format
@@ -7623,7 +7623,7 @@
static json from_ubjson(raw_istream& is,
const bool strict = true);
- static json from_ubjson(ArrayRef<uint8_t> arr, const bool strict = true);
+ static json from_ubjson(span<const uint8_t> arr, const bool strict = true);
/// @}
@@ -8074,7 +8074,7 @@
*/
inline wpi::json operator "" _json(const char* s, std::size_t n)
{
- return wpi::json::parse(wpi::StringRef(s, n));
+ return wpi::json::parse(std::string_view(s, n));
}
/*!
@@ -8092,7 +8092,7 @@
*/
inline wpi::json::json_pointer operator "" _json_pointer(const char* s, std::size_t n)
{
- return wpi::json::json_pointer(wpi::StringRef(s, n));
+ return wpi::json::json_pointer(std::string_view(s, n));
}
#ifndef WPI_JSON_IMPLEMENTATION
diff --git a/wpiutil/src/main/native/cpp/json_serializer.h b/wpiutil/src/main/native/include/wpi/json_serializer.h
similarity index 93%
rename from wpiutil/src/main/native/cpp/json_serializer.h
rename to wpiutil/src/main/native/include/wpi/json_serializer.h
index 6c5ffbe..3796c95 100644
--- a/wpiutil/src/main/native/cpp/json_serializer.h
+++ b/wpiutil/src/main/native/include/wpi/json_serializer.h
@@ -35,7 +35,6 @@
#include <clocale> // lconv, localeconv
#include <cmath> // labs, isfinite, isnan, signbit, ldexp
-#include <locale> // locale
#include <type_traits>
#include "wpi/raw_ostream.h"
@@ -51,10 +50,11 @@
/*!
@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)
- : o(s), loc(std::localeconv()), indent_char(ichar),
- indent_string(512, indent_char)
+ 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
@@ -75,6 +75,7 @@
@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)
*/
@@ -83,7 +84,6 @@
const unsigned int indent_step,
const unsigned int current_indent = 0);
- private:
/*!
@brief dump escaped string
@@ -94,11 +94,11 @@
@param[in] s the string to escape
@param[in] ensure_ascii whether to escape non-ASCII characters with
- \uXXXX sequences
+ "\uXXXX" sequences
- @complexity Linear in the length of string @a s.
+ Complexity: Linear in the length of string @a s.
*/
- void dump_escaped(StringRef s, const bool ensure_ascii);
+ 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>
@@ -198,9 +198,6 @@
/// a (hopefully) large enough character buffer
std::array<char, 64> number_buffer{{}};
- /// the locale
- const std::lconv* loc = nullptr;
-
/// the indentation character
const char indent_char;
/// the indentation string
diff --git a/wpiutil/src/main/native/include/wpi/leb128.h b/wpiutil/src/main/native/include/wpi/leb128.h
index c70f471..04752c6 100644
--- a/wpiutil/src/main/native/include/wpi/leb128.h
+++ b/wpiutil/src/main/native/include/wpi/leb128.h
@@ -1,68 +1,112 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2015-2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_LEB128_H_
#define WPIUTIL_WPI_LEB128_H_
-#include <cstddef>
+#include <stdint.h>
-#include "wpi/SmallVector.h"
+#include <optional>
+
+#include "wpi/span.h"
namespace wpi {
+template <typename T>
+class SmallVectorImpl;
class raw_istream;
+class raw_ostream;
/**
- * Get size of unsigned LEB128 data
- * @val: value
+ * Get size of unsigned LEB128 data.
*
* Determine the number of bytes required to encode an unsigned LEB128 datum.
* The algorithm is taken from Appendix C of the DWARF 3 spec. For information
* on the encodings refer to section "7.6 - Variable Length Data". Return
* the number of bytes required.
+ *
+ * @param val LEB128 data.
*/
uint64_t SizeUleb128(uint64_t val);
/**
- * Write unsigned LEB128 data
- * @addr: the address where the ULEB128 data is to be stored
- * @val: value to be stored
+ * Write unsigned LEB128 data.
*
* Encode an unsigned LEB128 encoded datum. The algorithm is taken
* from Appendix C of the DWARF 3 spec. For information on the
* encodings refer to section "7.6 - Variable Length Data". Return
* the number of bytes written.
+ *
+ * @param dest The address where the ULEB128 data is to be stored.
+ * @param val Value to be stored.
*/
uint64_t WriteUleb128(SmallVectorImpl<char>& dest, uint64_t val);
/**
- * Read unsigned LEB128 data
- * @addr: the address where the ULEB128 data is stored
- * @ret: address to store the result
+ * Write unsigned LEB128 data.
+ *
+ * Encode an unsigned LEB128 encoded datum. The algorithm is taken
+ * from Appendix C of the DWARF 3 spec. For information on the
+ * encodings refer to section "7.6 - Variable Length Data".
+ *
+ * @param os Output stream.
+ * @param val Value to be stored.
+ */
+void WriteUleb128(raw_ostream& os, uint64_t val);
+
+/**
+ * Read unsigned LEB128 data.
*
* Decode an unsigned LEB128 encoded datum. The algorithm is taken
* from Appendix C of the DWARF 3 spec. For information on the
* encodings refer to section "7.6 - Variable Length Data". Return
* the number of bytes read.
+ *
+ * @param addr The address where the ULEB128 data is stored.
+ * @param ret Address to store the result.
*/
uint64_t ReadUleb128(const char* addr, uint64_t* ret);
/**
- * Read unsigned LEB128 data from a stream
- * @is: the input stream where the ULEB128 data is to be read from
- * @ret: address to store the result
+ * Read unsigned LEB128 data from a stream.
*
* Decode an unsigned LEB128 encoded datum. The algorithm is taken
* from Appendix C of the DWARF 3 spec. For information on the
* encodings refer to section "7.6 - Variable Length Data". Return
* false on stream error, true on success.
+ *
+ * @param is The input stream where the ULEB128 data is to be read from.
+ * @param ret Address to store the result.
*/
bool ReadUleb128(raw_istream& is, uint64_t* ret);
+/**
+ * Unsigned LEB128 streaming reader.
+ *
+ * Decode an unsigned LEB128 encoded datum. The algorithm is taken
+ * from Appendix C of the DWARF 3 spec. For information on the
+ * encodings refer to section "7.6 - Variable Length Data".
+ */
+class Uleb128Reader {
+ public:
+ /**
+ * Decode a single ULEB128 value. Returns after a single ULEB128 value has
+ * been read or insufficient input (call in a loop to get multiple values).
+ * If a value is returned, internal state is reset so it's safe to immediately
+ * call this function again to decode another value.
+ *
+ * @param in Input data; modified as data is consumed (any unconsumed data
+ * is left when function returns).
+ * @return value (in std::optional)
+ */
+ std::optional<uint64_t> ReadOne(span<const uint8_t>* in);
+
+ private:
+ uint64_t m_result = 0;
+ int m_shift = 0;
+};
+
} // namespace wpi
#endif // WPIUTIL_WPI_LEB128_H_
diff --git a/wpiutil/src/main/native/include/wpi/math b/wpiutil/src/main/native/include/wpi/math
index d9c18c7..25f2a64 100644
--- a/wpiutil/src/main/native/include/wpi/math
+++ b/wpiutil/src/main/native/include/wpi/math
@@ -1,67 +1,20 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2019 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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 "wpi/numbers"
+
+// clang-format off
+#ifdef _MSC_VER
+#pragma message("warning: Use <wpi/numbers> and wpi::numbers instead to reflect C++20 <numbers> and std::numbers")
+#else
+#warning "Use <wpi/numbers> and wpi::numbers instead to reflect C++20 <numbers> and std::numbers"
+#endif
+
+// clang-format on
namespace wpi::math {
-
-template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
-inline constexpr T e_v = 2.718281828459045235360287471352662498L;
-
-template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
-inline constexpr T log2e_v = 1.442695040888963407359924681001892137L;
-
-template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
-inline constexpr T log10e_v = 0.434294481903251827651128918916605082L;
-
-template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
-inline constexpr T pi_v = 3.141592653589793238462643383279502884L;
-
-template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
-inline constexpr T inv_pi_v = 0.318309886183790671537767526745028724L;
-
-template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
-inline constexpr T inv_sqrtpi_v = 0.564189583547756286948079451560772586L;
-
-template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
-inline constexpr T ln2_v = 0.693147180559945309417232121458176568L;
-
-template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
-inline constexpr T ln10_v = 2.302585092994045684017991454684364208L;
-
-template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
-inline constexpr T sqrt2_v = 1.414213562373095048801688724209698078L;
-
-template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
-inline constexpr T sqrt3_v = 1.732050807568877293527446341505872366L;
-
-template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
-inline constexpr T inv_sqrt3_v = 0.577350269189625764509148780501957456L;
-
-template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
-inline constexpr T egamma_v = 0.577215664901532860606512090082402431L;
-
-template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
-inline constexpr T phi_v = 1.618033988749894848204586834365638117L;
-
-inline constexpr double e = e_v<double>;
-inline constexpr double log2e = log2e_v<double>;
-inline constexpr double log10e = log10e_v<double>;
-inline constexpr double pi = pi_v<double>;
-inline constexpr double inv_pi = inv_pi_v<double>;
-inline constexpr double inv_sqrtpi = inv_sqrtpi_v<double>;
-inline constexpr double ln2 = ln2_v<double>;
-inline constexpr double ln10 = ln10_v<double>;
-inline constexpr double sqrt2 = sqrt2_v<double>;
-inline constexpr double sqrt3 = sqrt3_v<double>;
-inline constexpr double inv_sqrt3 = inv_sqrt3_v<double>;
-inline constexpr double egamma = egamma_v<double>;
-inline constexpr double phi = phi_v<double>;
-
+using namespace wpi::numbers;
} // namespace wpi::math
diff --git a/wpiutil/src/main/native/include/wpi/mpack.h b/wpiutil/src/main/native/include/wpi/mpack.h
new file mode 100644
index 0000000..870c26b
--- /dev/null
+++ b/wpiutil/src/main/native/include/wpi/mpack.h
@@ -0,0 +1,8207 @@
+/**
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2015-2021 Nicholas Fraser and the MPack authors
+ *
+ * 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.
+ *
+ */
+
+/*
+ * This is the MPack 1.1 amalgamation package.
+ *
+ * http://github.com/ludocode/mpack
+ */
+
+#ifndef MPACK_H
+#define MPACK_H 1
+
+#define MPACK_AMALGAMATED 1
+
+#if defined(MPACK_HAS_CONFIG) && MPACK_HAS_CONFIG
+#include "mpack-config.h"
+#endif
+
+
+/* mpack/mpack-platform.h.h */
+
+/**
+ * @file
+ *
+ * Abstracts all platform-specific code from MPack and handles configuration
+ * options.
+ *
+ * This verifies the configuration and sets defaults based on the platform,
+ * contains implementations of standard C functions when libc is not available,
+ * and provides wrappers to all library functions.
+ *
+ * Documentation for configuration options is available here:
+ *
+ * https://ludocode.github.io/mpack/group__config.html
+ */
+
+#ifndef MPACK_PLATFORM_H
+#define MPACK_PLATFORM_H 1
+
+
+
+/**
+ * @defgroup config Configuration Options
+ *
+ * Defines the MPack configuration options.
+ *
+ * Custom configuration of MPack is not usually necessary. In almost all
+ * cases you can ignore this and use the defaults.
+ *
+ * If you do want to configure MPack, you can define the below options as part
+ * of your build system or project settings. This will override the below
+ * defaults.
+ *
+ * If you'd like to use a file for configuration instead, define
+ * @ref MPACK_HAS_CONFIG to 1 in your build system or project settings.
+ * This will cause MPack to include a file you create called @c mpack-config.h
+ * in which you can define your configuration. This is useful if you need to
+ * include specific headers (such as a custom allocator) in order to configure
+ * MPack to use it.
+ *
+ * @warning The value of all configuration options must be the same in
+ * all translation units of your project, as well as in the mpack source
+ * itself. These configuration options affect the layout of structs, among
+ * other things, which cannot be different in source files that are linked
+ * together.
+ *
+ * @note MPack does not contain defaults for building inside the Linux kernel.
+ * There is a <a href="https://github.com/ludocode/mpack-linux-kernel">
+ * configuration file for the Linux kernel</a> that can be used instead.
+ *
+ * @{
+ */
+
+
+
+/*
+ * Pre-include checks
+ *
+ * These need to come before the user's mpack-config.h because they might be
+ * including headers in it.
+ */
+
+/** @cond */
+#if defined(_MSC_VER) && _MSC_VER < 1800 && !defined(__cplusplus)
+ #error "In Visual Studio 2012 and earlier, MPack must be compiled as C++. Enable the /Tp compiler flag."
+#endif
+
+#if defined(_WIN32) && MPACK_INTERNAL
+ #define _CRT_SECURE_NO_WARNINGS 1
+#endif
+
+#ifndef __STDC_LIMIT_MACROS
+ #define __STDC_LIMIT_MACROS 1
+#endif
+#ifndef __STDC_FORMAT_MACROS
+ #define __STDC_FORMAT_MACROS 1
+#endif
+#ifndef __STDC_CONSTANT_MACROS
+ #define __STDC_CONSTANT_MACROS 1
+#endif
+/** @endcond */
+
+
+
+/**
+ * @name File Configuration
+ * @{
+ */
+
+/**
+ * @def MPACK_HAS_CONFIG
+ *
+ * Causes MPack to include a file you create called @c mpack-config.h .
+ *
+ * The file is included before MPack sets any defaults for undefined
+ * configuration options. You can use it to configure MPack.
+ *
+ * This is off by default.
+ */
+#if defined(MPACK_HAS_CONFIG)
+ #if MPACK_HAS_CONFIG
+ #include "mpack-config.h"
+ #endif
+#else
+ #define MPACK_HAS_CONFIG 0
+#endif
+
+/**
+ * @}
+ */
+
+// this needs to come first since some stuff depends on it
+/** @cond */
+#ifndef MPACK_NO_BUILTINS
+ #define MPACK_NO_BUILTINS 0
+#endif
+/** @endcond */
+
+
+
+/**
+ * @name Features
+ * @{
+ */
+
+/**
+ * @def MPACK_READER
+ *
+ * Enables compilation of the base Tag Reader.
+ */
+#ifndef MPACK_READER
+#define MPACK_READER 1
+#endif
+
+/**
+ * @def MPACK_EXPECT
+ *
+ * Enables compilation of the static Expect API.
+ */
+#ifndef MPACK_EXPECT
+#define MPACK_EXPECT 1
+#endif
+
+/**
+ * @def MPACK_NODE
+ *
+ * Enables compilation of the dynamic Node API.
+ */
+#ifndef MPACK_NODE
+#define MPACK_NODE 1
+#endif
+
+/**
+ * @def MPACK_WRITER
+ *
+ * Enables compilation of the Writer.
+ */
+#ifndef MPACK_WRITER
+#define MPACK_WRITER 1
+#endif
+
+/**
+ * @def MPACK_BUILDER
+ *
+ * Enables compilation of the Builder.
+ *
+ * The Builder API provides additional functions to the Writer for
+ * automatically determining the element count of compound elements so you do
+ * not have to specify them up-front.
+ *
+ * This requires a @c malloc(). It is enabled by default if MPACK_WRITER is
+ * enabled and MPACK_MALLOC is defined.
+ *
+ * @see mpack_build_map()
+ * @see mpack_build_array()
+ * @see mpack_complete_map()
+ * @see mpack_complete_array()
+ */
+// This is defined furthur below after we've resolved whether we have malloc().
+
+/**
+ * @def MPACK_COMPATIBILITY
+ *
+ * Enables compatibility features for reading and writing older
+ * versions of MessagePack.
+ *
+ * This is disabled by default. When disabled, the behaviour is equivalent to
+ * using the default version, @ref mpack_version_current.
+ *
+ * Enable this if you need to interoperate with applications or data that do
+ * not support the new (v5) MessagePack spec. See the section on v4
+ * compatibility in @ref docs/protocol.md for more information.
+ */
+#ifndef MPACK_COMPATIBILITY
+#define MPACK_COMPATIBILITY 0
+#endif
+
+/**
+ * @def MPACK_EXTENSIONS
+ *
+ * Enables the use of extension types.
+ *
+ * This is disabled by default. Define it to 1 to enable it. If disabled,
+ * functions to read and write extensions will not exist, and any occurrence of
+ * extension types in parsed messages will flag @ref mpack_error_invalid.
+ *
+ * MPack discourages the use of extension types. See the section on extension
+ * types in @ref docs/protocol.md for more information.
+ */
+#ifndef MPACK_EXTENSIONS
+#define MPACK_EXTENSIONS 0
+#endif
+
+/**
+ * @}
+ */
+
+
+
+// workarounds for Doxygen
+#if defined(MPACK_DOXYGEN)
+#if MPACK_DOXYGEN
+// We give these their default values of 0 here even though they are defined to
+// 1 in the doxyfile. Doxygen will show this as the value in the docs, even
+// though it ignores it when parsing the rest of the source. This is what we
+// want, since we want the documentation to show these defaults but still
+// generate documentation for the functions they add when they're on.
+#define MPACK_COMPATIBILITY 0
+#define MPACK_EXTENSIONS 0
+#endif
+#endif
+
+
+
+/**
+ * @name Dependencies
+ * @{
+ */
+
+/**
+ * @def MPACK_CONFORMING
+ *
+ * Enables the inclusion of basic C headers to define standard types and
+ * macros.
+ *
+ * This causes MPack to include headers required for conforming implementations
+ * of C99 even in freestanding, in particular <stddef.h>, <stdint.h>,
+ * <stdbool.h> and <limits.h>. It also includes <inttypes.h>; this is
+ * technically not required for freestanding but MPack needs it to detect
+ * integer limits.
+ *
+ * You can disable this if these headers are unavailable or if they do not
+ * define the standard types and macros (for example inside the Linux kernel.)
+ * If this is disabled, MPack will include no headers and will assume a 32-bit
+ * int. You will probably also want to define @ref MPACK_HAS_CONFIG to 1 and
+ * include your own headers in the config file. You must provide definitions
+ * for standard types such as @c size_t, @c bool, @c int32_t and so on.
+ *
+ * @see <a href="https://en.cppreference.com/w/c/language/conformance">
+ * cppreference.com documentation on Conformance</a>
+ */
+#ifndef MPACK_CONFORMING
+ #define MPACK_CONFORMING 1
+#endif
+
+/**
+ * @def MPACK_STDLIB
+ *
+ * Enables the use of the C stdlib.
+ *
+ * This allows the library to use basic functions like @c memcmp() and @c
+ * strlen(), as well as @c malloc() for debugging and in allocation helpers.
+ *
+ * If this is disabled, allocation helper functions will not be defined, and
+ * MPack will attempt to detect compiler intrinsics for functions like @c
+ * memcmp() (assuming @ref MPACK_NO_BUILTINS is not set.) It will fallback to
+ * its own (slow) implementations if it cannot use builtins. You may want to
+ * define @ref MPACK_MEMCMP and friends if you disable this.
+ *
+ * @see MPACK_MEMCMP
+ * @see MPACK_MEMCPY
+ * @see MPACK_MEMMOVE
+ * @see MPACK_MEMSET
+ * @see MPACK_STRLEN
+ * @see MPACK_MALLOC
+ * @see MPACK_REALLOC
+ * @see MPACK_FREE
+ */
+#ifndef MPACK_STDLIB
+ #if !MPACK_CONFORMING
+ // If we don't even have a proper <limits.h> we assume we won't have
+ // malloc() either.
+ #define MPACK_STDLIB 0
+ #else
+ #define MPACK_STDLIB 1
+ #endif
+#endif
+
+/**
+ * @def MPACK_STDIO
+ *
+ * Enables the use of C stdio. This adds helpers for easily
+ * reading/writing C files and makes debugging easier.
+ */
+#ifndef MPACK_STDIO
+ #if !MPACK_STDLIB || defined(__AVR__)
+ #define MPACK_STDIO 0
+ #else
+ #define MPACK_STDIO 1
+ #endif
+#endif
+
+/**
+ * Whether the 'float' type and floating point operations are supported.
+ *
+ * If @ref MPACK_FLOAT is disabled, floats are read and written as @c uint32_t
+ * instead. This way messages with floats do not result in errors and you can
+ * still perform manual float parsing yourself.
+ */
+#ifndef MPACK_FLOAT
+ #define MPACK_FLOAT 1
+#endif
+
+/**
+ * Whether the 'double' type is supported. This requires support for 'float'.
+ *
+ * If @ref MPACK_DOUBLE is disabled, doubles are read and written as @c
+ * uint32_t instead. This way messages with doubles do not result in errors and
+ * you can still perform manual doubles parsing yourself.
+ *
+ * If @ref MPACK_FLOAT is enabled but @ref MPACK_DOUBLE is not, doubles can be
+ * read as floats using the shortening conversion functions, e.g. @ref
+ * mpack_expect_float() or @ref mpack_node_float().
+ */
+#ifndef MPACK_DOUBLE
+ #if !MPACK_FLOAT || defined(__AVR__)
+ // AVR supports only float, not double.
+ #define MPACK_DOUBLE 0
+ #else
+ #define MPACK_DOUBLE 1
+ #endif
+#endif
+
+/**
+ * @}
+ */
+
+
+
+/**
+ * @name Allocation Functions
+ * @{
+ */
+
+/**
+ * @def MPACK_MALLOC
+ *
+ * Defines the memory allocation function used by MPack. This is used by
+ * helpers for automatically allocating data the correct size, and for
+ * debugging functions. If this macro is undefined, the allocation helpers
+ * will not be compiled.
+ *
+ * Set this to use a custom @c malloc() function. Your function must have the
+ * signature:
+ *
+ * @code
+ * void* malloc(size_t size);
+ * @endcode
+ *
+ * The default is @c malloc() if @ref MPACK_STDLIB is enabled.
+ */
+/**
+ * @def MPACK_FREE
+ *
+ * Defines the memory free function used by MPack. This is used by helpers
+ * for automatically allocating data the correct size. If this macro is
+ * undefined, the allocation helpers will not be compiled.
+ *
+ * Set this to use a custom @c free() function. Your function must have the
+ * signature:
+ *
+ * @code
+ * void free(void* p);
+ * @endcode
+ *
+ * The default is @c free() if @ref MPACK_MALLOC has not been customized and
+ * @ref MPACK_STDLIB is enabled.
+ */
+/**
+ * @def MPACK_REALLOC
+ *
+ * Defines the realloc function used by MPack. It is used by growable
+ * buffers to resize more efficiently.
+ *
+ * The default is @c realloc() if @ref MPACK_MALLOC has not been customized and
+ * @ref MPACK_STDLIB is enabled.
+ *
+ * Set this to use a custom @c realloc() function. Your function must have the
+ * signature:
+ *
+ * @code
+ * void* realloc(void* p, size_t new_size);
+ * @endcode
+ *
+ * This is optional, even when @ref MPACK_MALLOC is used. If @ref MPACK_MALLOC is
+ * set and @ref MPACK_REALLOC is not, @ref MPACK_MALLOC is used with a simple copy
+ * to grow buffers.
+ */
+
+#if defined(MPACK_MALLOC) && !defined(MPACK_FREE)
+ #error "MPACK_MALLOC requires MPACK_FREE."
+#endif
+#if !defined(MPACK_MALLOC) && defined(MPACK_FREE)
+ #error "MPACK_FREE requires MPACK_MALLOC."
+#endif
+
+// These were never configurable in lowercase but we check anyway.
+#ifdef mpack_malloc
+ #error "Define MPACK_MALLOC, not mpack_malloc."
+#endif
+#ifdef mpack_realloc
+ #error "Define MPACK_REALLOC, not mpack_realloc."
+#endif
+#ifdef mpack_free
+ #error "Define MPACK_FREE, not mpack_free."
+#endif
+
+// We don't use calloc() at all.
+#ifdef MPACK_CALLOC
+ #error "Don't define MPACK_CALLOC. MPack does not use calloc()."
+#endif
+#ifdef mpack_calloc
+ #error "Don't define mpack_calloc. MPack does not use calloc()."
+#endif
+
+// Use defaults in stdlib if we have them. Without it we don't use malloc.
+#if defined(MPACK_STDLIB)
+ #if MPACK_STDLIB && !defined(MPACK_MALLOC)
+ #define MPACK_MALLOC malloc
+ #define MPACK_REALLOC realloc
+ #define MPACK_FREE free
+ #endif
+#endif
+
+/**
+ * @}
+ */
+
+
+
+// This needs to be defined after we've decided whether we have malloc().
+#ifndef MPACK_BUILDER
+ #if defined(MPACK_MALLOC) && MPACK_WRITER
+ #define MPACK_BUILDER 1
+ #else
+ #define MPACK_BUILDER 0
+ #endif
+#endif
+
+
+
+/**
+ * @name System Functions
+ * @{
+ */
+
+/**
+ * @def MPACK_MEMCMP
+ *
+ * The function MPack will use to provide @c memcmp().
+ *
+ * Set this to use a custom @c memcmp() function. Your function must have the
+ * signature:
+ *
+ * @code
+ * int memcmp(const void* left, const void* right, size_t count);
+ * @endcode
+ */
+/**
+ * @def MPACK_MEMCPY
+ *
+ * The function MPack will use to provide @c memcpy().
+ *
+ * Set this to use a custom @c memcpy() function. Your function must have the
+ * signature:
+ *
+ * @code
+ * void* memcpy(void* restrict dest, const void* restrict src, size_t count);
+ * @endcode
+ */
+/**
+ * @def MPACK_MEMMOVE
+ *
+ * The function MPack will use to provide @c memmove().
+ *
+ * Set this to use a custom @c memmove() function. Your function must have the
+ * signature:
+ *
+ * @code
+ * void* memmove(void* dest, const void* src, size_t count);
+ * @endcode
+ */
+/**
+ * @def MPACK_MEMSET
+ *
+ * The function MPack will use to provide @c memset().
+ *
+ * Set this to use a custom @c memset() function. Your function must have the
+ * signature:
+ *
+ * @code
+ * void* memset(void* p, int c, size_t count);
+ * @endcode
+ */
+/**
+ * @def MPACK_STRLEN
+ *
+ * The function MPack will use to provide @c strlen().
+ *
+ * Set this to use a custom @c strlen() function. Your function must have the
+ * signature:
+ *
+ * @code
+ * size_t strlen(const char* str);
+ * @endcode
+ */
+
+// These were briefly configurable in lowercase in an unreleased version. Just
+// to make sure no one is doing this, we make sure these aren't already defined.
+#ifdef mpack_memcmp
+ #error "Define MPACK_MEMCMP, not mpack_memcmp."
+#endif
+#ifdef mpack_memcpy
+ #error "Define MPACK_MEMCPY, not mpack_memcpy."
+#endif
+#ifdef mpack_memmove
+ #error "Define MPACK_MEMMOVE, not mpack_memmove."
+#endif
+#ifdef mpack_memset
+ #error "Define MPACK_MEMSET, not mpack_memset."
+#endif
+#ifdef mpack_strlen
+ #error "Define MPACK_STRLEN, not mpack_strlen."
+#endif
+
+// If the standard library is available, we prefer to use its functions.
+#if MPACK_STDLIB
+ #ifndef MPACK_MEMCMP
+ #define MPACK_MEMCMP memcmp
+ #endif
+ #ifndef MPACK_MEMCPY
+ #define MPACK_MEMCPY memcpy
+ #endif
+ #ifndef MPACK_MEMMOVE
+ #define MPACK_MEMMOVE memmove
+ #endif
+ #ifndef MPACK_MEMSET
+ #define MPACK_MEMSET memset
+ #endif
+ #ifndef MPACK_STRLEN
+ #define MPACK_STRLEN strlen
+ #endif
+#endif
+
+#if !MPACK_NO_BUILTINS
+ #ifdef __has_builtin
+ #if !defined(MPACK_MEMCMP) && __has_builtin(__builtin_memcmp)
+ #define MPACK_MEMCMP __builtin_memcmp
+ #endif
+ #if !defined(MPACK_MEMCPY) && __has_builtin(__builtin_memcpy)
+ #define MPACK_MEMCPY __builtin_memcpy
+ #endif
+ #if !defined(MPACK_MEMMOVE) && __has_builtin(__builtin_memmove)
+ #define MPACK_MEMMOVE __builtin_memmove
+ #endif
+ #if !defined(MPACK_MEMSET) && __has_builtin(__builtin_memset)
+ #define MPACK_MEMSET __builtin_memset
+ #endif
+ #if !defined(MPACK_STRLEN) && __has_builtin(__builtin_strlen)
+ #define MPACK_STRLEN __builtin_strlen
+ #endif
+ #elif defined(__GNUC__)
+ #ifndef MPACK_MEMCMP
+ #define MPACK_MEMCMP __builtin_memcmp
+ #endif
+ #ifndef MPACK_MEMCPY
+ #define MPACK_MEMCPY __builtin_memcpy
+ #endif
+ // There's not always a builtin memmove for GCC. If we can't test for
+ // it with __has_builtin above, we don't use it. It's been around for
+ // much longer under clang, but then so has __has_builtin, so we let
+ // the block above handle it.
+ #ifndef MPACK_MEMSET
+ #define MPACK_MEMSET __builtin_memset
+ #endif
+ #ifndef MPACK_STRLEN
+ #define MPACK_STRLEN __builtin_strlen
+ #endif
+ #endif
+#endif
+
+/**
+ * @}
+ */
+
+
+
+/**
+ * @name Debugging Options
+ * @{
+ */
+
+/**
+ * @def MPACK_DEBUG
+ *
+ * Enables debug features. You may want to wrap this around your
+ * own debug preprocs. By default, this is enabled if @c DEBUG or @c _DEBUG
+ * are defined. (@c NDEBUG is not used since it is allowed to have
+ * different values in different translation units.)
+ */
+#if !defined(MPACK_DEBUG)
+ #if defined(DEBUG) || defined(_DEBUG)
+ #define MPACK_DEBUG 1
+ #else
+ #define MPACK_DEBUG 0
+ #endif
+#endif
+
+/**
+ * @def MPACK_STRINGS
+ *
+ * Enables descriptive error and type strings.
+ *
+ * This can be turned off (by defining it to 0) to maximize space savings
+ * on embedded devices. If this is disabled, string functions such as
+ * mpack_error_to_string() and mpack_type_to_string() return an empty string.
+ */
+#ifndef MPACK_STRINGS
+ #ifdef __AVR__
+ #define MPACK_STRINGS 0
+ #else
+ #define MPACK_STRINGS 1
+ #endif
+#endif
+
+/**
+ * Set this to 1 to implement a custom @ref mpack_assert_fail() function.
+ * See the documentation on @ref mpack_assert_fail() for details.
+ *
+ * Asserts are only used when @ref MPACK_DEBUG is enabled, and can be
+ * triggered by bugs in MPack or bugs due to incorrect usage of MPack.
+ */
+#ifndef MPACK_CUSTOM_ASSERT
+#define MPACK_CUSTOM_ASSERT 0
+#endif
+
+/**
+ * @def MPACK_READ_TRACKING
+ *
+ * Enables compound type size tracking for readers. This ensures that the
+ * correct number of elements or bytes are read from a compound type.
+ *
+ * This is enabled by default in debug builds (provided a @c malloc() is
+ * available.)
+ */
+#if !defined(MPACK_READ_TRACKING)
+ #if MPACK_DEBUG && MPACK_READER && defined(MPACK_MALLOC)
+ #define MPACK_READ_TRACKING 1
+ #else
+ #define MPACK_READ_TRACKING 0
+ #endif
+#endif
+#if MPACK_READ_TRACKING && !MPACK_READER
+ #error "MPACK_READ_TRACKING requires MPACK_READER."
+#endif
+
+/**
+ * @def MPACK_WRITE_TRACKING
+ *
+ * Enables compound type size tracking for writers. This ensures that the
+ * correct number of elements or bytes are written in a compound type.
+ *
+ * Note that without write tracking enabled, it is possible for buggy code
+ * to emit invalid MessagePack without flagging an error by writing the wrong
+ * number of elements or bytes in a compound type. With tracking enabled,
+ * MPack will catch such errors and break on the offending line of code.
+ *
+ * This is enabled by default in debug builds (provided a @c malloc() is
+ * available.)
+ */
+#if !defined(MPACK_WRITE_TRACKING)
+ #if MPACK_DEBUG && MPACK_WRITER && defined(MPACK_MALLOC)
+ #define MPACK_WRITE_TRACKING 1
+ #else
+ #define MPACK_WRITE_TRACKING 0
+ #endif
+#endif
+#if MPACK_WRITE_TRACKING && !MPACK_WRITER
+ #error "MPACK_WRITE_TRACKING requires MPACK_WRITER."
+#endif
+
+/**
+ * @}
+ */
+
+
+
+
+/**
+ * @name Miscellaneous Options
+ * @{
+ */
+
+/**
+ * Whether to optimize for size or speed.
+ *
+ * Optimizing for size simplifies some parsing and encoding algorithms
+ * at the expense of speed and saves a few kilobytes of space in the
+ * resulting executable.
+ *
+ * This automatically detects -Os with GCC/Clang. Unfortunately there
+ * doesn't seem to be a macro defined for /Os under MSVC.
+ */
+#ifndef MPACK_OPTIMIZE_FOR_SIZE
+ #ifdef __OPTIMIZE_SIZE__
+ #define MPACK_OPTIMIZE_FOR_SIZE 1
+ #else
+ #define MPACK_OPTIMIZE_FOR_SIZE 0
+ #endif
+#endif
+
+/**
+ * Stack space in bytes to use when initializing a reader or writer
+ * with a stack-allocated buffer.
+ *
+ * @warning Make sure you have sufficient stack space. Some libc use relatively
+ * small stacks even on desktop platforms, e.g. musl.
+ */
+#ifndef MPACK_STACK_SIZE
+#define MPACK_STACK_SIZE 4096
+#endif
+
+/**
+ * Buffer size to use for allocated buffers (such as for a file writer.)
+ *
+ * Starting with a single page and growing as needed seems to
+ * provide the best performance with minimal memory waste.
+ * Increasing this does not improve performance even when writing
+ * huge messages.
+ */
+#ifndef MPACK_BUFFER_SIZE
+#define MPACK_BUFFER_SIZE 4096
+#endif
+
+/**
+ * Minimum size for paged allocations in bytes.
+ *
+ * This is the value used by default for MPACK_NODE_PAGE_SIZE and
+ * MPACK_BUILDER_PAGE_SIZE.
+ */
+#ifndef MPACK_PAGE_SIZE
+#define MPACK_PAGE_SIZE 4096
+#endif
+
+/**
+ * Minimum size of an allocated node page in bytes.
+ *
+ * The children for a given compound element must be contiguous, so
+ * larger pages than this may be allocated as needed. (Safety checks
+ * exist to prevent malicious data from causing too large allocations.)
+ *
+ * See @ref mpack_node_data_t for the size of nodes.
+ *
+ * Using as many nodes fit in one memory page seems to provide the
+ * best performance, and has very little waste when parsing small
+ * messages.
+ */
+#ifndef MPACK_NODE_PAGE_SIZE
+#define MPACK_NODE_PAGE_SIZE MPACK_PAGE_SIZE
+#endif
+
+/**
+ * Minimum size of an allocated builder page in bytes.
+ *
+ * Builder writes are deferred to the allocated builder buffer which is
+ * composed of a list of buffer pages. This defines the size of those pages.
+ */
+#ifndef MPACK_BUILDER_PAGE_SIZE
+#define MPACK_BUILDER_PAGE_SIZE MPACK_PAGE_SIZE
+#endif
+
+/**
+ * @def MPACK_BUILDER_INTERNAL_STORAGE
+ *
+ * Enables a small amount of internal storage within the writer to avoid some
+ * allocations when using builders.
+ *
+ * This is disabled by default. Enable it to potentially improve performance at
+ * the expense of a larger writer.
+ *
+ * @see MPACK_BUILDER_INTERNAL_STORAGE_SIZE to configure its size.
+ */
+#ifndef MPACK_BUILDER_INTERNAL_STORAGE
+#define MPACK_BUILDER_INTERNAL_STORAGE 0
+#endif
+
+/**
+ * Amount of space reserved inside @ref mpack_writer_t for the Builders. This
+ * can allow small messages to be built with the Builder API without incurring
+ * an allocation.
+ *
+ * Builder metadata is placed in this space in addition to the literal
+ * MessagePack data. It needs to be big enough to be useful, but not so big as
+ * to overflow the stack. If more space is needed, pages are allocated.
+ *
+ * This is only used if MPACK_BUILDER_INTERNAL_STORAGE is enabled.
+ *
+ * @see MPACK_BUILDER_PAGE_SIZE
+ * @see MPACK_BUILDER_INTERNAL_STORAGE
+ *
+ * @warning Writers are typically placed on the stack so make sure you have
+ * sufficient stack space. Some libc use relatively small stacks even on
+ * desktop platforms, e.g. musl.
+ */
+#ifndef MPACK_BUILDER_INTERNAL_STORAGE_SIZE
+#define MPACK_BUILDER_INTERNAL_STORAGE_SIZE 256
+#endif
+
+/**
+ * The initial depth for the node parser. When MPACK_MALLOC is available,
+ * the node parser has no practical depth limit, and it is not recursive
+ * so there is no risk of overflowing the call stack.
+ */
+#ifndef MPACK_NODE_INITIAL_DEPTH
+#define MPACK_NODE_INITIAL_DEPTH 8
+#endif
+
+/**
+ * The maximum depth for the node parser if @ref MPACK_MALLOC is not available.
+ */
+#ifndef MPACK_NODE_MAX_DEPTH_WITHOUT_MALLOC
+#define MPACK_NODE_MAX_DEPTH_WITHOUT_MALLOC 32
+#endif
+
+/**
+ * @def MPACK_NO_BUILTINS
+ *
+ * Whether to disable compiler intrinsics and other built-in functions.
+ *
+ * If this is enabled, MPack won't use `__attribute__`, `__declspec`, any
+ * function starting with `__builtin`, or pretty much anything else that isn't
+ * standard C.
+ */
+#if defined(MPACK_DOXYGEN)
+#if MPACK_DOXYGEN
+ #define MPACK_NO_BUILTINS 0
+#endif
+#endif
+
+/**
+ * @}
+ */
+
+
+
+#if MPACK_DEBUG
+/**
+ * @name Debug Functions
+ * @{
+ */
+/**
+ * Implement this and define @ref MPACK_CUSTOM_ASSERT to use a custom
+ * assertion function.
+ *
+ * This function should not return. If it does, MPack will @c abort().
+ *
+ * If you use C++, make sure you include @c mpack.h where you define
+ * this to get the correct linkage (or define it <code>extern "C"</code>.)
+ *
+ * Asserts are only used when @ref MPACK_DEBUG is enabled, and can be
+ * triggered by bugs in MPack or bugs due to incorrect usage of MPack.
+ */
+void mpack_assert_fail(const char* message);
+/**
+ * @}
+ */
+#endif
+
+
+
+// The rest of this file shouldn't show up in Doxygen docs.
+/** @cond */
+
+
+
+/*
+ * All remaining pseudo-configuration options that have not yet been set must
+ * be defined here in order to support -Wundef.
+ *
+ * These aren't real configuration options; they are implementation details of
+ * MPack.
+ */
+#ifndef MPACK_CUSTOM_BREAK
+#define MPACK_CUSTOM_BREAK 0
+#endif
+#ifndef MPACK_EMIT_INLINE_DEFS
+#define MPACK_EMIT_INLINE_DEFS 0
+#endif
+#ifndef MPACK_AMALGAMATED
+#define MPACK_AMALGAMATED 0
+#endif
+#ifndef MPACK_RELEASE_VERSION
+#define MPACK_RELEASE_VERSION 0
+#endif
+#ifndef MPACK_INTERNAL
+#define MPACK_INTERNAL 0
+#endif
+
+
+
+/* System headers (based on configuration) */
+
+#if MPACK_CONFORMING
+ #include <stddef.h>
+ #include <stdint.h>
+ #include <stdbool.h>
+ #include <inttypes.h>
+ #include <limits.h>
+#endif
+
+#if MPACK_STDLIB
+ #include <string.h>
+ #include <stdlib.h>
+#endif
+
+#if MPACK_STDIO
+ #include <stdio.h>
+ #include <errno.h>
+ #if MPACK_DEBUG
+ #include <stdarg.h>
+ #endif
+#endif
+
+
+
+/*
+ * Integer Constants and Limits
+ */
+
+#if MPACK_CONFORMING
+ #define MPACK_INT64_C INT64_C
+ #define MPACK_UINT64_C UINT64_C
+
+ #define MPACK_INT8_MIN INT8_MIN
+ #define MPACK_INT16_MIN INT16_MIN
+ #define MPACK_INT32_MIN INT32_MIN
+ #define MPACK_INT64_MIN INT64_MIN
+ #define MPACK_INT_MIN INT_MIN
+
+ #define MPACK_INT8_MAX INT8_MAX
+ #define MPACK_INT16_MAX INT16_MAX
+ #define MPACK_INT32_MAX INT32_MAX
+ #define MPACK_INT64_MAX INT64_MAX
+ #define MPACK_INT_MAX INT_MAX
+
+ #define MPACK_UINT8_MAX UINT8_MAX
+ #define MPACK_UINT16_MAX UINT16_MAX
+ #define MPACK_UINT32_MAX UINT32_MAX
+ #define MPACK_UINT64_MAX UINT64_MAX
+ #define MPACK_UINT_MAX UINT_MAX
+#else
+ // For a non-conforming implementation we assume int is 32 bits.
+
+ #define MPACK_INT64_C(x) ((int64_t)(x##LL))
+ #define MPACK_UINT64_C(x) ((uint64_t)(x##LLU))
+
+ #define MPACK_INT8_MIN ((int8_t)(0x80))
+ #define MPACK_INT16_MIN ((int16_t)(0x8000))
+ #define MPACK_INT32_MIN ((int32_t)(0x80000000))
+ #define MPACK_INT64_MIN MPACK_INT64_C(0x8000000000000000)
+ #define MPACK_INT_MIN MPACK_INT32_MIN
+
+ #define MPACK_INT8_MAX ((int8_t)(0x7f))
+ #define MPACK_INT16_MAX ((int16_t)(0x7fff))
+ #define MPACK_INT32_MAX ((int32_t)(0x7fffffff))
+ #define MPACK_INT64_MAX MPACK_INT64_C(0x7fffffffffffffff)
+ #define MPACK_INT_MAX MPACK_INT32_MAX
+
+ #define MPACK_UINT8_MAX ((uint8_t)(0xffu))
+ #define MPACK_UINT16_MAX ((uint16_t)(0xffffu))
+ #define MPACK_UINT32_MAX ((uint32_t)(0xffffffffu))
+ #define MPACK_UINT64_MAX MPACK_UINT64_C(0xffffffffffffffff)
+ #define MPACK_UINT_MAX MPACK_UINT32_MAX
+#endif
+
+
+
+/*
+ * Floating point support
+ */
+
+#if MPACK_DOUBLE && !MPACK_FLOAT
+ #error "MPACK_DOUBLE requires MPACK_FLOAT."
+#endif
+
+// If we don't have support for float or double, we poison the identifiers to
+// make sure we don't define anything related to them.
+#if MPACK_INTERNAL
+ #ifdef __GNUC__
+ #if !MPACK_FLOAT
+ #pragma GCC poison float
+ #endif
+ #if !MPACK_DOUBLE
+ #pragma GCC poison double
+ #endif
+ #endif
+#endif
+
+
+
+/*
+ * extern C
+ */
+
+#ifdef __cplusplus
+ #define MPACK_EXTERN_C_BEGIN namespace mpack {
+ #define MPACK_EXTERN_C_END }
+#else
+ #define MPACK_EXTERN_C_BEGIN /*nothing*/
+ #define MPACK_EXTERN_C_END /*nothing*/
+#endif
+
+
+
+/*
+ * Warnings
+ */
+
+#if defined(__GNUC__)
+ // Diagnostic push is not supported before GCC 4.6.
+ #if defined(__clang__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
+ #define MPACK_SILENCE_WARNINGS_PUSH _Pragma ("GCC diagnostic push")
+ #define MPACK_SILENCE_WARNINGS_POP _Pragma ("GCC diagnostic pop")
+ #endif
+#elif defined(_MSC_VER)
+ // To support VS2017 and earlier we need to use __pragma and not _Pragma
+ #define MPACK_SILENCE_WARNINGS_PUSH __pragma(warning(push))
+ #define MPACK_SILENCE_WARNINGS_POP __pragma(warning(pop))
+#endif
+
+#if defined(_MSC_VER)
+ // These are a bunch of mostly useless warnings emitted under MSVC /W4,
+ // some as a result of the expansion of macros.
+ #define MPACK_SILENCE_WARNINGS_MSVC_W4 \
+ __pragma(warning(disable:4996)) /* _CRT_SECURE_NO_WARNINGS */ \
+ __pragma(warning(disable:4127)) /* comparison is constant */ \
+ __pragma(warning(disable:4702)) /* unreachable code */ \
+ __pragma(warning(disable:4310)) /* cast truncates constant value */
+#else
+ #define MPACK_SILENCE_WARNINGS_MSVC_W4 /*nothing*/
+#endif
+
+/* GCC versions before 5.1 warn about defining a C99 non-static inline function
+ * before declaring it (see issue #20). */
+#if defined(__GNUC__) && !defined(__clang__)
+ #if __GNUC__ < 5 || (__GNUC__ == 5 && __GNUC_MINOR__ < 1)
+ #ifdef __cplusplus
+ #define MPACK_SILENCE_WARNINGS_MISSING_PROTOTYPES \
+ _Pragma ("GCC diagnostic ignored \"-Wmissing-declarations\"")
+ #else
+ #define MPACK_SILENCE_WARNINGS_MISSING_PROTOTYPES \
+ _Pragma ("GCC diagnostic ignored \"-Wmissing-prototypes\"")
+ #endif
+ #endif
+#endif
+#ifndef MPACK_SILENCE_WARNINGS_MISSING_PROTOTYPES
+ #define MPACK_SILENCE_WARNINGS_MISSING_PROTOTYPES /*nothing*/
+#endif
+
+/* GCC versions before 4.8 warn about shadowing a function with a variable that
+ * isn't a function or function pointer (like "index"). */
+#if defined(__GNUC__) && !defined(__clang__)
+ #if __GNUC__ == 4 && __GNUC_MINOR__ < 8
+ #define MPACK_SILENCE_WARNINGS_SHADOW \
+ _Pragma ("GCC diagnostic ignored \"-Wshadow\"")
+ #endif
+#endif
+#ifndef MPACK_SILENCE_WARNINGS_SHADOW
+ #define MPACK_SILENCE_WARNINGS_SHADOW /*nothing*/
+#endif
+
+// On platforms with small size_t (e.g. AVR) we get type limits warnings where
+// we compare a size_t to e.g. MPACK_UINT32_MAX.
+#ifdef __AVR__
+ #define MPACK_SILENCE_WARNINGS_TYPE_LIMITS \
+ _Pragma ("GCC diagnostic ignored \"-Wtype-limits\"")
+#else
+ #define MPACK_SILENCE_WARNINGS_TYPE_LIMITS /*nothing*/
+#endif
+
+// MPack uses declarations after statements. This silences warnings about it
+// (e.g. when using MPack in a Linux kernel module.)
+#if defined(__GNUC__) && !defined(__cplusplus)
+ #define MPACK_SILENCE_WARNINGS_DECLARATION_AFTER_STATEMENT \
+ _Pragma ("GCC diagnostic ignored \"-Wdeclaration-after-statement\"")
+#else
+ #define MPACK_SILENCE_WARNINGS_DECLARATION_AFTER_STATEMENT /*nothing*/
+#endif
+
+#ifdef MPACK_SILENCE_WARNINGS_PUSH
+ // We only silence warnings if push/pop is supported, that way we aren't
+ // silencing warnings in code that uses MPack. If your compiler doesn't
+ // support push/pop silencing of warnings, you'll have to turn off
+ // conflicting warnings manually.
+
+ #define MPACK_SILENCE_WARNINGS_BEGIN \
+ MPACK_SILENCE_WARNINGS_PUSH \
+ MPACK_SILENCE_WARNINGS_MSVC_W4 \
+ MPACK_SILENCE_WARNINGS_MISSING_PROTOTYPES \
+ MPACK_SILENCE_WARNINGS_SHADOW \
+ MPACK_SILENCE_WARNINGS_TYPE_LIMITS \
+ MPACK_SILENCE_WARNINGS_DECLARATION_AFTER_STATEMENT
+
+ #define MPACK_SILENCE_WARNINGS_END \
+ MPACK_SILENCE_WARNINGS_POP
+#else
+ #define MPACK_SILENCE_WARNINGS_BEGIN /*nothing*/
+ #define MPACK_SILENCE_WARNINGS_END /*nothing*/
+#endif
+
+MPACK_SILENCE_WARNINGS_BEGIN
+MPACK_EXTERN_C_BEGIN
+
+
+
+/* Miscellaneous helper macros */
+
+#define MPACK_UNUSED(var) ((void)(var))
+
+#define MPACK_STRINGIFY_IMPL(arg) #arg
+#define MPACK_STRINGIFY(arg) MPACK_STRINGIFY_IMPL(arg)
+
+// This is a workaround for MSVC's incorrect expansion of __VA_ARGS__. It
+// treats __VA_ARGS__ as a single preprocessor token when passed in the
+// argument list of another macro unless we use an outer wrapper to expand it
+// lexically first. (For safety/consistency we use this in all variadic macros
+// that don't ignore the variadic arguments regardless of whether __VA_ARGS__
+// is passed to another macro.)
+// https://stackoverflow.com/a/32400131
+#define MPACK_EXPAND(x) x
+
+// Extracts the first argument of a variadic macro list, where there might only
+// be one argument.
+#define MPACK_EXTRACT_ARG0_IMPL(first, ...) first
+#define MPACK_EXTRACT_ARG0(...) MPACK_EXPAND(MPACK_EXTRACT_ARG0_IMPL( __VA_ARGS__ , ignored))
+
+// Stringifies the first argument of a variadic macro list, where there might
+// only be one argument.
+#define MPACK_STRINGIFY_ARG0_impl(first, ...) #first
+#define MPACK_STRINGIFY_ARG0(...) MPACK_EXPAND(MPACK_STRINGIFY_ARG0_impl( __VA_ARGS__ , ignored))
+
+
+
+/*
+ * Definition of inline macros.
+ *
+ * MPack does not use static inline in header files; only one non-inline definition
+ * of each function should exist in the final build. This can reduce the binary size
+ * in cases where the compiler cannot or chooses not to inline a function.
+ * The addresses of functions should also compare equal across translation units
+ * regardless of whether they are declared inline.
+ *
+ * The above requirements mean that the declaration and definition of non-trivial
+ * inline functions must be separated so that the definitions will only
+ * appear when necessary. In addition, three different linkage models need
+ * to be supported:
+ *
+ * - The C99 model, where a standalone function is emitted only if there is any
+ * `extern inline` or non-`inline` declaration (including the definition itself)
+ *
+ * - The GNU model, where an `inline` definition emits a standalone function and an
+ * `extern inline` definition does not, regardless of other declarations
+ *
+ * - The C++ model, where `inline` emits a standalone function with special
+ * (COMDAT) linkage
+ *
+ * The macros below wrap up everything above. All inline functions defined in header
+ * files have a single non-inline definition emitted in the compilation of
+ * mpack-platform.c. All inline declarations and definitions use the same MPACK_INLINE
+ * specification to simplify the rules on when standalone functions are emitted.
+ * Inline functions in source files are defined MPACK_STATIC_INLINE.
+ *
+ * Additional reading:
+ * http://www.greenend.org.uk/rjk/tech/inline.html
+ */
+
+#if defined(__cplusplus)
+ // C++ rules
+ // The linker will need COMDAT support to link C++ object files,
+ // so we don't need to worry about emitting definitions from C++
+ // translation units. If mpack-platform.c (or the amalgamation)
+ // is compiled as C, its definition will be used, otherwise a
+ // C++ definition will be used, and no other C files will emit
+ // a definition.
+ #define MPACK_INLINE inline
+
+#elif defined(_MSC_VER)
+ // MSVC 2013 always uses COMDAT linkage, but it doesn't treat 'inline' as a
+ // keyword in C99 mode. (This appears to be fixed in a later version of
+ // MSVC but we don't bother detecting it.)
+ #define MPACK_INLINE __inline
+ #define MPACK_STATIC_INLINE static __inline
+
+#elif defined(__GNUC__) && (defined(__GNUC_GNU_INLINE__) || \
+ (!defined(__GNUC_STDC_INLINE__) && !defined(__GNUC_GNU_INLINE__)))
+ // GNU rules
+ #if MPACK_EMIT_INLINE_DEFS
+ #define MPACK_INLINE inline
+ #else
+ #define MPACK_INLINE extern inline
+ #endif
+
+#elif defined(__TINYC__)
+ // tcc ignores the inline keyword, so we have to use static inline. We
+ // issue a warning to make sure you are aware. You can define the below
+ // macro to disable the warning. Hopefully this will be fixed soon:
+ // https://lists.nongnu.org/archive/html/tinycc-devel/2019-06/msg00000.html
+ #ifndef MPACK_DISABLE_TINYC_INLINE_WARNING
+ #warning "Single-definition inline is not supported by tcc. All inlines will be static. Define MPACK_DISABLE_TINYC_INLINE_WARNING to disable this warning."
+ #endif
+ #define MPACK_INLINE static inline
+
+#else
+ // C99 rules
+ #if MPACK_EMIT_INLINE_DEFS
+ #define MPACK_INLINE extern inline
+ #else
+ #define MPACK_INLINE inline
+ #endif
+#endif
+
+#ifndef MPACK_STATIC_INLINE
+#define MPACK_STATIC_INLINE static inline
+#endif
+
+#ifdef MPACK_OPTIMIZE_FOR_SPEED
+ #error "You should define MPACK_OPTIMIZE_FOR_SIZE, not MPACK_OPTIMIZE_FOR_SPEED."
+#endif
+
+
+
+/*
+ * Prevent inlining
+ *
+ * When a function is only used once, it is almost always inlined
+ * automatically. This can cause poor instruction cache usage because a
+ * function that should rarely be called (such as buffer exhaustion handling)
+ * will get inlined into the middle of a hot code path.
+ */
+
+#if !MPACK_NO_BUILTINS
+ #if defined(_MSC_VER)
+ #define MPACK_NOINLINE __declspec(noinline)
+ #elif defined(__GNUC__) || defined(__clang__)
+ #define MPACK_NOINLINE __attribute__((__noinline__))
+ #endif
+#endif
+#ifndef MPACK_NOINLINE
+ #define MPACK_NOINLINE /* nothing */
+#endif
+
+
+
+/* restrict */
+
+// We prefer the builtins even though e.g. MSVC's __restrict may not have
+// exactly the same behaviour as the proper C99 restrict keyword because the
+// builtins work in C++, so using the same keyword in both C and C++ prevents
+// any incompatibilities when using MPack compiled as C in C++ code.
+#if !MPACK_NO_BUILTINS
+ #if defined(__GNUC__)
+ #define MPACK_RESTRICT __restrict__
+ #elif defined(_MSC_VER)
+ #define MPACK_RESTRICT __restrict
+ #endif
+#endif
+
+#ifndef MPACK_RESTRICT
+ #ifdef __cplusplus
+ #define MPACK_RESTRICT /* nothing, unavailable in C++ */
+ #endif
+#endif
+
+#ifndef MPACK_RESTRICT
+ #ifdef _MSC_VER
+ // MSVC 2015 apparently doesn't properly support the restrict keyword
+ // in C. We're using builtins above which do work on 2015, but when
+ // MPACK_NO_BUILTINS is enabled we can't use it.
+ #if _MSC_VER < 1910
+ #define MPACK_RESTRICT /*nothing*/
+ #endif
+ #endif
+#endif
+
+#ifndef MPACK_RESTRICT
+ #define MPACK_RESTRICT restrict /* required in C99 */
+#endif
+
+
+
+/* Some compiler-specific keywords and builtins */
+
+#if !MPACK_NO_BUILTINS
+ #if defined(__GNUC__) || defined(__clang__)
+ #define MPACK_UNREACHABLE __builtin_unreachable()
+ #define MPACK_NORETURN(fn) fn __attribute__((__noreturn__))
+ #elif defined(_MSC_VER)
+ #define MPACK_UNREACHABLE __assume(0)
+ #define MPACK_NORETURN(fn) __declspec(noreturn) fn
+ #endif
+#endif
+
+#ifndef MPACK_UNREACHABLE
+#define MPACK_UNREACHABLE ((void)0)
+#endif
+#ifndef MPACK_NORETURN
+#define MPACK_NORETURN(fn) fn
+#endif
+
+
+
+/*
+ * Likely/unlikely
+ *
+ * These should only really be used when a branch is taken (or not taken) less
+ * than 1/1000th of the time. Buffer flush checks when writing very small
+ * elements are a good example.
+ */
+
+#if !MPACK_NO_BUILTINS
+ #if defined(__GNUC__) || defined(__clang__)
+ #ifndef MPACK_LIKELY
+ #define MPACK_LIKELY(x) __builtin_expect((x),1)
+ #endif
+ #ifndef MPACK_UNLIKELY
+ #define MPACK_UNLIKELY(x) __builtin_expect((x),0)
+ #endif
+ #endif
+#endif
+
+#ifndef MPACK_LIKELY
+ #define MPACK_LIKELY(x) (x)
+#endif
+#ifndef MPACK_UNLIKELY
+ #define MPACK_UNLIKELY(x) (x)
+#endif
+
+
+
+/* alignof */
+
+#ifndef MPACK_ALIGNOF
+ #if defined(__STDC_VERSION__)
+ #if __STDC_VERSION__ >= 201112L
+ #define MPACK_ALIGNOF(T) (_Alignof(T))
+ #endif
+ #endif
+#endif
+
+#ifndef MPACK_ALIGNOF
+ #if defined(__cplusplus)
+ #if __cplusplus >= 201103L
+ #define MPACK_ALIGNOF(T) (alignof(T))
+ #endif
+ #endif
+#endif
+
+#ifndef MPACK_ALIGNOF
+ #if defined(__GNUC__) && !defined(MPACK_NO_BUILTINS)
+ #if defined(__clang__) || __GNUC__ >= 4
+ #define MPACK_ALIGNOF(T) (__alignof__(T))
+ #endif
+ #endif
+#endif
+
+#ifndef MPACK_ALIGNOF
+ #ifdef _MSC_VER
+ #define MPACK_ALIGNOF(T) __alignof(T)
+ #endif
+#endif
+
+// MPACK_ALIGNOF may not exist, in which case a workaround is used.
+
+
+
+/* Static assert */
+
+#ifndef MPACK_STATIC_ASSERT
+ #if defined(__cplusplus)
+ #if __cplusplus >= 201103L
+ #define MPACK_STATIC_ASSERT static_assert
+ #endif
+ #elif defined(__STDC_VERSION__)
+ #if __STDC_VERSION__ >= 201112L
+ #define MPACK_STATIC_ASSERT _Static_assert
+ #endif
+ #endif
+#endif
+
+#if !MPACK_NO_BUILTINS
+ #ifndef MPACK_STATIC_ASSERT
+ #if defined(__has_feature)
+ #if __has_feature(cxx_static_assert)
+ #define MPACK_STATIC_ASSERT static_assert
+ #elif __has_feature(c_static_assert)
+ #define MPACK_STATIC_ASSERT _Static_assert
+ #endif
+ #endif
+ #endif
+
+ #ifndef MPACK_STATIC_ASSERT
+ #if defined(__GNUC__)
+ /* Diagnostic push is not supported before GCC 4.6. */
+ #if defined(__clang__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
+ #ifndef __cplusplus
+ #if defined(__clang__) || __GNUC__ >= 5
+ #define MPACK_IGNORE_PEDANTIC "GCC diagnostic ignored \"-Wpedantic\""
+ #else
+ #define MPACK_IGNORE_PEDANTIC "GCC diagnostic ignored \"-pedantic\""
+ #endif
+ #define MPACK_STATIC_ASSERT(expr, str) do { \
+ _Pragma ("GCC diagnostic push") \
+ _Pragma (MPACK_IGNORE_PEDANTIC) \
+ _Pragma ("GCC diagnostic ignored \"-Wc++-compat\"") \
+ _Static_assert(expr, str); \
+ _Pragma ("GCC diagnostic pop") \
+ } while (0)
+ #endif
+ #endif
+ #endif
+ #endif
+
+ #ifndef MPACK_STATIC_ASSERT
+ #ifdef _MSC_VER
+ #if _MSC_VER >= 1600
+ #define MPACK_STATIC_ASSERT static_assert
+ #endif
+ #endif
+ #endif
+#endif
+
+#ifndef MPACK_STATIC_ASSERT
+ #define MPACK_STATIC_ASSERT(expr, str) (MPACK_UNUSED(sizeof(char[1 - 2*!(expr)])))
+#endif
+
+
+
+/* _Generic */
+
+#ifndef MPACK_HAS_GENERIC
+ #if defined(__clang__) && defined(__has_feature)
+ // With Clang we can test for _Generic support directly
+ // and ignore C/C++ version
+ #if __has_feature(c_generic_selections)
+ #define MPACK_HAS_GENERIC 1
+ #else
+ #define MPACK_HAS_GENERIC 0
+ #endif
+ #endif
+#endif
+
+#ifndef MPACK_HAS_GENERIC
+ #if defined(__STDC_VERSION__)
+ #if __STDC_VERSION__ >= 201112L
+ #if defined(__GNUC__) && !defined(__clang__)
+ // GCC does not have full C11 support in GCC 4.7 and 4.8
+ #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)
+ #define MPACK_HAS_GENERIC 1
+ #endif
+ #else
+ // We hope other compilers aren't lying about C11/_Generic support
+ #define MPACK_HAS_GENERIC 1
+ #endif
+ #endif
+ #endif
+#endif
+
+#ifndef MPACK_HAS_GENERIC
+ #define MPACK_HAS_GENERIC 0
+#endif
+
+
+
+/*
+ * Finite Math
+ *
+ * -ffinite-math-only, included in -ffast-math, breaks functions that
+ * that check for non-finite real values such as isnan() and isinf().
+ *
+ * We should use this to trap errors when reading data that contains
+ * non-finite reals. This isn't currently implemented.
+ */
+
+#ifndef MPACK_FINITE_MATH
+#if defined(__FINITE_MATH_ONLY__) && __FINITE_MATH_ONLY__
+#define MPACK_FINITE_MATH 1
+#endif
+#endif
+
+#ifndef MPACK_FINITE_MATH
+#define MPACK_FINITE_MATH 0
+#endif
+
+
+
+/*
+ * Endianness checks
+ *
+ * These define MPACK_NHSWAP*() which swap network<->host byte
+ * order when needed.
+ *
+ * We leave them undefined if we can't determine the endianness
+ * at compile-time, in which case we fall back to bit-shifts.
+ *
+ * See the notes in mpack-common.h.
+ */
+
+#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && defined(__ORDER_BIG_ENDIAN__)
+ #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+ #define MPACK_NHSWAP16(x) (x)
+ #define MPACK_NHSWAP32(x) (x)
+ #define MPACK_NHSWAP64(x) (x)
+ #elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+
+ #if !MPACK_NO_BUILTINS
+ #if defined(__clang__)
+ #ifdef __has_builtin
+ // Unlike the GCC builtins, the bswap builtins in Clang
+ // significantly improve ARM performance.
+ #if __has_builtin(__builtin_bswap16)
+ #define MPACK_NHSWAP16(x) __builtin_bswap16(x)
+ #endif
+ #if __has_builtin(__builtin_bswap32)
+ #define MPACK_NHSWAP32(x) __builtin_bswap32(x)
+ #endif
+ #if __has_builtin(__builtin_bswap64)
+ #define MPACK_NHSWAP64(x) __builtin_bswap64(x)
+ #endif
+ #endif
+
+ #elif defined(__GNUC__)
+
+ // The GCC bswap builtins are apparently poorly optimized on older
+ // versions of GCC, so we set a minimum version here just in case.
+ // http://hardwarebug.org/2010/01/14/beware-the-builtins/
+
+ #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
+ #define MPACK_NHSWAP64(x) __builtin_bswap64(x)
+ #endif
+
+ // __builtin_bswap16() was not implemented on all platforms
+ // until GCC 4.8.0:
+ // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52624
+ //
+ // The 16- and 32-bit versions in GCC significantly reduce performance
+ // on ARM with little effect on code size so we don't use them.
+
+ #endif
+ #endif
+ #endif
+
+#elif defined(_MSC_VER) && defined(_WIN32) && MPACK_STDLIB && !MPACK_NO_BUILTINS
+
+ // On Windows, we assume x86 and x86_64 are always little-endian.
+ // We make no assumptions about ARM even though all current
+ // Windows Phone devices are little-endian in case Microsoft's
+ // compiler is ever used with a big-endian ARM device.
+
+ // These are functions in <stdlib.h> so we depend on MPACK_STDLIB.
+ // It's not clear if these are actually faster than just doing the
+ // swap manually; maybe we shouldn't bother with this.
+
+ #if defined(_M_IX86) || defined(_M_X64) || defined(_M_AMD64)
+ #define MPACK_NHSWAP16(x) _byteswap_ushort(x)
+ #define MPACK_NHSWAP32(x) _byteswap_ulong(x)
+ #define MPACK_NHSWAP64(x) _byteswap_uint64(x)
+ #endif
+
+#endif
+
+#if defined(__FLOAT_WORD_ORDER__) && defined(__BYTE_ORDER__)
+
+ // We check where possible that the float byte order matches the
+ // integer byte order. This is extremely unlikely to fail, but
+ // we check anyway just in case.
+ //
+ // (The static assert is placed in float/double encoders instead
+ // of here because our static assert fallback doesn't work at
+ // file scope)
+
+ #define MPACK_CHECK_FLOAT_ORDER() \
+ MPACK_STATIC_ASSERT(__FLOAT_WORD_ORDER__ == __BYTE_ORDER__, \
+ "float byte order does not match int byte order! float/double " \
+ "encoding is not properly implemented on this platform.")
+
+#endif
+
+#ifndef MPACK_CHECK_FLOAT_ORDER
+ #define MPACK_CHECK_FLOAT_ORDER() /* nothing */
+#endif
+
+
+/*
+ * Here we define mpack_assert() and mpack_break(). They both work like a normal
+ * assertion function in debug mode, causing a trap or abort. However, on some platforms
+ * you can safely resume execution from mpack_break(), whereas mpack_assert() is
+ * always fatal.
+ *
+ * In release mode, mpack_assert() is converted to an assurance to the compiler
+ * that the expression cannot be false (via e.g. __assume() or __builtin_unreachable())
+ * to improve optimization where supported. There is thus no point in "safely" handling
+ * the case of this being false. Writing mpack_assert(0) rarely makes sense (except
+ * possibly as a default handler in a switch) since the compiler will throw away any
+ * code after it. If at any time an mpack_assert() is not true, the behaviour is
+ * undefined. This also means the expression is evaluated even in release.
+ *
+ * mpack_break() on the other hand is compiled to nothing in release. It is
+ * used in situations where we want to highlight a programming error as early as
+ * possible (in the debugger), but we still handle the situation safely if it
+ * happens in release to avoid producing incorrect results (such as in
+ * MPACK_WRITE_TRACKING.) It does not take an expression to test because it
+ * belongs in a safe-handling block after its failing condition has been tested.
+ *
+ * If stdio is available, we can add a format string describing the error, and
+ * on some compilers we can declare it noreturn to get correct results from static
+ * analysis tools. Note that the format string and arguments are not evaluated unless
+ * the assertion is hit.
+ *
+ * Note that any arguments to mpack_assert() beyond the first are only evaluated
+ * if the expression is false (and are never evaluated in release.)
+ *
+ * mpack_assert_fail() and mpack_break_hit() are defined separately
+ * because assert is noreturn and break isn't. This distinction is very
+ * important for static analysis tools to give correct results.
+ */
+
+#if MPACK_DEBUG
+ MPACK_NORETURN(void mpack_assert_fail_wrapper(const char* message));
+ #if MPACK_STDIO
+ MPACK_NORETURN(void mpack_assert_fail_format(const char* format, ...));
+ #define mpack_assert_fail_at(line, file, exprstr, format, ...) \
+ MPACK_EXPAND(mpack_assert_fail_format("mpack assertion failed at " file ":" #line "\n%s\n" format, exprstr, __VA_ARGS__))
+ #else
+ #define mpack_assert_fail_at(line, file, exprstr, format, ...) \
+ mpack_assert_fail_wrapper("mpack assertion failed at " file ":" #line "\n" exprstr "\n")
+ #endif
+
+ #define mpack_assert_fail_pos(line, file, exprstr, expr, ...) \
+ MPACK_EXPAND(mpack_assert_fail_at(line, file, exprstr, __VA_ARGS__))
+
+ // This contains a workaround to the pedantic C99 requirement of having at
+ // least one argument to a variadic macro. The first argument is the
+ // boolean expression, the optional second argument (if provided) must be a
+ // literal format string, and any additional arguments are the format
+ // argument list.
+ //
+ // Unfortunately this means macros are expanded in the expression before it
+ // gets stringified. I haven't found a workaround to this.
+ //
+ // This adds two unused arguments to the format argument list when a
+ // format string is provided, so this would complicate the use of
+ // -Wformat and __attribute__((__format__)) on mpack_assert_fail_format()
+ // if we ever bothered to implement it.
+ #define mpack_assert(...) \
+ MPACK_EXPAND(((!(MPACK_EXTRACT_ARG0(__VA_ARGS__))) ? \
+ mpack_assert_fail_pos(__LINE__, __FILE__, MPACK_STRINGIFY_ARG0(__VA_ARGS__) , __VA_ARGS__ , "", NULL) : \
+ (void)0))
+
+ void mpack_break_hit(const char* message);
+ #if MPACK_STDIO
+ void mpack_break_hit_format(const char* format, ...);
+ #define mpack_break_hit_at(line, file, ...) \
+ MPACK_EXPAND(mpack_break_hit_format("mpack breakpoint hit at " file ":" #line "\n" __VA_ARGS__))
+ #else
+ #define mpack_break_hit_at(line, file, ...) \
+ mpack_break_hit("mpack breakpoint hit at " file ":" #line )
+ #endif
+ #define mpack_break_hit_pos(line, file, ...) MPACK_EXPAND(mpack_break_hit_at(line, file, __VA_ARGS__))
+ #define mpack_break(...) MPACK_EXPAND(mpack_break_hit_pos(__LINE__, __FILE__, __VA_ARGS__))
+#else
+ #define mpack_assert(...) \
+ (MPACK_EXPAND((!(MPACK_EXTRACT_ARG0(__VA_ARGS__))) ? \
+ (MPACK_UNREACHABLE, (void)0) : \
+ (void)0))
+ #define mpack_break(...) ((void)0)
+#endif
+
+
+
+// make sure we don't use the stdlib directly during development
+#if MPACK_STDLIB && defined(MPACK_UNIT_TESTS) && MPACK_INTERNAL && defined(__GNUC__)
+ #undef memcmp
+ #undef memcpy
+ #undef memmove
+ #undef memset
+ #undef strlen
+ #undef malloc
+ #undef calloc
+ #undef realloc
+ #undef free
+ #pragma GCC poison memcmp
+ #pragma GCC poison memcpy
+ #pragma GCC poison memmove
+ #pragma GCC poison memset
+ #pragma GCC poison strlen
+ #pragma GCC poison malloc
+ #pragma GCC poison calloc
+ #pragma GCC poison realloc
+ #pragma GCC poison free
+#endif
+
+
+
+// If we don't have these stdlib functions, we need to define them ourselves.
+// Either way we give them a lowercase name to make the code a bit nicer.
+
+#ifdef MPACK_MEMCMP
+ #define mpack_memcmp MPACK_MEMCMP
+#else
+ int mpack_memcmp(const void* s1, const void* s2, size_t n);
+#endif
+
+#ifdef MPACK_MEMCPY
+ #define mpack_memcpy MPACK_MEMCPY
+#else
+ void* mpack_memcpy(void* MPACK_RESTRICT s1, const void* MPACK_RESTRICT s2, size_t n);
+#endif
+
+#ifdef MPACK_MEMMOVE
+ #define mpack_memmove MPACK_MEMMOVE
+#else
+ void* mpack_memmove(void* s1, const void* s2, size_t n);
+#endif
+
+#ifdef MPACK_MEMSET
+ #define mpack_memset MPACK_MEMSET
+#else
+ void* mpack_memset(void* s, int c, size_t n);
+#endif
+
+#ifdef MPACK_STRLEN
+ #define mpack_strlen MPACK_STRLEN
+#else
+ size_t mpack_strlen(const char* s);
+#endif
+
+
+
+#if MPACK_STDIO
+ #if defined(WIN32)
+ #define mpack_snprintf _snprintf
+ #else
+ #define mpack_snprintf snprintf
+ #endif
+#endif
+
+
+
+/* Debug logging */
+#if 0
+ #include <stdio.h>
+ #define mpack_log(...) (MPACK_EXPAND(printf(__VA_ARGS__)), fflush(stdout))
+#else
+ #define mpack_log(...) ((void)0)
+#endif
+
+
+
+/* Make sure our configuration makes sense */
+#ifndef MPACK_MALLOC
+ #if MPACK_STDIO
+ #error "MPACK_STDIO requires preprocessor definitions for MPACK_MALLOC and MPACK_FREE."
+ #endif
+ #if MPACK_READ_TRACKING
+ #error "MPACK_READ_TRACKING requires preprocessor definitions for MPACK_MALLOC and MPACK_FREE."
+ #endif
+ #if MPACK_WRITE_TRACKING
+ #error "MPACK_WRITE_TRACKING requires preprocessor definitions for MPACK_MALLOC and MPACK_FREE."
+ #endif
+#endif
+
+
+
+/* Implement realloc if unavailable */
+#ifdef MPACK_MALLOC
+ #ifdef MPACK_REALLOC
+ MPACK_INLINE void* mpack_realloc(void* old_ptr, size_t used_size, size_t new_size) {
+ MPACK_UNUSED(used_size);
+ return MPACK_REALLOC(old_ptr, new_size);
+ }
+ #else
+ void* mpack_realloc(void* old_ptr, size_t used_size, size_t new_size);
+ #endif
+#endif
+
+
+
+/** @endcond */
+/**
+ * @}
+ */
+
+MPACK_EXTERN_C_END
+MPACK_SILENCE_WARNINGS_END
+
+#endif
+
+/* mpack/mpack-common.h.h */
+
+/**
+ * @file
+ *
+ * Defines types and functions shared by the MPack reader and writer.
+ */
+
+#ifndef MPACK_COMMON_H
+#define MPACK_COMMON_H 1
+
+/* #include "mpack-platform.h" */
+
+#ifndef MPACK_PRINT_BYTE_COUNT
+#define MPACK_PRINT_BYTE_COUNT 12
+#endif
+
+MPACK_SILENCE_WARNINGS_BEGIN
+MPACK_EXTERN_C_BEGIN
+
+
+
+/**
+ * @defgroup common Tags and Common Elements
+ *
+ * Contains types, constants and functions shared by both the encoding
+ * and decoding portions of MPack.
+ *
+ * @{
+ */
+
+/* Version information */
+
+#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. */
+
+/** A number containing the version number of MPack for comparison purposes. */
+#define MPACK_VERSION ((MPACK_VERSION_MAJOR * 10000) + \
+ (MPACK_VERSION_MINOR * 100) + MPACK_VERSION_PATCH)
+
+/** A macro to test for a minimum version of MPack. */
+#define MPACK_VERSION_AT_LEAST(major, minor, patch) \
+ (MPACK_VERSION >= (((major) * 10000) + ((minor) * 100) + (patch)))
+
+/** @cond */
+#if (MPACK_VERSION_PATCH > 0)
+#define MPACK_VERSION_STRING_BASE \
+ MPACK_STRINGIFY(MPACK_VERSION_MAJOR) "." \
+ MPACK_STRINGIFY(MPACK_VERSION_MINOR) "." \
+ MPACK_STRINGIFY(MPACK_VERSION_PATCH)
+#else
+#define MPACK_VERSION_STRING_BASE \
+ MPACK_STRINGIFY(MPACK_VERSION_MAJOR) "." \
+ MPACK_STRINGIFY(MPACK_VERSION_MINOR)
+#endif
+/** @endcond */
+
+/**
+ * @def MPACK_VERSION_STRING
+ * @hideinitializer
+ *
+ * A string containing the MPack version.
+ */
+#if MPACK_RELEASE_VERSION
+#define MPACK_VERSION_STRING MPACK_VERSION_STRING_BASE
+#else
+#define MPACK_VERSION_STRING MPACK_VERSION_STRING_BASE "dev"
+#endif
+
+/**
+ * @def MPACK_LIBRARY_STRING
+ * @hideinitializer
+ *
+ * A string describing MPack, containing the library name, version and debug mode.
+ */
+#if MPACK_DEBUG
+#define MPACK_LIBRARY_STRING "MPack " MPACK_VERSION_STRING "-debug"
+#else
+#define MPACK_LIBRARY_STRING "MPack " MPACK_VERSION_STRING
+#endif
+
+/** @cond */
+/**
+ * @def MPACK_MAXIMUM_TAG_SIZE
+ *
+ * The maximum encoded size of a tag in bytes.
+ */
+#define MPACK_MAXIMUM_TAG_SIZE 9
+/** @endcond */
+
+#if MPACK_EXTENSIONS
+/**
+ * @def MPACK_TIMESTAMP_NANOSECONDS_MAX
+ *
+ * The maximum value of nanoseconds for a timestamp.
+ *
+ * @note This requires @ref MPACK_EXTENSIONS.
+ */
+#define MPACK_TIMESTAMP_NANOSECONDS_MAX 999999999
+#endif
+
+
+
+#if MPACK_COMPATIBILITY
+/**
+ * Versions of the MessagePack format.
+ *
+ * A reader, writer, or tree can be configured to serialize in an older
+ * version of the MessagePack spec. This is necessary to interface with
+ * older MessagePack libraries that do not support new MessagePack features.
+ *
+ * @note This requires @ref MPACK_COMPATIBILITY.
+ */
+typedef enum mpack_version_t {
+
+ /**
+ * Version 1.0/v4, supporting only the @c raw type without @c str8.
+ */
+ mpack_version_v4 = 4,
+
+ /**
+ * Version 2.0/v5, supporting the @c str8, @c bin and @c ext types.
+ */
+ mpack_version_v5 = 5,
+
+ /**
+ * The most recent supported version of MessagePack. This is the default.
+ */
+ mpack_version_current = mpack_version_v5,
+
+} mpack_version_t;
+#endif
+
+/**
+ * Error states for MPack objects.
+ *
+ * When a reader, writer, or tree is in an error state, all subsequent calls
+ * are ignored and their return values are nil/zero. You should check whether
+ * the source is in an error state before using such values.
+ */
+typedef enum mpack_error_t {
+ mpack_ok = 0, /**< No error. */
+ mpack_error_io = 2, /**< The reader or writer failed to fill or flush, or some other file or socket error occurred. */
+ mpack_error_invalid, /**< The data read is not valid MessagePack. */
+ mpack_error_unsupported, /**< The data read is not supported by this configuration of MPack. (See @ref MPACK_EXTENSIONS.) */
+ mpack_error_type, /**< The type or value range did not match what was expected by the caller. */
+ mpack_error_too_big, /**< A read or write was bigger than the maximum size allowed for that operation. */
+ mpack_error_memory, /**< An allocation failure occurred. */
+ mpack_error_bug, /**< The MPack API was used incorrectly. (This will always assert in debug mode.) */
+ mpack_error_data, /**< The contained data is not valid. */
+ mpack_error_eof, /**< The reader failed to read because of file or socket EOF */
+} mpack_error_t;
+
+/**
+ * Converts an MPack error to a string. This function returns an empty
+ * string when MPACK_DEBUG is not set.
+ */
+const char* mpack_error_to_string(mpack_error_t error);
+
+/**
+ * Defines the type of a MessagePack tag.
+ *
+ * Note that extension types, both user defined and built-in, are represented
+ * in tags as @ref mpack_type_ext. The value for an extension type is stored
+ * separately.
+ */
+typedef enum mpack_type_t {
+ mpack_type_missing = 0, /**< Special type indicating a missing optional value. */
+ mpack_type_nil, /**< A null value. */
+ mpack_type_bool, /**< A boolean (true or false.) */
+ mpack_type_int, /**< A 64-bit signed integer. */
+ mpack_type_uint, /**< A 64-bit unsigned integer. */
+ mpack_type_float, /**< A 32-bit IEEE 754 floating point number. */
+ mpack_type_double, /**< A 64-bit IEEE 754 floating point number. */
+ mpack_type_str, /**< A string. */
+ mpack_type_bin, /**< A chunk of binary data. */
+ mpack_type_array, /**< An array of MessagePack objects. */
+ mpack_type_map, /**< An ordered map of key/value pairs of MessagePack objects. */
+
+ #if MPACK_EXTENSIONS
+ /**
+ * A typed MessagePack extension object containing a chunk of binary data.
+ *
+ * @note This requires @ref MPACK_EXTENSIONS.
+ */
+ mpack_type_ext,
+ #endif
+} mpack_type_t;
+
+/**
+ * Converts an MPack type to a string. This function returns an empty
+ * string when MPACK_DEBUG is not set.
+ */
+const char* mpack_type_to_string(mpack_type_t type);
+
+#if MPACK_EXTENSIONS
+/**
+ * A timestamp.
+ *
+ * @note This requires @ref MPACK_EXTENSIONS.
+ */
+typedef struct mpack_timestamp_t {
+ int64_t seconds; /*< The number of seconds (signed) since 1970-01-01T00:00:00Z. */
+ uint32_t nanoseconds; /*< The number of additional nanoseconds, between 0 and 999,999,999. */
+} mpack_timestamp_t;
+#endif
+
+/**
+ * An MPack tag is a MessagePack object header. It is a variant type
+ * representing any kind of object, and includes the length of compound types
+ * (e.g. map, array, string) or the value of non-compound types (e.g. boolean,
+ * integer, float.)
+ *
+ * If the type is compound (str, bin, ext, array or map), the contained
+ * elements or bytes are stored separately.
+ *
+ * This structure is opaque; its fields should not be accessed outside
+ * of MPack.
+ */
+typedef struct mpack_tag_t mpack_tag_t;
+
+/* Hide internals from documentation */
+/** @cond */
+struct mpack_tag_t {
+ mpack_type_t type; /*< The type of value. */
+
+ #if MPACK_EXTENSIONS
+ int8_t exttype; /*< The extension type if the type is @ref mpack_type_ext. */
+ #endif
+
+ /* The value for non-compound types. */
+ union {
+ uint64_t u; /*< The value if the type is unsigned int. */
+ int64_t i; /*< The value if the type is signed int. */
+ bool b; /*< The value if the type is bool. */
+
+ #if MPACK_FLOAT
+ float f; /*< The value if the type is float. */
+ #else
+ uint32_t f; /*< The raw value if the type is float. */
+ #endif
+
+ #if MPACK_DOUBLE
+ double d; /*< The value if the type is double. */
+ #else
+ uint64_t d; /*< The raw value if the type is double. */
+ #endif
+
+ /* The number of bytes if the type is str, bin or ext. */
+ uint32_t l;
+
+ /* The element count if the type is an array, or the number of
+ key/value pairs if the type is map. */
+ uint32_t n;
+ } v;
+};
+/** @endcond */
+
+/**
+ * @name Tag Generators
+ * @{
+ */
+
+/**
+ * @def MPACK_TAG_ZERO
+ *
+ * An @ref mpack_tag_t initializer that zeroes the given tag.
+ *
+ * @warning This does not make the tag nil! The tag's type is invalid when
+ * initialized this way. Use @ref mpack_tag_make_nil() to generate a nil tag.
+ */
+#if MPACK_EXTENSIONS
+#define MPACK_TAG_ZERO {(mpack_type_t)0, 0, {0}}
+#else
+#define MPACK_TAG_ZERO {(mpack_type_t)0, {0}}
+#endif
+
+/** Generates a nil tag. */
+MPACK_INLINE mpack_tag_t mpack_tag_make_nil(void) {
+ mpack_tag_t ret = MPACK_TAG_ZERO;
+ ret.type = mpack_type_nil;
+ return ret;
+}
+
+/** Generates a bool tag. */
+MPACK_INLINE mpack_tag_t mpack_tag_make_bool(bool value) {
+ mpack_tag_t ret = MPACK_TAG_ZERO;
+ ret.type = mpack_type_bool;
+ ret.v.b = value;
+ return ret;
+}
+
+/** Generates a bool tag with value true. */
+MPACK_INLINE mpack_tag_t mpack_tag_make_true(void) {
+ mpack_tag_t ret = MPACK_TAG_ZERO;
+ ret.type = mpack_type_bool;
+ ret.v.b = true;
+ return ret;
+}
+
+/** Generates a bool tag with value false. */
+MPACK_INLINE mpack_tag_t mpack_tag_make_false(void) {
+ mpack_tag_t ret = MPACK_TAG_ZERO;
+ ret.type = mpack_type_bool;
+ ret.v.b = false;
+ return ret;
+}
+
+/** Generates a signed int tag. */
+MPACK_INLINE mpack_tag_t mpack_tag_make_int(int64_t value) {
+ mpack_tag_t ret = MPACK_TAG_ZERO;
+ ret.type = mpack_type_int;
+ ret.v.i = value;
+ return ret;
+}
+
+/** Generates an unsigned int tag. */
+MPACK_INLINE mpack_tag_t mpack_tag_make_uint(uint64_t value) {
+ mpack_tag_t ret = MPACK_TAG_ZERO;
+ ret.type = mpack_type_uint;
+ ret.v.u = value;
+ return ret;
+}
+
+#if MPACK_FLOAT
+/** Generates a float tag. */
+MPACK_INLINE mpack_tag_t mpack_tag_make_float(float value)
+#else
+/** Generates a float tag from a raw uint32_t. */
+MPACK_INLINE mpack_tag_t mpack_tag_make_raw_float(uint32_t value)
+#endif
+{
+ mpack_tag_t ret = MPACK_TAG_ZERO;
+ ret.type = mpack_type_float;
+ ret.v.f = value;
+ return ret;
+}
+
+#if MPACK_DOUBLE
+/** Generates a double tag. */
+MPACK_INLINE mpack_tag_t mpack_tag_make_double(double value)
+#else
+/** Generates a double tag from a raw uint64_t. */
+MPACK_INLINE mpack_tag_t mpack_tag_make_raw_double(uint64_t value)
+#endif
+{
+ mpack_tag_t ret = MPACK_TAG_ZERO;
+ ret.type = mpack_type_double;
+ ret.v.d = value;
+ return ret;
+}
+
+/** Generates an array tag. */
+MPACK_INLINE mpack_tag_t mpack_tag_make_array(uint32_t count) {
+ mpack_tag_t ret = MPACK_TAG_ZERO;
+ ret.type = mpack_type_array;
+ ret.v.n = count;
+ return ret;
+}
+
+/** Generates a map tag. */
+MPACK_INLINE mpack_tag_t mpack_tag_make_map(uint32_t count) {
+ mpack_tag_t ret = MPACK_TAG_ZERO;
+ ret.type = mpack_type_map;
+ ret.v.n = count;
+ return ret;
+}
+
+/** Generates a str tag. */
+MPACK_INLINE mpack_tag_t mpack_tag_make_str(uint32_t length) {
+ mpack_tag_t ret = MPACK_TAG_ZERO;
+ ret.type = mpack_type_str;
+ ret.v.l = length;
+ return ret;
+}
+
+/** Generates a bin tag. */
+MPACK_INLINE mpack_tag_t mpack_tag_make_bin(uint32_t length) {
+ mpack_tag_t ret = MPACK_TAG_ZERO;
+ ret.type = mpack_type_bin;
+ ret.v.l = length;
+ return ret;
+}
+
+#if MPACK_EXTENSIONS
+/**
+ * Generates an ext tag.
+ *
+ * @note This requires @ref MPACK_EXTENSIONS.
+ */
+MPACK_INLINE mpack_tag_t mpack_tag_make_ext(int8_t exttype, uint32_t length) {
+ mpack_tag_t ret = MPACK_TAG_ZERO;
+ ret.type = mpack_type_ext;
+ ret.exttype = exttype;
+ ret.v.l = length;
+ return ret;
+}
+#endif
+
+/**
+ * @}
+ */
+
+/**
+ * @name Tag Querying Functions
+ * @{
+ */
+
+/**
+ * Gets the type of a tag.
+ */
+MPACK_INLINE mpack_type_t mpack_tag_type(mpack_tag_t* tag) {
+ return tag->type;
+}
+
+/**
+ * Gets the boolean value of a bool-type tag. The tag must be of type @ref
+ * mpack_type_bool.
+ *
+ * This asserts that the type in the tag is @ref mpack_type_bool. (No check is
+ * performed if MPACK_DEBUG is not set.)
+ */
+MPACK_INLINE bool mpack_tag_bool_value(mpack_tag_t* tag) {
+ mpack_assert(tag->type == mpack_type_bool, "tag is not a bool!");
+ return tag->v.b;
+}
+
+/**
+ * Gets the signed integer value of an int-type tag.
+ *
+ * This asserts that the type in the tag is @ref mpack_type_int. (No check is
+ * performed if MPACK_DEBUG is not set.)
+ *
+ * @warning This does not convert between signed and unsigned tags! A positive
+ * integer may be stored in a tag as either @ref mpack_type_int or @ref
+ * mpack_type_uint. You must check the type first; this can only be used if the
+ * type is @ref mpack_type_int.
+ *
+ * @see mpack_type_int
+ */
+MPACK_INLINE int64_t mpack_tag_int_value(mpack_tag_t* tag) {
+ mpack_assert(tag->type == mpack_type_int, "tag is not an int!");
+ return tag->v.i;
+}
+
+/**
+ * Gets the unsigned integer value of a uint-type tag.
+ *
+ * This asserts that the type in the tag is @ref mpack_type_uint. (No check is
+ * performed if MPACK_DEBUG is not set.)
+ *
+ * @warning This does not convert between signed and unsigned tags! A positive
+ * integer may be stored in a tag as either @ref mpack_type_int or @ref
+ * mpack_type_uint. You must check the type first; this can only be used if the
+ * type is @ref mpack_type_uint.
+ *
+ * @see mpack_type_uint
+ */
+MPACK_INLINE uint64_t mpack_tag_uint_value(mpack_tag_t* tag) {
+ mpack_assert(tag->type == mpack_type_uint, "tag is not a uint!");
+ return tag->v.u;
+}
+
+/**
+ * Gets the float value of a float-type tag.
+ *
+ * This asserts that the type in the tag is @ref mpack_type_float. (No check is
+ * performed if MPACK_DEBUG is not set.)
+ *
+ * @warning This does not convert between float and double tags! This can only
+ * be used if the type is @ref mpack_type_float.
+ *
+ * @see mpack_type_float
+ */
+MPACK_INLINE
+#if MPACK_FLOAT
+float mpack_tag_float_value(mpack_tag_t* tag)
+#else
+uint32_t mpack_tag_raw_float_value(mpack_tag_t* tag)
+#endif
+{
+ mpack_assert(tag->type == mpack_type_float, "tag is not a float!");
+ return tag->v.f;
+}
+
+/**
+ * Gets the double value of a double-type tag.
+ *
+ * This asserts that the type in the tag is @ref mpack_type_double. (No check
+ * is performed if MPACK_DEBUG is not set.)
+ *
+ * @warning This does not convert between float and double tags! This can only
+ * be used if the type is @ref mpack_type_double.
+ *
+ * @see mpack_type_double
+ */
+MPACK_INLINE
+#if MPACK_DOUBLE
+double mpack_tag_double_value(mpack_tag_t* tag)
+#else
+uint64_t mpack_tag_raw_double_value(mpack_tag_t* tag)
+#endif
+{
+ mpack_assert(tag->type == mpack_type_double, "tag is not a double!");
+ return tag->v.d;
+}
+
+/**
+ * Gets the number of elements in an array tag.
+ *
+ * This asserts that the type in the tag is @ref mpack_type_array. (No check is
+ * performed if MPACK_DEBUG is not set.)
+ *
+ * @see mpack_type_array
+ */
+MPACK_INLINE uint32_t mpack_tag_array_count(mpack_tag_t* tag) {
+ mpack_assert(tag->type == mpack_type_array, "tag is not an array!");
+ return tag->v.n;
+}
+
+/**
+ * Gets the number of key-value pairs in a map tag.
+ *
+ * This asserts that the type in the tag is @ref mpack_type_map. (No check is
+ * performed if MPACK_DEBUG is not set.)
+ *
+ * @see mpack_type_map
+ */
+MPACK_INLINE uint32_t mpack_tag_map_count(mpack_tag_t* tag) {
+ mpack_assert(tag->type == mpack_type_map, "tag is not a map!");
+ return tag->v.n;
+}
+
+/**
+ * Gets the length in bytes of a str-type tag.
+ *
+ * This asserts that the type in the tag is @ref mpack_type_str. (No check is
+ * performed if MPACK_DEBUG is not set.)
+ *
+ * @see mpack_type_str
+ */
+MPACK_INLINE uint32_t mpack_tag_str_length(mpack_tag_t* tag) {
+ mpack_assert(tag->type == mpack_type_str, "tag is not a str!");
+ return tag->v.l;
+}
+
+/**
+ * Gets the length in bytes of a bin-type tag.
+ *
+ * This asserts that the type in the tag is @ref mpack_type_bin. (No check is
+ * performed if MPACK_DEBUG is not set.)
+ *
+ * @see mpack_type_bin
+ */
+MPACK_INLINE uint32_t mpack_tag_bin_length(mpack_tag_t* tag) {
+ mpack_assert(tag->type == mpack_type_bin, "tag is not a bin!");
+ return tag->v.l;
+}
+
+#if MPACK_EXTENSIONS
+/**
+ * Gets the length in bytes of an ext-type tag.
+ *
+ * This asserts that the type in the tag is @ref mpack_type_ext. (No check is
+ * performed if MPACK_DEBUG is not set.)
+ *
+ * @note This requires @ref MPACK_EXTENSIONS.
+ *
+ * @see mpack_type_ext
+ */
+MPACK_INLINE uint32_t mpack_tag_ext_length(mpack_tag_t* tag) {
+ mpack_assert(tag->type == mpack_type_ext, "tag is not an ext!");
+ return tag->v.l;
+}
+
+/**
+ * Gets the extension type (exttype) of an ext-type tag.
+ *
+ * This asserts that the type in the tag is @ref mpack_type_ext. (No check is
+ * performed if MPACK_DEBUG is not set.)
+ *
+ * @note This requires @ref MPACK_EXTENSIONS.
+ *
+ * @see mpack_type_ext
+ */
+MPACK_INLINE int8_t mpack_tag_ext_exttype(mpack_tag_t* tag) {
+ mpack_assert(tag->type == mpack_type_ext, "tag is not an ext!");
+ return tag->exttype;
+}
+#endif
+
+/**
+ * Gets the length in bytes of a str-, bin- or ext-type tag.
+ *
+ * This asserts that the type in the tag is @ref mpack_type_str, @ref
+ * mpack_type_bin or @ref mpack_type_ext. (No check is performed if MPACK_DEBUG
+ * is not set.)
+ *
+ * @see mpack_type_str
+ * @see mpack_type_bin
+ * @see mpack_type_ext
+ */
+MPACK_INLINE uint32_t mpack_tag_bytes(mpack_tag_t* tag) {
+ #if MPACK_EXTENSIONS
+ mpack_assert(tag->type == mpack_type_str || tag->type == mpack_type_bin
+ || tag->type == mpack_type_ext, "tag is not a str, bin or ext!");
+ #else
+ mpack_assert(tag->type == mpack_type_str || tag->type == mpack_type_bin,
+ "tag is not a str or bin!");
+ #endif
+ return tag->v.l;
+}
+
+/**
+ * @}
+ */
+
+/**
+ * @name Other tag functions
+ * @{
+ */
+
+#if MPACK_EXTENSIONS
+/**
+ * The extension type for a timestamp.
+ *
+ * @note This requires @ref MPACK_EXTENSIONS.
+ */
+#define MPACK_EXTTYPE_TIMESTAMP ((int8_t)(-1))
+#endif
+
+/**
+ * Compares two tags with an arbitrary fixed ordering. Returns 0 if the tags are
+ * equal, a negative integer if left comes before right, or a positive integer
+ * otherwise.
+ *
+ * \warning The ordering is not guaranteed to be preserved across MPack versions; do
+ * not rely on it in persistent data.
+ *
+ * \warning Floating point numbers are compared bit-for-bit, not using the language's
+ * operator==. This means that NaNs with matching representation will compare equal.
+ * This behaviour is up for debate; see comments in the definition of mpack_tag_cmp().
+ *
+ * See mpack_tag_equal() for more information on when tags are considered equal.
+ */
+int mpack_tag_cmp(mpack_tag_t left, mpack_tag_t right);
+
+/**
+ * Compares two tags for equality. Tags are considered equal if the types are compatible
+ * and the values (for non-compound types) are equal.
+ *
+ * The field width of variable-width fields is ignored (and in fact is not stored
+ * in a tag), and positive numbers in signed integers are considered equal to their
+ * unsigned counterparts. So for example the value 1 stored as a positive fixint
+ * is equal to the value 1 stored in a 64-bit unsigned integer field.
+ *
+ * The "extension type" of an extension object is considered part of the value
+ * and must match exactly.
+ *
+ * \warning Floating point numbers are compared bit-for-bit, not using the language's
+ * operator==. This means that NaNs with matching representation will compare equal.
+ * This behaviour is up for debate; see comments in the definition of mpack_tag_cmp().
+ */
+MPACK_INLINE bool mpack_tag_equal(mpack_tag_t left, mpack_tag_t right) {
+ return mpack_tag_cmp(left, right) == 0;
+}
+
+#if MPACK_DEBUG && MPACK_STDIO
+/**
+ * Generates a json-like debug description of the given tag into the given buffer.
+ *
+ * This is only available in debug mode, and only if stdio is available (since
+ * it uses snprintf().) It's strictly for debugging purposes.
+ *
+ * The prefix is used to print the first few hexadecimal bytes of a bin or ext
+ * type. Pass NULL if not a bin or ext.
+ */
+void mpack_tag_debug_pseudo_json(mpack_tag_t tag, char* buffer, size_t buffer_size,
+ const char* prefix, size_t prefix_size);
+
+/**
+ * Generates a debug string description of the given tag into the given buffer.
+ *
+ * This is only available in debug mode, and only if stdio is available (since
+ * it uses snprintf().) It's strictly for debugging purposes.
+ */
+void mpack_tag_debug_describe(mpack_tag_t tag, char* buffer, size_t buffer_size);
+
+/** @cond */
+
+/*
+ * A callback function for printing pseudo-JSON for debugging purposes.
+ *
+ * @see mpack_node_print_callback
+ */
+typedef void (*mpack_print_callback_t)(void* context, const char* data, size_t count);
+
+// helpers for printing debug output
+// i feel a bit like i'm re-implementing a buffered writer again...
+typedef struct mpack_print_t {
+ char* buffer;
+ size_t size;
+ size_t count;
+ mpack_print_callback_t callback;
+ void* context;
+} mpack_print_t;
+
+void mpack_print_append(mpack_print_t* print, const char* data, size_t count);
+
+MPACK_INLINE void mpack_print_append_cstr(mpack_print_t* print, const char* cstr) {
+ mpack_print_append(print, cstr, mpack_strlen(cstr));
+}
+
+void mpack_print_flush(mpack_print_t* print);
+
+void mpack_print_file_callback(void* context, const char* data, size_t count);
+
+/** @endcond */
+
+#endif
+
+/**
+ * @}
+ */
+
+/**
+ * @name Deprecated Tag Generators
+ * @{
+ */
+
+/*
+ * "make" has been added to their names to disambiguate them from the
+ * value-fetching functions (e.g. mpack_tag_make_bool() vs
+ * mpack_tag_bool_value().)
+ *
+ * The length and count for all compound types was the wrong sign (int32_t
+ * instead of uint32_t.) These preserve the old behaviour; the new "make"
+ * functions have the correct sign.
+ */
+
+/** \deprecated Renamed to mpack_tag_make_nil(). */
+MPACK_INLINE mpack_tag_t mpack_tag_nil(void) {
+ return mpack_tag_make_nil();
+}
+
+/** \deprecated Renamed to mpack_tag_make_bool(). */
+MPACK_INLINE mpack_tag_t mpack_tag_bool(bool value) {
+ return mpack_tag_make_bool(value);
+}
+
+/** \deprecated Renamed to mpack_tag_make_true(). */
+MPACK_INLINE mpack_tag_t mpack_tag_true(void) {
+ return mpack_tag_make_true();
+}
+
+/** \deprecated Renamed to mpack_tag_make_false(). */
+MPACK_INLINE mpack_tag_t mpack_tag_false(void) {
+ return mpack_tag_make_false();
+}
+
+/** \deprecated Renamed to mpack_tag_make_int(). */
+MPACK_INLINE mpack_tag_t mpack_tag_int(int64_t value) {
+ return mpack_tag_make_int(value);
+}
+
+/** \deprecated Renamed to mpack_tag_make_uint(). */
+MPACK_INLINE mpack_tag_t mpack_tag_uint(uint64_t value) {
+ return mpack_tag_make_uint(value);
+}
+
+#if MPACK_FLOAT
+/** \deprecated Renamed to mpack_tag_make_float(). */
+MPACK_INLINE mpack_tag_t mpack_tag_float(float value) {
+ return mpack_tag_make_float(value);
+}
+#endif
+
+#if MPACK_DOUBLE
+/** \deprecated Renamed to mpack_tag_make_double(). */
+MPACK_INLINE mpack_tag_t mpack_tag_double(double value) {
+ return mpack_tag_make_double(value);
+}
+#endif
+
+/** \deprecated Renamed to mpack_tag_make_array(). */
+MPACK_INLINE mpack_tag_t mpack_tag_array(int32_t count) {
+ return mpack_tag_make_array((uint32_t)count);
+}
+
+/** \deprecated Renamed to mpack_tag_make_map(). */
+MPACK_INLINE mpack_tag_t mpack_tag_map(int32_t count) {
+ return mpack_tag_make_map((uint32_t)count);
+}
+
+/** \deprecated Renamed to mpack_tag_make_str(). */
+MPACK_INLINE mpack_tag_t mpack_tag_str(int32_t length) {
+ return mpack_tag_make_str((uint32_t)length);
+}
+
+/** \deprecated Renamed to mpack_tag_make_bin(). */
+MPACK_INLINE mpack_tag_t mpack_tag_bin(int32_t length) {
+ return mpack_tag_make_bin((uint32_t)length);
+}
+
+#if MPACK_EXTENSIONS
+/** \deprecated Renamed to mpack_tag_make_ext(). */
+MPACK_INLINE mpack_tag_t mpack_tag_ext(int8_t exttype, int32_t length) {
+ return mpack_tag_make_ext(exttype, (uint32_t)length);
+}
+#endif
+
+/**
+ * @}
+ */
+
+/** @cond */
+
+/*
+ * Helpers to perform unaligned network-endian loads and stores
+ * at arbitrary addresses. Byte-swapping builtins are used if they
+ * are available and if they improve performance.
+ *
+ * These will remain available in the public API so feel free to
+ * use them for other purposes, but they are undocumented.
+ */
+
+MPACK_INLINE uint8_t mpack_load_u8(const char* p) {
+ return (uint8_t)p[0];
+}
+
+MPACK_INLINE uint16_t mpack_load_u16(const char* p) {
+ #ifdef MPACK_NHSWAP16
+ uint16_t val;
+ mpack_memcpy(&val, p, sizeof(val));
+ return MPACK_NHSWAP16(val);
+ #else
+ return (uint16_t)((((uint16_t)(uint8_t)p[0]) << 8) |
+ ((uint16_t)(uint8_t)p[1]));
+ #endif
+}
+
+MPACK_INLINE uint32_t mpack_load_u32(const char* p) {
+ #ifdef MPACK_NHSWAP32
+ uint32_t val;
+ mpack_memcpy(&val, p, sizeof(val));
+ return MPACK_NHSWAP32(val);
+ #else
+ return (((uint32_t)(uint8_t)p[0]) << 24) |
+ (((uint32_t)(uint8_t)p[1]) << 16) |
+ (((uint32_t)(uint8_t)p[2]) << 8) |
+ ((uint32_t)(uint8_t)p[3]);
+ #endif
+}
+
+MPACK_INLINE uint64_t mpack_load_u64(const char* p) {
+ #ifdef MPACK_NHSWAP64
+ uint64_t val;
+ mpack_memcpy(&val, p, sizeof(val));
+ return MPACK_NHSWAP64(val);
+ #else
+ return (((uint64_t)(uint8_t)p[0]) << 56) |
+ (((uint64_t)(uint8_t)p[1]) << 48) |
+ (((uint64_t)(uint8_t)p[2]) << 40) |
+ (((uint64_t)(uint8_t)p[3]) << 32) |
+ (((uint64_t)(uint8_t)p[4]) << 24) |
+ (((uint64_t)(uint8_t)p[5]) << 16) |
+ (((uint64_t)(uint8_t)p[6]) << 8) |
+ ((uint64_t)(uint8_t)p[7]);
+ #endif
+}
+
+MPACK_INLINE void mpack_store_u8(char* p, uint8_t val) {
+ uint8_t* u = (uint8_t*)p;
+ u[0] = val;
+}
+
+MPACK_INLINE void mpack_store_u16(char* p, uint16_t val) {
+ #ifdef MPACK_NHSWAP16
+ val = MPACK_NHSWAP16(val);
+ mpack_memcpy(p, &val, sizeof(val));
+ #else
+ uint8_t* u = (uint8_t*)p;
+ u[0] = (uint8_t)((val >> 8) & 0xFF);
+ u[1] = (uint8_t)( val & 0xFF);
+ #endif
+}
+
+MPACK_INLINE void mpack_store_u32(char* p, uint32_t val) {
+ #ifdef MPACK_NHSWAP32
+ val = MPACK_NHSWAP32(val);
+ mpack_memcpy(p, &val, sizeof(val));
+ #else
+ uint8_t* u = (uint8_t*)p;
+ u[0] = (uint8_t)((val >> 24) & 0xFF);
+ u[1] = (uint8_t)((val >> 16) & 0xFF);
+ u[2] = (uint8_t)((val >> 8) & 0xFF);
+ u[3] = (uint8_t)( val & 0xFF);
+ #endif
+}
+
+MPACK_INLINE void mpack_store_u64(char* p, uint64_t val) {
+ #ifdef MPACK_NHSWAP64
+ val = MPACK_NHSWAP64(val);
+ mpack_memcpy(p, &val, sizeof(val));
+ #else
+ uint8_t* u = (uint8_t*)p;
+ u[0] = (uint8_t)((val >> 56) & 0xFF);
+ u[1] = (uint8_t)((val >> 48) & 0xFF);
+ u[2] = (uint8_t)((val >> 40) & 0xFF);
+ u[3] = (uint8_t)((val >> 32) & 0xFF);
+ u[4] = (uint8_t)((val >> 24) & 0xFF);
+ u[5] = (uint8_t)((val >> 16) & 0xFF);
+ u[6] = (uint8_t)((val >> 8) & 0xFF);
+ u[7] = (uint8_t)( val & 0xFF);
+ #endif
+}
+
+MPACK_INLINE int8_t mpack_load_i8 (const char* p) {return (int8_t) mpack_load_u8 (p);}
+MPACK_INLINE int16_t mpack_load_i16(const char* p) {return (int16_t)mpack_load_u16(p);}
+MPACK_INLINE int32_t mpack_load_i32(const char* p) {return (int32_t)mpack_load_u32(p);}
+MPACK_INLINE int64_t mpack_load_i64(const char* p) {return (int64_t)mpack_load_u64(p);}
+MPACK_INLINE void mpack_store_i8 (char* p, int8_t val) {mpack_store_u8 (p, (uint8_t) val);}
+MPACK_INLINE void mpack_store_i16(char* p, int16_t val) {mpack_store_u16(p, (uint16_t)val);}
+MPACK_INLINE void mpack_store_i32(char* p, int32_t val) {mpack_store_u32(p, (uint32_t)val);}
+MPACK_INLINE void mpack_store_i64(char* p, int64_t val) {mpack_store_u64(p, (uint64_t)val);}
+
+#if MPACK_FLOAT
+MPACK_INLINE float mpack_load_float(const char* p) {
+ MPACK_CHECK_FLOAT_ORDER();
+ MPACK_STATIC_ASSERT(sizeof(float) == sizeof(uint32_t), "float is wrong size??");
+ union {
+ float f;
+ uint32_t u;
+ } v;
+ v.u = mpack_load_u32(p);
+ return v.f;
+}
+#endif
+
+#if MPACK_DOUBLE
+MPACK_INLINE double mpack_load_double(const char* p) {
+ MPACK_CHECK_FLOAT_ORDER();
+ MPACK_STATIC_ASSERT(sizeof(double) == sizeof(uint64_t), "double is wrong size??");
+ union {
+ double d;
+ uint64_t u;
+ } v;
+ v.u = mpack_load_u64(p);
+ return v.d;
+}
+#endif
+
+#if MPACK_FLOAT
+MPACK_INLINE void mpack_store_float(char* p, float value) {
+ MPACK_CHECK_FLOAT_ORDER();
+ union {
+ float f;
+ uint32_t u;
+ } v;
+ v.f = value;
+ mpack_store_u32(p, v.u);
+}
+#endif
+
+#if MPACK_DOUBLE
+MPACK_INLINE void mpack_store_double(char* p, double value) {
+ MPACK_CHECK_FLOAT_ORDER();
+ union {
+ double d;
+ uint64_t u;
+ } v;
+ v.d = value;
+ mpack_store_u64(p, v.u);
+}
+#endif
+
+#if MPACK_FLOAT && !MPACK_DOUBLE
+/**
+ * Performs a manual shortening conversion on the raw 64-bit representation of
+ * a double. This is useful for parsing doubles on platforms that only support
+ * floats (such as AVR.)
+ *
+ * The significand is truncated rather than rounded and subnormal numbers are
+ * set to 0 so this may not be quite as accurate as a real double-to-float
+ * conversion.
+ */
+MPACK_INLINE float mpack_shorten_raw_double_to_float(uint64_t d) {
+ MPACK_CHECK_FLOAT_ORDER();
+ union {
+ float f;
+ uint32_t u;
+ } v;
+
+ // float has 1 bit sign, 8 bits exponent, 23 bits significand
+ // double has 1 bit sign, 11 bits exponent, 52 bits significand
+
+ uint64_t d_sign = (uint64_t)(d >> 63);
+ uint64_t d_exponent = (uint32_t)(d >> 52) & ((1 << 11) - 1);
+ uint64_t d_significand = d & (((uint64_t)1 << 52) - 1);
+
+ uint32_t f_sign = (uint32_t)d_sign;
+ uint32_t f_exponent;
+ uint32_t f_significand;
+
+ if (MPACK_UNLIKELY(d_exponent == ((1 << 11) - 1))) {
+ // infinity or NAN. shift down to preserve the top bit since it
+ // indicates signaling NAN, but also set the low bit if any bits were
+ // set (that way we can't shift NAN to infinity.)
+ f_exponent = ((1 << 8) - 1);
+ f_significand = (uint32_t)(d_significand >> 29) | (d_significand ? 1 : 0);
+
+ } else {
+ int fix_bias = (int)d_exponent - ((1 << 10) - 1) + ((1 << 7) - 1);
+ if (MPACK_UNLIKELY(fix_bias <= 0)) {
+ // we don't currently handle subnormal numbers. just set it to zero.
+ f_exponent = 0;
+ f_significand = 0;
+ } else if (MPACK_UNLIKELY(fix_bias > 0xff)) {
+ // exponent is too large; saturate to infinity
+ f_exponent = 0xff;
+ f_significand = 0;
+ } else {
+ // a normal number that fits in a float. this is the usual case.
+ f_exponent = (uint32_t)fix_bias;
+ f_significand = (uint32_t)(d_significand >> 29);
+ }
+ }
+
+ #if 0
+ printf("\n===============\n");
+ for (size_t i = 0; i < 64; ++i)
+ printf("%i%s",(int)((d>>(63-i))&1),((i%8)==7)?" ":"");
+ printf("\n%lu %lu %lu\n", d_sign, d_exponent, d_significand);
+ printf("%u %u %u\n", f_sign, f_exponent, f_significand);
+ #endif
+
+ v.u = (f_sign << 31) | (f_exponent << 23) | f_significand;
+ return v.f;
+}
+#endif
+
+/** @endcond */
+
+
+
+/** @cond */
+
+// Sizes in bytes for the various possible tags
+#define MPACK_TAG_SIZE_FIXUINT 1
+#define MPACK_TAG_SIZE_U8 2
+#define MPACK_TAG_SIZE_U16 3
+#define MPACK_TAG_SIZE_U32 5
+#define MPACK_TAG_SIZE_U64 9
+#define MPACK_TAG_SIZE_FIXINT 1
+#define MPACK_TAG_SIZE_I8 2
+#define MPACK_TAG_SIZE_I16 3
+#define MPACK_TAG_SIZE_I32 5
+#define MPACK_TAG_SIZE_I64 9
+#define MPACK_TAG_SIZE_FLOAT 5
+#define MPACK_TAG_SIZE_DOUBLE 9
+#define MPACK_TAG_SIZE_FIXARRAY 1
+#define MPACK_TAG_SIZE_ARRAY16 3
+#define MPACK_TAG_SIZE_ARRAY32 5
+#define MPACK_TAG_SIZE_FIXMAP 1
+#define MPACK_TAG_SIZE_MAP16 3
+#define MPACK_TAG_SIZE_MAP32 5
+#define MPACK_TAG_SIZE_FIXSTR 1
+#define MPACK_TAG_SIZE_STR8 2
+#define MPACK_TAG_SIZE_STR16 3
+#define MPACK_TAG_SIZE_STR32 5
+#define MPACK_TAG_SIZE_BIN8 2
+#define MPACK_TAG_SIZE_BIN16 3
+#define MPACK_TAG_SIZE_BIN32 5
+#define MPACK_TAG_SIZE_FIXEXT1 2
+#define MPACK_TAG_SIZE_FIXEXT2 2
+#define MPACK_TAG_SIZE_FIXEXT4 2
+#define MPACK_TAG_SIZE_FIXEXT8 2
+#define MPACK_TAG_SIZE_FIXEXT16 2
+#define MPACK_TAG_SIZE_EXT8 3
+#define MPACK_TAG_SIZE_EXT16 4
+#define MPACK_TAG_SIZE_EXT32 6
+
+// size in bytes for complete ext types
+#define MPACK_EXT_SIZE_TIMESTAMP4 (MPACK_TAG_SIZE_FIXEXT4 + 4)
+#define MPACK_EXT_SIZE_TIMESTAMP8 (MPACK_TAG_SIZE_FIXEXT8 + 8)
+#define MPACK_EXT_SIZE_TIMESTAMP12 (MPACK_TAG_SIZE_EXT8 + 12)
+
+/** @endcond */
+
+
+
+#if MPACK_READ_TRACKING || MPACK_WRITE_TRACKING
+/* Tracks the write state of compound elements (maps, arrays, */
+/* strings, binary blobs and extension types) */
+/** @cond */
+
+typedef struct mpack_track_element_t {
+ mpack_type_t type;
+ uint32_t left;
+
+ // indicates that a value still needs to be read/written for an already
+ // read/written key. left is not decremented until both key and value are
+ // read/written.
+ bool key_needs_value;
+
+ // tracks whether the map/array being written is using a builder. if true,
+ // the number of elements is automatic, and left is 0.
+ bool builder;
+} mpack_track_element_t;
+
+typedef struct mpack_track_t {
+ size_t count;
+ size_t capacity;
+ mpack_track_element_t* elements;
+} mpack_track_t;
+
+#if MPACK_INTERNAL
+mpack_error_t mpack_track_init(mpack_track_t* track);
+mpack_error_t mpack_track_grow(mpack_track_t* track);
+mpack_error_t mpack_track_push(mpack_track_t* track, mpack_type_t type, uint32_t count);
+mpack_error_t mpack_track_push_builder(mpack_track_t* track, mpack_type_t type);
+mpack_error_t mpack_track_pop(mpack_track_t* track, mpack_type_t type);
+mpack_error_t mpack_track_pop_builder(mpack_track_t* track, mpack_type_t type);
+mpack_error_t mpack_track_element(mpack_track_t* track, bool read);
+mpack_error_t mpack_track_peek_element(mpack_track_t* track, bool read);
+mpack_error_t mpack_track_bytes(mpack_track_t* track, bool read, size_t count);
+mpack_error_t mpack_track_str_bytes_all(mpack_track_t* track, bool read, size_t count);
+mpack_error_t mpack_track_check_empty(mpack_track_t* track);
+mpack_error_t mpack_track_destroy(mpack_track_t* track, bool cancel);
+#endif
+
+/** @endcond */
+#endif
+
+
+
+#if MPACK_INTERNAL
+/** @cond */
+
+
+
+/* Miscellaneous string functions */
+
+/**
+ * Returns true if the given UTF-8 string is valid.
+ */
+bool mpack_utf8_check(const char* str, size_t bytes);
+
+/**
+ * Returns true if the given UTF-8 string is valid and contains no null characters.
+ */
+bool mpack_utf8_check_no_null(const char* str, size_t bytes);
+
+/**
+ * Returns true if the given string has no null bytes.
+ */
+bool mpack_str_check_no_null(const char* str, size_t bytes);
+
+
+
+/** @endcond */
+#endif
+
+
+
+/**
+ * @}
+ */
+
+MPACK_EXTERN_C_END
+MPACK_SILENCE_WARNINGS_END
+
+#endif
+
+
+/* mpack/mpack-writer.h.h */
+
+/**
+ * @file
+ *
+ * Declares the MPack Writer.
+ */
+
+#ifndef MPACK_WRITER_H
+#define MPACK_WRITER_H 1
+
+/* #include "mpack-common.h" */
+
+#if MPACK_WRITER
+
+MPACK_SILENCE_WARNINGS_BEGIN
+MPACK_EXTERN_C_BEGIN
+
+#if MPACK_WRITE_TRACKING
+struct mpack_track_t;
+#endif
+
+/**
+ * @defgroup writer Write API
+ *
+ * The MPack Write API encodes structured data of a fixed (hardcoded) schema to MessagePack.
+ *
+ * @{
+ */
+
+/**
+ * @def MPACK_WRITER_MINIMUM_BUFFER_SIZE
+ *
+ * The minimum buffer size for a writer with a flush function.
+ */
+#define MPACK_WRITER_MINIMUM_BUFFER_SIZE 32
+
+/**
+ * A buffered MessagePack encoder.
+ *
+ * The encoder wraps an existing buffer and, optionally, a flush function.
+ * This allows efficiently encoding to an in-memory buffer or to a stream.
+ *
+ * All write operations are synchronous; they will block until the
+ * data is fully written, or an error occurs.
+ */
+typedef struct mpack_writer_t mpack_writer_t;
+
+/**
+ * The MPack writer's flush function to flush the buffer to the output stream.
+ * It should flag an appropriate error on the writer if flushing fails (usually
+ * mpack_error_io or mpack_error_memory.)
+ *
+ * The specified context for callbacks is at writer->context.
+ */
+typedef void (*mpack_writer_flush_t)(mpack_writer_t* writer, const char* buffer, size_t count);
+
+/**
+ * An error handler function to be called when an error is flagged on
+ * the writer.
+ *
+ * The error handler will only be called once on the first error flagged;
+ * any subsequent writes and errors are ignored, and the writer is
+ * permanently in that error state.
+ *
+ * MPack is safe against non-local jumps out of error handler callbacks.
+ * This means you are allowed to longjmp or throw an exception (in C++,
+ * Objective-C, or with SEH) out of this callback.
+ *
+ * Bear in mind when using longjmp that local non-volatile variables that
+ * have changed are undefined when setjmp() returns, so you can't put the
+ * writer on the stack in the same activation frame as the setjmp without
+ * declaring it volatile.
+ *
+ * You must still eventually destroy the writer. It is not destroyed
+ * automatically when an error is flagged. It is safe to destroy the
+ * writer within this error callback, but you will either need to perform
+ * a non-local jump, or store something in your context to identify
+ * that the writer is destroyed since any future accesses to it cause
+ * undefined behavior.
+ */
+typedef void (*mpack_writer_error_t)(mpack_writer_t* writer, mpack_error_t error);
+
+/**
+ * A teardown function to be called when the writer is destroyed.
+ */
+typedef void (*mpack_writer_teardown_t)(mpack_writer_t* writer);
+
+/* Hide internals from documentation */
+/** @cond */
+
+#if MPACK_BUILDER
+/**
+ * Build buffer pages form a linked list.
+ *
+ * They don't always fill up. If there is not enough space within them to write
+ * a tag or place an mpack_build_t, a new page is allocated. For this reason
+ * they store the number of used bytes.
+ */
+typedef struct mpack_builder_page_t {
+ struct mpack_builder_page_t* next;
+ size_t bytes_used;
+} mpack_builder_page_t;
+
+/**
+ * Builds form a linked list of mpack_build_t, interleaved with their encoded
+ * contents directly in the paged builder buffer.
+ */
+typedef struct mpack_build_t {
+ //mpack_builder_page_t* page;
+ struct mpack_build_t* parent;
+ //struct mpack_build_t* next;
+
+ size_t bytes; // number of bytes between this build and the next one
+ uint32_t count; // number of elements (or key/value pairs) in this map/array
+ mpack_type_t type;
+
+ // depth of nested non-build compound elements within this
+ // build.
+ uint32_t nested_compound_elements;
+
+ // indicates that a value still needs to be written for an already
+ // written key. count is not incremented until both key and value are
+ // written.
+ bool key_needs_value;
+} mpack_build_t;
+
+/**
+ * The builder state. This is stored within mpack_writer_t.
+ */
+typedef struct mpack_builder_t {
+ mpack_build_t* current_build; // build which is accumulating elements
+ mpack_build_t* latest_build; // build which is accumulating bytes
+ mpack_builder_page_t* current_page;
+ mpack_builder_page_t* pages;
+ char* stash_buffer;
+ char* stash_position;
+ char* stash_end;
+ #if MPACK_BUILDER_INTERNAL_STORAGE
+ char internal[MPACK_BUILDER_INTERNAL_STORAGE_SIZE];
+ #endif
+} mpack_builder_t;
+#endif
+
+struct mpack_writer_t {
+ #if MPACK_COMPATIBILITY
+ mpack_version_t version; /* Version of the MessagePack spec to write */
+ #endif
+ mpack_writer_flush_t flush; /* Function to write bytes to the output stream */
+ mpack_writer_error_t error_fn; /* Function to call on error */
+ mpack_writer_teardown_t teardown; /* Function to teardown the context on destroy */
+ void* context; /* Context for writer callbacks */
+
+ char* buffer; /* Byte buffer */
+ char* position; /* Current position within the buffer */
+ char* end; /* The end of the buffer */
+ mpack_error_t error; /* Error state */
+
+ #if MPACK_WRITE_TRACKING
+ mpack_track_t track; /* Stack of map/array/str/bin/ext writes */
+ #endif
+
+ #ifdef MPACK_MALLOC
+ /* Reserved. You can use this space to allocate a custom
+ * context in order to reduce heap allocations. */
+ void* reserved[2];
+ #endif
+
+ #if MPACK_BUILDER
+ mpack_builder_t builder;
+ #endif
+};
+
+
+#if MPACK_WRITE_TRACKING
+void mpack_writer_track_push(mpack_writer_t* writer, mpack_type_t type, uint32_t count);
+void mpack_writer_track_push_builder(mpack_writer_t* writer, mpack_type_t type);
+void mpack_writer_track_pop(mpack_writer_t* writer, mpack_type_t type);
+void mpack_writer_track_pop_builder(mpack_writer_t* writer, mpack_type_t type);
+void mpack_writer_track_bytes(mpack_writer_t* writer, size_t count);
+#else
+MPACK_INLINE void mpack_writer_track_push(mpack_writer_t* writer, mpack_type_t type, uint32_t count) {
+ MPACK_UNUSED(writer);
+ MPACK_UNUSED(type);
+ MPACK_UNUSED(count);
+}
+MPACK_INLINE void mpack_writer_track_push_builder(mpack_writer_t* writer, mpack_type_t type) {
+ MPACK_UNUSED(writer);
+ MPACK_UNUSED(type);
+}
+MPACK_INLINE void mpack_writer_track_pop(mpack_writer_t* writer, mpack_type_t type) {
+ MPACK_UNUSED(writer);
+ MPACK_UNUSED(type);
+}
+MPACK_INLINE void mpack_writer_track_pop_builder(mpack_writer_t* writer, mpack_type_t type) {
+ MPACK_UNUSED(writer);
+ MPACK_UNUSED(type);
+}
+MPACK_INLINE void mpack_writer_track_bytes(mpack_writer_t* writer, size_t count) {
+ MPACK_UNUSED(writer);
+ MPACK_UNUSED(count);
+}
+#endif
+
+/** @endcond */
+
+/**
+ * @name Lifecycle Functions
+ * @{
+ */
+
+/**
+ * Initializes an MPack writer with the given buffer. The writer
+ * does not assume ownership of the buffer.
+ *
+ * Trying to write past the end of the buffer will result in mpack_error_too_big
+ * unless a flush function is set with mpack_writer_set_flush(). To use the data
+ * without flushing, call mpack_writer_buffer_used() to determine the number of
+ * bytes written.
+ *
+ * @param writer The MPack writer.
+ * @param buffer The buffer into which to write MessagePack data.
+ * @param size The size of the buffer.
+ */
+void mpack_writer_init(mpack_writer_t* writer, char* buffer, size_t size);
+
+#ifdef MPACK_MALLOC
+/**
+ * Initializes an MPack writer using a growable buffer.
+ *
+ * The data is placed in the given data pointer if and when the writer
+ * is destroyed without error. The data pointer is NULL during writing,
+ * and will remain NULL if an error occurs.
+ *
+ * The allocated data must be freed with MPACK_FREE() (or simply free()
+ * if MPack's allocator hasn't been customized.)
+ *
+ * @throws mpack_error_memory if the buffer fails to grow when
+ * flushing.
+ *
+ * @param writer The MPack writer.
+ * @param data Where to place the allocated data.
+ * @param size Where to write the size of the data.
+ */
+void mpack_writer_init_growable(mpack_writer_t* writer, char** data, size_t* size);
+#endif
+
+/**
+ * Initializes an MPack writer directly into an error state. Use this if you
+ * are writing a wrapper to mpack_writer_init() which can fail its setup.
+ */
+void mpack_writer_init_error(mpack_writer_t* writer, mpack_error_t error);
+
+#if MPACK_STDIO
+/**
+ * Initializes an MPack writer that writes to a file.
+ *
+ * @throws mpack_error_memory if allocation fails
+ * @throws mpack_error_io if the file cannot be opened
+ */
+void mpack_writer_init_filename(mpack_writer_t* writer, const char* filename);
+
+/**
+ * Deprecated.
+ *
+ * \deprecated Renamed to mpack_writer_init_filename().
+ */
+MPACK_INLINE void mpack_writer_init_file(mpack_writer_t* writer, const char* filename) {
+ mpack_writer_init_filename(writer, filename);
+}
+
+/**
+ * Initializes an MPack writer that writes to a libc FILE. This can be used to
+ * write to stdout or stderr, or to a file opened separately.
+ *
+ * @param writer The MPack writer.
+ * @param stdfile The FILE.
+ * @param close_when_done If true, fclose() will be called on the FILE when it
+ * is no longer needed. If false, the file will not be flushed or
+ * closed when writing is done.
+ *
+ * @note The writer is buffered. If you want to write other data to the FILE in
+ * between messages, you must flush it first.
+ *
+ * @see mpack_writer_flush_message
+ */
+void mpack_writer_init_stdfile(mpack_writer_t* writer, FILE* stdfile, bool close_when_done);
+#endif
+
+/** @cond */
+
+#define mpack_writer_init_stack_line_ex(line, writer) \
+ char mpack_buf_##line[MPACK_STACK_SIZE]; \
+ mpack_writer_init(writer, mpack_buf_##line, sizeof(mpack_buf_##line))
+
+#define mpack_writer_init_stack_line(line, writer) \
+ mpack_writer_init_stack_line_ex(line, writer)
+
+/*
+ * Initializes an MPack writer using stack space as a buffer. A flush function
+ * should be added to the writer to flush the buffer.
+ *
+ * This is currently undocumented since it's not entirely useful on its own.
+ */
+
+#define mpack_writer_init_stack(writer) \
+ mpack_writer_init_stack_line(__LINE__, (writer))
+
+/** @endcond */
+
+/**
+ * Cleans up the MPack writer, flushing and closing the underlying stream,
+ * if any. Returns the final error state of the writer.
+ *
+ * No flushing is performed if the writer is in an error state. The attached
+ * teardown function is called whether or not the writer is in an error state.
+ *
+ * This will assert in tracking mode if the writer is not in an error
+ * state and has any unclosed compound types. If you want to cancel
+ * writing in the middle of a document, you need to flag an error on
+ * the writer before destroying it (such as mpack_error_data).
+ *
+ * Note that a writer may raise an error and call your error handler during
+ * the final flush. It is safe to longjmp or throw out of this error handler,
+ * but if you do, the writer will not be destroyed, and the teardown function
+ * will not be called. You can still get the writer's error state, and you
+ * must call @ref mpack_writer_destroy() again. (The second call is guaranteed
+ * not to call your error handler again since the writer is already in an error
+ * state.)
+ *
+ * @see mpack_writer_set_error_handler
+ * @see mpack_writer_set_flush
+ * @see mpack_writer_set_teardown
+ * @see mpack_writer_flag_error
+ * @see mpack_error_data
+ */
+mpack_error_t mpack_writer_destroy(mpack_writer_t* writer);
+
+/**
+ * @}
+ */
+
+/**
+ * @name Configuration
+ * @{
+ */
+
+#if MPACK_COMPATIBILITY
+/**
+ * Sets the version of the MessagePack spec that will be generated.
+ *
+ * This can be used to interface with older libraries that do not support
+ * the newest MessagePack features (such as the @c str8 type.)
+ *
+ * @note This requires @ref MPACK_COMPATIBILITY.
+ */
+MPACK_INLINE void mpack_writer_set_version(mpack_writer_t* writer, mpack_version_t version) {
+ writer->version = version;
+}
+#endif
+
+/**
+ * Sets the custom pointer to pass to the writer callbacks, such as flush
+ * or teardown.
+ *
+ * @param writer The MPack writer.
+ * @param context User data to pass to the writer callbacks.
+ *
+ * @see mpack_writer_context()
+ */
+MPACK_INLINE void mpack_writer_set_context(mpack_writer_t* writer, void* context) {
+ writer->context = context;
+}
+
+/**
+ * Returns the custom context for writer callbacks.
+ *
+ * @see mpack_writer_set_context
+ * @see mpack_writer_set_flush
+ */
+MPACK_INLINE void* mpack_writer_context(mpack_writer_t* writer) {
+ return writer->context;
+}
+
+/**
+ * Sets the flush function to write out the data when the buffer is full.
+ *
+ * If no flush function is used, trying to write past the end of the
+ * buffer will result in mpack_error_too_big.
+ *
+ * This should normally be used with mpack_writer_set_context() to register
+ * a custom pointer to pass to the flush function.
+ *
+ * @param writer The MPack writer.
+ * @param flush The function to write out data from the buffer.
+ *
+ * @see mpack_writer_context()
+ */
+void mpack_writer_set_flush(mpack_writer_t* writer, mpack_writer_flush_t flush);
+
+/**
+ * Sets the error function to call when an error is flagged on the writer.
+ *
+ * This should normally be used with mpack_writer_set_context() to register
+ * a custom pointer to pass to the error function.
+ *
+ * See the definition of mpack_writer_error_t for more information about
+ * what you can do from an error callback.
+ *
+ * @see mpack_writer_error_t
+ * @param writer The MPack writer.
+ * @param error_fn The function to call when an error is flagged on the writer.
+ */
+MPACK_INLINE void mpack_writer_set_error_handler(mpack_writer_t* writer, mpack_writer_error_t error_fn) {
+ writer->error_fn = error_fn;
+}
+
+/**
+ * Sets the teardown function to call when the writer is destroyed.
+ *
+ * This should normally be used with mpack_writer_set_context() to register
+ * a custom pointer to pass to the teardown function.
+ *
+ * @param writer The MPack writer.
+ * @param teardown The function to call when the writer is destroyed.
+ */
+MPACK_INLINE void mpack_writer_set_teardown(mpack_writer_t* writer, mpack_writer_teardown_t teardown) {
+ writer->teardown = teardown;
+}
+
+/**
+ * @}
+ */
+
+/**
+ * @name Core Writer Functions
+ * @{
+ */
+
+/**
+ * Flushes any buffered data to the underlying stream.
+ *
+ * If the writer is connected to a socket and you are keeping it open,
+ * you will want to call this after writing a message (or set of
+ * messages) so that the data is actually sent.
+ *
+ * It is not necessary to call this if you are not keeping the writer
+ * open afterwards. You can just call `mpack_writer_destroy()` and it
+ * will flush before cleaning up.
+ *
+ * This will assert if no flush function is assigned to the writer.
+ *
+ * If write tracking is enabled, this will break and flag @ref
+ * mpack_error_bug if the writer has any open compound types, ensuring
+ * that no compound types are still open. This prevents a "missing
+ * finish" bug from causing a never-ending message.
+ */
+void mpack_writer_flush_message(mpack_writer_t* writer);
+
+/**
+ * Returns the number of bytes currently stored in the buffer. This
+ * may be less than the total number of bytes written if bytes have
+ * been flushed to an underlying stream.
+ */
+MPACK_INLINE size_t mpack_writer_buffer_used(mpack_writer_t* writer) {
+ return (size_t)(writer->position - writer->buffer);
+}
+
+/**
+ * Returns the amount of space left in the buffer. This may be reset
+ * after a write if bytes are flushed to an underlying stream.
+ */
+MPACK_INLINE size_t mpack_writer_buffer_left(mpack_writer_t* writer) {
+ return (size_t)(writer->end - writer->position);
+}
+
+/**
+ * Returns the (current) size of the buffer. This may change after a write if
+ * the flush callback changes the buffer.
+ */
+MPACK_INLINE size_t mpack_writer_buffer_size(mpack_writer_t* writer) {
+ return (size_t)(writer->end - writer->buffer);
+}
+
+/**
+ * Places the writer in the given error state, calling the error callback if one
+ * is set.
+ *
+ * This allows you to externally flag errors, for example if you are validating
+ * data as you write it, or if you want to cancel writing in the middle of a
+ * document. (The writer will assert if you try to destroy it without error and
+ * with unclosed compound types. In this case you should flag mpack_error_data
+ * before destroying it.)
+ *
+ * If the writer is already in an error state, this call is ignored and no
+ * error callback is called.
+ *
+ * @see mpack_writer_destroy
+ * @see mpack_error_data
+ */
+void mpack_writer_flag_error(mpack_writer_t* writer, mpack_error_t error);
+
+/**
+ * Queries the error state of the MPack writer.
+ *
+ * If a writer is in an error state, you should discard all data since the
+ * last time the error flag was checked. The error flag cannot be cleared.
+ */
+MPACK_INLINE mpack_error_t mpack_writer_error(mpack_writer_t* writer) {
+ return writer->error;
+}
+
+/**
+ * Writes a MessagePack object header (an MPack Tag.)
+ *
+ * If the value is a map, array, string, binary or extension type, the
+ * containing elements or bytes must be written separately and the
+ * appropriate finish function must be called (as though one of the
+ * mpack_start_*() functions was called.)
+ *
+ * @see mpack_write_bytes()
+ * @see mpack_finish_map()
+ * @see mpack_finish_array()
+ * @see mpack_finish_str()
+ * @see mpack_finish_bin()
+ * @see mpack_finish_ext()
+ * @see mpack_finish_type()
+ */
+void mpack_write_tag(mpack_writer_t* writer, mpack_tag_t tag);
+
+/**
+ * @}
+ */
+
+/**
+ * @name Integers
+ * @{
+ */
+
+/** Writes an 8-bit integer in the most efficient packing available. */
+void mpack_write_i8(mpack_writer_t* writer, int8_t value);
+
+/** Writes a 16-bit integer in the most efficient packing available. */
+void mpack_write_i16(mpack_writer_t* writer, int16_t value);
+
+/** Writes a 32-bit integer in the most efficient packing available. */
+void mpack_write_i32(mpack_writer_t* writer, int32_t value);
+
+/** Writes a 64-bit integer in the most efficient packing available. */
+void mpack_write_i64(mpack_writer_t* writer, int64_t value);
+
+/** Writes an integer in the most efficient packing available. */
+MPACK_INLINE void mpack_write_int(mpack_writer_t* writer, int64_t value) {
+ mpack_write_i64(writer, value);
+}
+
+/** Writes an 8-bit unsigned integer in the most efficient packing available. */
+void mpack_write_u8(mpack_writer_t* writer, uint8_t value);
+
+/** Writes an 16-bit unsigned integer in the most efficient packing available. */
+void mpack_write_u16(mpack_writer_t* writer, uint16_t value);
+
+/** Writes an 32-bit unsigned integer in the most efficient packing available. */
+void mpack_write_u32(mpack_writer_t* writer, uint32_t value);
+
+/** Writes an 64-bit unsigned integer in the most efficient packing available. */
+void mpack_write_u64(mpack_writer_t* writer, uint64_t value);
+
+/** Writes an unsigned integer in the most efficient packing available. */
+MPACK_INLINE void mpack_write_uint(mpack_writer_t* writer, uint64_t value) {
+ mpack_write_u64(writer, value);
+}
+
+/**
+ * @}
+ */
+
+/**
+ * @name Other Basic Types
+ * @{
+ */
+
+#if MPACK_FLOAT
+/** Writes a float. */
+void mpack_write_float(mpack_writer_t* writer, float value);
+#else
+/** Writes a float from a raw uint32_t. */
+void mpack_write_raw_float(mpack_writer_t* writer, uint32_t raw_value);
+#endif
+
+#if MPACK_DOUBLE
+/** Writes a double. */
+void mpack_write_double(mpack_writer_t* writer, double value);
+#else
+/** Writes a double from a raw uint64_t. */
+void mpack_write_raw_double(mpack_writer_t* writer, uint64_t raw_value);
+#endif
+
+/** Writes a boolean. */
+void mpack_write_bool(mpack_writer_t* writer, bool value);
+
+/** Writes a boolean with value true. */
+void mpack_write_true(mpack_writer_t* writer);
+
+/** Writes a boolean with value false. */
+void mpack_write_false(mpack_writer_t* writer);
+
+/** Writes a nil. */
+void mpack_write_nil(mpack_writer_t* writer);
+
+/** Write a pre-encoded messagepack object */
+void mpack_write_object_bytes(mpack_writer_t* writer, const char* data, size_t bytes);
+
+#if MPACK_EXTENSIONS
+/**
+ * Writes a timestamp.
+ *
+ * @note This requires @ref MPACK_EXTENSIONS.
+ *
+ * @param writer The writer
+ * @param seconds The (signed) number of seconds since 1970-01-01T00:00:00Z.
+ * @param nanoseconds The additional number of nanoseconds from 0 to 999,999,999 inclusive.
+ */
+void mpack_write_timestamp(mpack_writer_t* writer, int64_t seconds, uint32_t nanoseconds);
+
+/**
+ * Writes a timestamp with the given number of seconds (and zero nanoseconds).
+ *
+ * @note This requires @ref MPACK_EXTENSIONS.
+ *
+ * @param writer The writer
+ * @param seconds The (signed) number of seconds since 1970-01-01T00:00:00Z.
+ */
+MPACK_INLINE void mpack_write_timestamp_seconds(mpack_writer_t* writer, int64_t seconds) {
+ mpack_write_timestamp(writer, seconds, 0);
+}
+
+/**
+ * Writes a timestamp.
+ *
+ * @note This requires @ref MPACK_EXTENSIONS.
+ */
+MPACK_INLINE void mpack_write_timestamp_struct(mpack_writer_t* writer, mpack_timestamp_t timestamp) {
+ mpack_write_timestamp(writer, timestamp.seconds, timestamp.nanoseconds);
+}
+#endif
+
+/**
+ * @}
+ */
+
+/**
+ * @name Map and Array Functions
+ * @{
+ */
+
+/**
+ * Opens an array.
+ *
+ * `count` elements must follow, and mpack_finish_array() must be called
+ * when done.
+ *
+ * If you do not know the number of elements to be written ahead of time, call
+ * mpack_build_array() instead.
+ *
+ * @see mpack_finish_array()
+ * @see mpack_build_array() to count the number of elements automatically
+ */
+void mpack_start_array(mpack_writer_t* writer, uint32_t count);
+
+/**
+ * Opens a map.
+ *
+ * `count * 2` elements must follow, and mpack_finish_map() must be called
+ * when done.
+ *
+ * If you do not know the number of elements to be written ahead of time, call
+ * mpack_build_map() instead.
+ *
+ * Remember that while map elements in MessagePack are implicitly ordered,
+ * they are not ordered in JSON. If you need elements to be read back
+ * in the order they are written, consider use an array instead.
+ *
+ * @see mpack_finish_map()
+ * @see mpack_build_map() to count the number of key/value pairs automatically
+ */
+void mpack_start_map(mpack_writer_t* writer, uint32_t count);
+
+MPACK_INLINE void mpack_builder_compound_push(mpack_writer_t* writer) {
+ MPACK_UNUSED(writer);
+
+ #if MPACK_BUILDER
+ mpack_build_t* build = writer->builder.current_build;
+ if (build != NULL) {
+ ++build->nested_compound_elements;
+ }
+ #endif
+}
+
+MPACK_INLINE void mpack_builder_compound_pop(mpack_writer_t* writer) {
+ MPACK_UNUSED(writer);
+
+ #if MPACK_BUILDER
+ mpack_build_t* build = writer->builder.current_build;
+ if (build != NULL) {
+ mpack_assert(build->nested_compound_elements > 0);
+ --build->nested_compound_elements;
+ }
+ #endif
+}
+
+/**
+ * Finishes writing an array.
+ *
+ * This should be called only after a corresponding call to mpack_start_array()
+ * and after the array contents are written.
+ *
+ * In debug mode (or if MPACK_WRITE_TRACKING is not 0), this will track writes
+ * to ensure that the correct number of elements are written.
+ *
+ * @see mpack_start_array()
+ */
+MPACK_INLINE void mpack_finish_array(mpack_writer_t* writer) {
+ mpack_writer_track_pop(writer, mpack_type_array);
+ mpack_builder_compound_pop(writer);
+}
+
+/**
+ * Finishes writing a map.
+ *
+ * This should be called only after a corresponding call to mpack_start_map()
+ * and after the map contents are written.
+ *
+ * In debug mode (or if MPACK_WRITE_TRACKING is not 0), this will track writes
+ * to ensure that the correct number of elements are written.
+ *
+ * @see mpack_start_map()
+ */
+MPACK_INLINE void mpack_finish_map(mpack_writer_t* writer) {
+ mpack_writer_track_pop(writer, mpack_type_map);
+ mpack_builder_compound_pop(writer);
+}
+
+/**
+ * Starts building an array.
+ *
+ * Elements must follow, and mpack_complete_map() 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
+ * efficient to call mpack_start_array() instead, even if you are already
+ * within another open build.
+ *
+ * Builder containers can be nested within normal (known size) containers and
+ * vice versa. You can call mpack_build_array(), then mpack_start_array()
+ * inside it, then mpack_build_array() inside that, and so forth.
+ *
+ * @see mpack_complete_array() to complete this array
+ * @see mpack_start_array() if you already know the size of the array
+ * @see mpack_build_map() for implementation details
+ */
+void mpack_build_array(struct mpack_writer_t* writer);
+
+/**
+ * Starts building a map.
+ *
+ * An even number of elements must follow, and mpack_complete_map() must be
+ * called when done. The number of elements is determined automatically.
+ *
+ * If you know ahead of time the number of elements in the map, it is more
+ * efficient to call mpack_start_map() instead, even if you are already within
+ * another open build.
+ *
+ * Builder containers can be nested within normal (known size) containers and
+ * vice versa. You can call mpack_build_map(), then mpack_start_map() inside
+ * it, then mpack_build_map() inside that, and so forth.
+ *
+ * A writer in build mode diverts writes to a builder buffer that allocates as
+ * needed. Once the last map or array being built is completed, the deferred
+ * message is composed with computed array and map sizes into the writer.
+ * Builder maps and arrays are encoded exactly the same as ordinary maps and
+ * arrays in the final message.
+ *
+ * This indirect encoding is costly, as it incurs at least an extra copy of all
+ * data written within a builder (but not additional copies for nested
+ * builders.) Expect a speed penalty of half or more.
+ *
+ * A good strategy is to use this during early development when your messages
+ * are constantly changing, and then closer to release when your message
+ * formats have stabilized, replace all your build calls with start calls with
+ * pre-computed sizes. Or don't, if you find the builder has little impact on
+ * performance, because even with builders MPack is extremely fast.
+ *
+ * @note When an array or map starts being built, nothing will be flushed
+ * until it is completed. If you are building a large message that
+ * does not fit in the output stream, you won't get an error about it
+ * until everything is written.
+ *
+ * @see mpack_complete_map() to complete this map
+ * @see mpack_start_map() if you already know the size of the map
+ */
+void mpack_build_map(struct mpack_writer_t* writer);
+
+/**
+ * Completes an array being built.
+ *
+ * @see mpack_build_array()
+ */
+void mpack_complete_array(struct mpack_writer_t* writer);
+
+/**
+ * Completes a map being built.
+ *
+ * @see mpack_build_map()
+ */
+void mpack_complete_map(struct mpack_writer_t* writer);
+
+/**
+ * @}
+ */
+
+/**
+ * @name Data Helpers
+ * @{
+ */
+
+/**
+ * Writes a string.
+ *
+ * To stream a string in chunks, use mpack_start_str() instead.
+ *
+ * MPack does not care about the underlying encoding, but UTF-8 is highly
+ * recommended, especially for compatibility with JSON. You should consider
+ * calling mpack_write_utf8() instead, especially if you will be reading
+ * it back as UTF-8.
+ *
+ * You should not call mpack_finish_str() after calling this; this
+ * performs both start and finish.
+ */
+void mpack_write_str(mpack_writer_t* writer, const char* str, uint32_t length);
+
+/**
+ * Writes a string, ensuring that it is valid UTF-8.
+ *
+ * This does not accept any UTF-8 variant such as Modified UTF-8, CESU-8 or
+ * WTF-8. Only pure UTF-8 is allowed.
+ *
+ * You should not call mpack_finish_str() after calling this; this
+ * performs both start and finish.
+ *
+ * @throws mpack_error_invalid if the string is not valid UTF-8
+ */
+void mpack_write_utf8(mpack_writer_t* writer, const char* str, uint32_t length);
+
+/**
+ * Writes a null-terminated string. (The null-terminator is not written.)
+ *
+ * MPack does not care about the underlying encoding, but UTF-8 is highly
+ * recommended, especially for compatibility with JSON. You should consider
+ * calling mpack_write_utf8_cstr() instead, especially if you will be reading
+ * it back as UTF-8.
+ *
+ * You should not call mpack_finish_str() after calling this; this
+ * performs both start and finish.
+ */
+void mpack_write_cstr(mpack_writer_t* writer, const char* cstr);
+
+/**
+ * Writes a null-terminated string, or a nil node if the given cstr pointer
+ * is NULL. (The null-terminator is not written.)
+ *
+ * MPack does not care about the underlying encoding, but UTF-8 is highly
+ * recommended, especially for compatibility with JSON. You should consider
+ * calling mpack_write_utf8_cstr_or_nil() instead, especially if you will
+ * be reading it back as UTF-8.
+ *
+ * You should not call mpack_finish_str() after calling this; this
+ * performs both start and finish.
+ */
+void mpack_write_cstr_or_nil(mpack_writer_t* writer, const char* cstr);
+
+/**
+ * Writes a null-terminated string, ensuring that it is valid UTF-8. (The
+ * null-terminator is not written.)
+ *
+ * This does not accept any UTF-8 variant such as Modified UTF-8, CESU-8 or
+ * WTF-8. Only pure UTF-8 is allowed.
+ *
+ * You should not call mpack_finish_str() after calling this; this
+ * performs both start and finish.
+ *
+ * @throws mpack_error_invalid if the string is not valid UTF-8
+ */
+void mpack_write_utf8_cstr(mpack_writer_t* writer, const char* cstr);
+
+/**
+ * Writes a null-terminated string ensuring that it is valid UTF-8, or
+ * writes nil if the given cstr pointer is NULL. (The null-terminator
+ * is not written.)
+ *
+ * This does not accept any UTF-8 variant such as Modified UTF-8, CESU-8 or
+ * WTF-8. Only pure UTF-8 is allowed.
+ *
+ * You should not call mpack_finish_str() after calling this; this
+ * performs both start and finish.
+ *
+ * @throws mpack_error_invalid if the string is not valid UTF-8
+ */
+void mpack_write_utf8_cstr_or_nil(mpack_writer_t* writer, const char* cstr);
+
+/**
+ * Writes a binary blob.
+ *
+ * To stream a binary blob in chunks, use mpack_start_bin() instead.
+ *
+ * You should not call mpack_finish_bin() after calling this; this
+ * performs both start and finish.
+ */
+void mpack_write_bin(mpack_writer_t* writer, const char* data, uint32_t count);
+
+#if MPACK_EXTENSIONS
+/**
+ * Writes an extension type.
+ *
+ * To stream an extension blob in chunks, use mpack_start_ext() instead.
+ *
+ * Extension types [0, 127] are available for application-specific types. Extension
+ * types [-128, -1] are reserved for future extensions of MessagePack.
+ *
+ * You should not call mpack_finish_ext() after calling this; this
+ * performs both start and finish.
+ *
+ * @note This requires @ref MPACK_EXTENSIONS.
+ */
+void mpack_write_ext(mpack_writer_t* writer, int8_t exttype, const char* data, uint32_t count);
+#endif
+
+/**
+ * @}
+ */
+
+/**
+ * @name Chunked Data Functions
+ * @{
+ */
+
+/**
+ * Opens a string. `count` bytes should be written with calls to
+ * mpack_write_bytes(), and mpack_finish_str() should be called
+ * when done.
+ *
+ * To write an entire string at once, use mpack_write_str() or
+ * mpack_write_cstr() instead.
+ *
+ * MPack does not care about the underlying encoding, but UTF-8 is highly
+ * recommended, especially for compatibility with JSON.
+ */
+void mpack_start_str(mpack_writer_t* writer, uint32_t count);
+
+/**
+ * Opens a binary blob. `count` bytes should be written with calls to
+ * mpack_write_bytes(), and mpack_finish_bin() should be called
+ * when done.
+ */
+void mpack_start_bin(mpack_writer_t* writer, uint32_t count);
+
+#if MPACK_EXTENSIONS
+/**
+ * Opens an extension type. `count` bytes should be written with calls
+ * to mpack_write_bytes(), and mpack_finish_ext() should be called
+ * when done.
+ *
+ * Extension types [0, 127] are available for application-specific types. Extension
+ * types [-128, -1] are reserved for future extensions of MessagePack.
+ *
+ * @note This requires @ref MPACK_EXTENSIONS.
+ */
+void mpack_start_ext(mpack_writer_t* writer, int8_t exttype, uint32_t count);
+#endif
+
+/**
+ * Writes a portion of bytes for a string, binary blob or extension type which
+ * was opened by mpack_write_tag() or one of the mpack_start_*() functions.
+ *
+ * This can be called multiple times to write the data in chunks, as long as
+ * the total amount of bytes written matches the count given when the compound
+ * type was started.
+ *
+ * The corresponding mpack_finish_*() function must be called when done.
+ *
+ * To write an entire string, binary blob or extension type at
+ * once, use one of the mpack_write_*() functions instead.
+ *
+ * @see mpack_write_tag()
+ * @see mpack_start_str()
+ * @see mpack_start_bin()
+ * @see mpack_start_ext()
+ * @see mpack_finish_str()
+ * @see mpack_finish_bin()
+ * @see mpack_finish_ext()
+ * @see mpack_finish_type()
+ */
+void mpack_write_bytes(mpack_writer_t* writer, const char* data, size_t count);
+
+/**
+ * Finishes writing a string.
+ *
+ * This should be called only after a corresponding call to mpack_start_str()
+ * and after the string bytes are written with mpack_write_bytes().
+ *
+ * This will track writes to ensure that the correct number of elements are written.
+ *
+ * @see mpack_start_str()
+ * @see mpack_write_bytes()
+ */
+MPACK_INLINE void mpack_finish_str(mpack_writer_t* writer) {
+ mpack_writer_track_pop(writer, mpack_type_str);
+}
+
+/**
+ * Finishes writing a binary blob.
+ *
+ * This should be called only after a corresponding call to mpack_start_bin()
+ * and after the binary bytes are written with mpack_write_bytes().
+ *
+ * This will track writes to ensure that the correct number of bytes are written.
+ *
+ * @see mpack_start_bin()
+ * @see mpack_write_bytes()
+ */
+MPACK_INLINE void mpack_finish_bin(mpack_writer_t* writer) {
+ mpack_writer_track_pop(writer, mpack_type_bin);
+}
+
+#if MPACK_EXTENSIONS
+/**
+ * Finishes writing an extended type binary data blob.
+ *
+ * This should be called only after a corresponding call to mpack_start_bin()
+ * and after the binary bytes are written with mpack_write_bytes().
+ *
+ * This will track writes to ensure that the correct number of bytes are written.
+ *
+ * @note This requires @ref MPACK_EXTENSIONS.
+ *
+ * @see mpack_start_ext()
+ * @see mpack_write_bytes()
+ */
+MPACK_INLINE void mpack_finish_ext(mpack_writer_t* writer) {
+ mpack_writer_track_pop(writer, mpack_type_ext);
+}
+#endif
+
+/**
+ * Finishes writing the given compound type.
+ *
+ * This will track writes to ensure that the correct number of elements
+ * or bytes are written.
+ *
+ * This can be called with the appropriate type instead the corresponding
+ * mpack_finish_*() function if you want to finish a dynamic type.
+ */
+MPACK_INLINE void mpack_finish_type(mpack_writer_t* writer, mpack_type_t type) {
+ mpack_writer_track_pop(writer, type);
+}
+
+/**
+ * @}
+ */
+
+#if MPACK_HAS_GENERIC && !defined(__cplusplus)
+
+/**
+ * @name Type-Generic Writers
+ * @{
+ */
+
+/**
+ * @def mpack_write(writer, value)
+ *
+ * Type-generic writer for primitive types.
+ *
+ * The compiler will dispatch to an appropriate write function based
+ * on the type of the @a value parameter.
+ *
+ * @note This requires C11 `_Generic` support. (A set of inline overloads
+ * are used in C++ to provide the same functionality.)
+ *
+ * @warning In C11, the indentifiers `true`, `false` and `NULL` are
+ * all of type `int`, not `bool` or `void*`! They will emit unexpected
+ * types when passed uncast, so be careful when using them.
+ */
+#if MPACK_FLOAT
+ #define MPACK_WRITE_GENERIC_FLOAT float: mpack_write_float,
+#else
+ #define MPACK_WRITE_GENERIC_FLOAT /*nothing*/
+#endif
+#if MPACK_DOUBLE
+ #define MPACK_WRITE_GENERIC_DOUBLE double: mpack_write_double,
+#else
+ #define MPACK_WRITE_GENERIC_DOUBLE /*nothing*/
+#endif
+#define mpack_write(writer, value) \
+ _Generic(((void)0, value), \
+ int8_t: mpack_write_i8, \
+ int16_t: mpack_write_i16, \
+ int32_t: mpack_write_i32, \
+ int64_t: mpack_write_i64, \
+ uint8_t: mpack_write_u8, \
+ uint16_t: mpack_write_u16, \
+ uint32_t: mpack_write_u32, \
+ uint64_t: mpack_write_u64, \
+ bool: mpack_write_bool, \
+ MPACK_WRITE_GENERIC_FLOAT \
+ MPACK_WRITE_GENERIC_DOUBLE \
+ char *: mpack_write_cstr_or_nil, \
+ const char *: mpack_write_cstr_or_nil \
+ )(writer, value)
+
+/**
+ * @def mpack_write_kv(writer, key, value)
+ *
+ * Type-generic writer for key-value pairs of null-terminated string
+ * keys and primitive values.
+ *
+ * @warning @a writer may be evaluated multiple times.
+ *
+ * @warning In C11, the indentifiers `true`, `false` and `NULL` are
+ * all of type `int`, not `bool` or `void*`! They will emit unexpected
+ * types when passed uncast, so be careful when using them.
+ *
+ * @param writer The writer.
+ * @param key A null-terminated C string.
+ * @param value A primitive type supported by mpack_write().
+ */
+#define mpack_write_kv(writer, key, value) do { \
+ mpack_write_cstr(writer, key); \
+ mpack_write(writer, value); \
+} while (0)
+
+/**
+ * @}
+ */
+
+#endif // MPACK_HAS_GENERIC && !defined(__cplusplus)
+
+// The rest of this file contains C++ overloads, so we end extern "C" here.
+MPACK_EXTERN_C_END
+
+#if defined(__cplusplus) || defined(MPACK_DOXYGEN)
+
+namespace mpack {
+/**
+ * @name C++ write overloads
+ * @{
+ */
+
+/*
+ * C++ generic writers for primitive values
+ */
+
+#ifdef MPACK_DOXYGEN
+#undef mpack_write
+#undef mpack_write_kv
+#endif
+
+MPACK_INLINE void mpack_write(mpack_writer_t* writer, int8_t value) {
+ mpack_write_i8(writer, value);
+}
+
+MPACK_INLINE void mpack_write(mpack_writer_t* writer, int16_t value) {
+ mpack_write_i16(writer, value);
+}
+
+MPACK_INLINE void mpack_write(mpack_writer_t* writer, int32_t value) {
+ mpack_write_i32(writer, value);
+}
+
+MPACK_INLINE void mpack_write(mpack_writer_t* writer, int64_t value) {
+ mpack_write_i64(writer, value);
+}
+
+MPACK_INLINE void mpack_write(mpack_writer_t* writer, uint8_t value) {
+ mpack_write_u8(writer, value);
+}
+
+MPACK_INLINE void mpack_write(mpack_writer_t* writer, uint16_t value) {
+ mpack_write_u16(writer, value);
+}
+
+MPACK_INLINE void mpack_write(mpack_writer_t* writer, uint32_t value) {
+ mpack_write_u32(writer, value);
+}
+
+MPACK_INLINE void mpack_write(mpack_writer_t* writer, uint64_t value) {
+ mpack_write_u64(writer, value);
+}
+
+MPACK_INLINE void mpack_write(mpack_writer_t* writer, bool value) {
+ mpack_write_bool(writer, value);
+}
+
+MPACK_INLINE void mpack_write(mpack_writer_t* writer, float value) {
+ mpack_write_float(writer, value);
+}
+
+MPACK_INLINE void mpack_write(mpack_writer_t* writer, double value) {
+ mpack_write_double(writer, value);
+}
+
+MPACK_INLINE void mpack_write(mpack_writer_t* writer, char *value) {
+ mpack_write_cstr_or_nil(writer, value);
+}
+
+MPACK_INLINE void mpack_write(mpack_writer_t* writer, const char *value) {
+ mpack_write_cstr_or_nil(writer, value);
+}
+
+/* C++ generic write for key-value pairs */
+
+MPACK_INLINE void mpack_write_kv(mpack_writer_t* writer, const char *key, int8_t value) {
+ mpack_write_cstr(writer, key);
+ mpack_write_i8(writer, value);
+}
+
+MPACK_INLINE void mpack_write_kv(mpack_writer_t* writer, const char *key, int16_t value) {
+ mpack_write_cstr(writer, key);
+ mpack_write_i16(writer, value);
+}
+
+MPACK_INLINE void mpack_write_kv(mpack_writer_t* writer, const char *key, int32_t value) {
+ mpack_write_cstr(writer, key);
+ mpack_write_i32(writer, value);
+}
+
+MPACK_INLINE void mpack_write_kv(mpack_writer_t* writer, const char *key, int64_t value) {
+ mpack_write_cstr(writer, key);
+ mpack_write_i64(writer, value);
+}
+
+MPACK_INLINE void mpack_write_kv(mpack_writer_t* writer, const char *key, uint8_t value) {
+ mpack_write_cstr(writer, key);
+ mpack_write_u8(writer, value);
+}
+
+MPACK_INLINE void mpack_write_kv(mpack_writer_t* writer, const char *key, uint16_t value) {
+ mpack_write_cstr(writer, key);
+ mpack_write_u16(writer, value);
+}
+
+MPACK_INLINE void mpack_write_kv(mpack_writer_t* writer, const char *key, uint32_t value) {
+ mpack_write_cstr(writer, key);
+ mpack_write_u32(writer, value);
+}
+
+MPACK_INLINE void mpack_write_kv(mpack_writer_t* writer, const char *key, uint64_t value) {
+ mpack_write_cstr(writer, key);
+ mpack_write_u64(writer, value);
+}
+
+MPACK_INLINE void mpack_write_kv(mpack_writer_t* writer, const char *key, bool value) {
+ mpack_write_cstr(writer, key);
+ mpack_write_bool(writer, value);
+}
+
+MPACK_INLINE void mpack_write_kv(mpack_writer_t* writer, const char *key, float value) {
+ mpack_write_cstr(writer, key);
+ mpack_write_float(writer, value);
+}
+
+MPACK_INLINE void mpack_write_kv(mpack_writer_t* writer, const char *key, double value) {
+ mpack_write_cstr(writer, key);
+ mpack_write_double(writer, value);
+}
+
+MPACK_INLINE void mpack_write_kv(mpack_writer_t* writer, const char *key, char *value) {
+ mpack_write_cstr(writer, key);
+ mpack_write_cstr_or_nil(writer, value);
+}
+
+MPACK_INLINE void mpack_write_kv(mpack_writer_t* writer, const char *key, const char *value) {
+ mpack_write_cstr(writer, key);
+ mpack_write_cstr_or_nil(writer, value);
+}
+
+/**
+ * @}
+ */
+} // namespace mpack
+#endif /* __cplusplus */
+
+/**
+ * @}
+ */
+
+MPACK_SILENCE_WARNINGS_END
+
+#endif // MPACK_WRITER
+
+#endif
+
+/* mpack/mpack-reader.h.h */
+
+/**
+ * @file
+ *
+ * Declares the core MPack Tag Reader.
+ */
+
+#ifndef MPACK_READER_H
+#define MPACK_READER_H 1
+
+/* #include "mpack-common.h" */
+
+MPACK_SILENCE_WARNINGS_BEGIN
+MPACK_EXTERN_C_BEGIN
+
+#if MPACK_READER
+
+#if MPACK_READ_TRACKING
+struct mpack_track_t;
+#endif
+
+// The denominator to determine whether a read is a small
+// fraction of the buffer size.
+#define MPACK_READER_SMALL_FRACTION_DENOMINATOR 32
+
+/**
+ * @defgroup reader Reader API
+ *
+ * The MPack Reader API contains functions for imperatively reading dynamically
+ * typed data from a MessagePack stream.
+ *
+ * See @ref docs/reader.md for examples.
+ *
+ * @note If you are not writing code for an embedded device (or otherwise do
+ * not need maximum performance with minimal memory usage), you should not use
+ * this. You probably want to use the @link node Node API@endlink instead.
+ *
+ * This forms the basis of the @link expect Expect API@endlink, which can be
+ * used to interpret the stream of elements in expected types and value ranges.
+ *
+ * @{
+ */
+
+/**
+ * @def MPACK_READER_MINIMUM_BUFFER_SIZE
+ *
+ * The minimum buffer size for a reader with a fill function.
+ */
+#define MPACK_READER_MINIMUM_BUFFER_SIZE 32
+
+/**
+ * A buffered MessagePack decoder.
+ *
+ * The decoder wraps an existing buffer and, optionally, a fill function.
+ * This allows efficiently decoding data from existing memory buffers, files,
+ * streams, etc.
+ *
+ * All read operations are synchronous; they will block until the
+ * requested data is fully read, or an error occurs.
+ *
+ * This structure is opaque; its fields should not be accessed outside
+ * of MPack.
+ */
+typedef struct mpack_reader_t mpack_reader_t;
+
+/**
+ * The MPack reader's fill function. It should fill the buffer with at
+ * least one byte and at most the given @c count, returning the number
+ * of bytes written to the buffer.
+ *
+ * In case of error, it should flag an appropriate error on the reader
+ * (usually @ref mpack_error_io), or simply return zero. If zero is
+ * returned, mpack_error_io is raised.
+ *
+ * @note When reading from a stream, you should only copy and return
+ * the bytes that are immediately available. It is always safe to return
+ * less than the requested count as long as some non-zero number of bytes
+ * are read; if more bytes are needed, the read function will simply be
+ * called again.
+ *
+ * @see mpack_reader_context()
+ */
+typedef size_t (*mpack_reader_fill_t)(mpack_reader_t* reader, char* buffer, size_t count);
+
+/**
+ * The MPack reader's skip function. It should discard the given number
+ * of bytes from the source (for example by seeking forward.)
+ *
+ * In case of error, it should flag an appropriate error on the reader.
+ *
+ * @see mpack_reader_context()
+ */
+typedef void (*mpack_reader_skip_t)(mpack_reader_t* reader, size_t count);
+
+/**
+ * An error handler function to be called when an error is flagged on
+ * the reader.
+ *
+ * The error handler will only be called once on the first error flagged;
+ * any subsequent reads and errors are ignored, and the reader is
+ * permanently in that error state.
+ *
+ * MPack is safe against non-local jumps out of error handler callbacks.
+ * This means you are allowed to longjmp or throw an exception (in C++,
+ * Objective-C, or with SEH) out of this callback.
+ *
+ * Bear in mind when using longjmp that local non-volatile variables that
+ * have changed are undefined when setjmp() returns, so you can't put the
+ * reader on the stack in the same activation frame as the setjmp without
+ * declaring it volatile.
+ *
+ * You must still eventually destroy the reader. It is not destroyed
+ * automatically when an error is flagged. It is safe to destroy the
+ * reader within this error callback, but you will either need to perform
+ * a non-local jump, or store something in your context to identify
+ * that the reader is destroyed since any future accesses to it cause
+ * undefined behavior.
+ */
+typedef void (*mpack_reader_error_t)(mpack_reader_t* reader, mpack_error_t error);
+
+/**
+ * A teardown function to be called when the reader is destroyed.
+ */
+typedef void (*mpack_reader_teardown_t)(mpack_reader_t* reader);
+
+/* Hide internals from documentation */
+/** @cond */
+
+struct mpack_reader_t {
+ void* context; /* Context for reader callbacks */
+ mpack_reader_fill_t fill; /* Function to read bytes into the buffer */
+ mpack_reader_error_t error_fn; /* Function to call on error */
+ mpack_reader_teardown_t teardown; /* Function to teardown the context on destroy */
+ mpack_reader_skip_t skip; /* Function to skip bytes from the source */
+
+ char* buffer; /* Writeable byte buffer */
+ size_t size; /* Size of the buffer */
+
+ const char* data; /* Current data pointer (in the buffer, if it is used) */
+ const char* end; /* The end of available data (in the buffer, if it is used) */
+
+ mpack_error_t error; /* Error state */
+
+ #if MPACK_READ_TRACKING
+ mpack_track_t track; /* Stack of map/array/str/bin/ext reads */
+ #endif
+};
+
+/** @endcond */
+
+/**
+ * @name Lifecycle Functions
+ * @{
+ */
+
+/**
+ * Initializes an MPack reader with the given buffer. The reader does
+ * not assume ownership of the buffer, but the buffer must be writeable
+ * if a fill function will be used to refill it.
+ *
+ * @param reader The MPack reader.
+ * @param buffer The buffer with which to read MessagePack data.
+ * @param size The size of the buffer.
+ * @param count The number of bytes already in the buffer.
+ */
+void mpack_reader_init(mpack_reader_t* reader, char* buffer, size_t size, size_t count);
+
+/**
+ * Initializes an MPack reader directly into an error state. Use this if you
+ * are writing a wrapper to mpack_reader_init() which can fail its setup.
+ */
+void mpack_reader_init_error(mpack_reader_t* reader, mpack_error_t error);
+
+/**
+ * Initializes an MPack reader to parse a pre-loaded contiguous chunk of data. The
+ * reader does not assume ownership of the data.
+ *
+ * @param reader The MPack reader.
+ * @param data The data to parse.
+ * @param count The number of bytes pointed to by data.
+ */
+void mpack_reader_init_data(mpack_reader_t* reader, const char* data, size_t count);
+
+#if MPACK_STDIO
+/**
+ * Initializes an MPack reader that reads from a file.
+ *
+ * The file will be automatically opened and closed by the reader.
+ */
+void mpack_reader_init_filename(mpack_reader_t* reader, const char* filename);
+
+/**
+ * Deprecated.
+ *
+ * \deprecated Renamed to mpack_reader_init_filename().
+ */
+MPACK_INLINE void mpack_reader_init_file(mpack_reader_t* reader, const char* filename) {
+ mpack_reader_init_filename(reader, filename);
+}
+
+/**
+ * Initializes an MPack reader that reads from a libc FILE. This can be used to
+ * read from stdin, or from a file opened separately.
+ *
+ * @param reader The MPack reader.
+ * @param stdfile The FILE.
+ * @param close_when_done If true, fclose() will be called on the FILE when it
+ * is no longer needed. If false, the file will not be closed when
+ * reading is done.
+ *
+ * @warning The reader is buffered. It will read data in advance of parsing it,
+ * and it may read more data than it parsed. See mpack_reader_remaining() to
+ * access the extra data.
+ */
+void mpack_reader_init_stdfile(mpack_reader_t* reader, FILE* stdfile, bool close_when_done);
+#endif
+
+/**
+ * @def mpack_reader_init_stack(reader)
+ * @hideinitializer
+ *
+ * Initializes an MPack reader using stack space as a buffer. A fill function
+ * should be added to the reader to fill the buffer.
+ *
+ * @see mpack_reader_set_fill
+ */
+
+/** @cond */
+#define mpack_reader_init_stack_line_ex(line, reader) \
+ char mpack_buf_##line[MPACK_STACK_SIZE]; \
+ mpack_reader_init((reader), mpack_buf_##line, sizeof(mpack_buf_##line), 0)
+
+#define mpack_reader_init_stack_line(line, reader) \
+ mpack_reader_init_stack_line_ex(line, reader)
+/** @endcond */
+
+#define mpack_reader_init_stack(reader) \
+ mpack_reader_init_stack_line(__LINE__, (reader))
+
+/**
+ * Cleans up the MPack reader, ensuring that all compound elements
+ * have been completely read. Returns the final error state of the
+ * reader.
+ *
+ * This will assert in tracking mode if the reader is not in an error
+ * state and has any incomplete reads. If you want to cancel reading
+ * in the middle of a document, you need to flag an error on the reader
+ * before destroying it (such as mpack_error_data).
+ *
+ * @see mpack_read_tag()
+ * @see mpack_reader_flag_error()
+ * @see mpack_error_data
+ */
+mpack_error_t mpack_reader_destroy(mpack_reader_t* reader);
+
+/**
+ * @}
+ */
+
+/**
+ * @name Callbacks
+ * @{
+ */
+
+/**
+ * Sets the custom pointer to pass to the reader callbacks, such as fill
+ * or teardown.
+ *
+ * @param reader The MPack reader.
+ * @param context User data to pass to the reader callbacks.
+ *
+ * @see mpack_reader_context()
+ */
+MPACK_INLINE void mpack_reader_set_context(mpack_reader_t* reader, void* context) {
+ reader->context = context;
+}
+
+/**
+ * Returns the custom context for reader callbacks.
+ *
+ * @see mpack_reader_set_context
+ * @see mpack_reader_set_fill
+ * @see mpack_reader_set_skip
+ */
+MPACK_INLINE void* mpack_reader_context(mpack_reader_t* reader) {
+ return reader->context;
+}
+
+/**
+ * Sets the fill function to refill the data buffer when it runs out of data.
+ *
+ * If no fill function is used, truncated MessagePack data results in
+ * mpack_error_invalid (since the buffer is assumed to contain a
+ * complete MessagePack object.)
+ *
+ * If a fill function is used, truncated MessagePack data usually
+ * results in mpack_error_io (since the fill function fails to get
+ * the missing data.)
+ *
+ * This should normally be used with mpack_reader_set_context() to register
+ * a custom pointer to pass to the fill function.
+ *
+ * @param reader The MPack reader.
+ * @param fill The function to fetch additional data into the buffer.
+ */
+void mpack_reader_set_fill(mpack_reader_t* reader, mpack_reader_fill_t fill);
+
+/**
+ * Sets the skip function to discard bytes from the source stream.
+ *
+ * It's not necessary to implement this function. If the stream is not
+ * seekable, don't set a skip callback. The reader will fall back to
+ * using the fill function instead.
+ *
+ * This should normally be used with mpack_reader_set_context() to register
+ * a custom pointer to pass to the skip function.
+ *
+ * The skip function is ignored in size-optimized builds to reduce code
+ * size. Data will be skipped with the fill function when necessary.
+ *
+ * @param reader The MPack reader.
+ * @param skip The function to discard bytes from the source stream.
+ */
+void mpack_reader_set_skip(mpack_reader_t* reader, mpack_reader_skip_t skip);
+
+/**
+ * Sets the error function to call when an error is flagged on the reader.
+ *
+ * This should normally be used with mpack_reader_set_context() to register
+ * a custom pointer to pass to the error function.
+ *
+ * See the definition of mpack_reader_error_t for more information about
+ * what you can do from an error callback.
+ *
+ * @see mpack_reader_error_t
+ * @param reader The MPack reader.
+ * @param error_fn The function to call when an error is flagged on the reader.
+ */
+MPACK_INLINE void mpack_reader_set_error_handler(mpack_reader_t* reader, mpack_reader_error_t error_fn) {
+ reader->error_fn = error_fn;
+}
+
+/**
+ * Sets the teardown function to call when the reader is destroyed.
+ *
+ * This should normally be used with mpack_reader_set_context() to register
+ * a custom pointer to pass to the teardown function.
+ *
+ * @param reader The MPack reader.
+ * @param teardown The function to call when the reader is destroyed.
+ */
+MPACK_INLINE void mpack_reader_set_teardown(mpack_reader_t* reader, mpack_reader_teardown_t teardown) {
+ reader->teardown = teardown;
+}
+
+/**
+ * @}
+ */
+
+/**
+ * @name Core Reader Functions
+ * @{
+ */
+
+/**
+ * Queries the error state of the MPack reader.
+ *
+ * If a reader is in an error state, you should discard all data since the
+ * last time the error flag was checked. The error flag cannot be cleared.
+ */
+MPACK_INLINE mpack_error_t mpack_reader_error(mpack_reader_t* reader) {
+ return reader->error;
+}
+
+/**
+ * Places the reader in the given error state, calling the error callback if one
+ * is set.
+ *
+ * This allows you to externally flag errors, for example if you are validating
+ * data as you read it.
+ *
+ * If the reader is already in an error state, this call is ignored and no
+ * error callback is called.
+ */
+void mpack_reader_flag_error(mpack_reader_t* reader, mpack_error_t error);
+
+/**
+ * Places the reader in the given error state if the given error is not mpack_ok,
+ * returning the resulting error state of the reader.
+ *
+ * This allows you to externally flag errors, for example if you are validating
+ * data as you read it.
+ *
+ * If the given error is mpack_ok or if the reader is already in an error state,
+ * this call is ignored and the actual error state of the reader is returned.
+ */
+MPACK_INLINE mpack_error_t mpack_reader_flag_if_error(mpack_reader_t* reader, mpack_error_t error) {
+ if (error != mpack_ok)
+ mpack_reader_flag_error(reader, error);
+ return mpack_reader_error(reader);
+}
+
+/**
+ * Returns bytes left in the reader's buffer.
+ *
+ * If you are done reading MessagePack data but there is other interesting data
+ * following it, the reader may have buffered too much data. The number of bytes
+ * remaining in the buffer and a pointer to the position of those bytes can be
+ * queried here.
+ *
+ * If you know the length of the MPack chunk beforehand, it's better to instead
+ * have your fill function limit the data it reads so that the reader does not
+ * have extra data. In this case you can simply check that this returns zero.
+ *
+ * Returns 0 if the reader is in an error state.
+ *
+ * @param reader The MPack reader from which to query remaining data.
+ * @param data [out] A pointer to the remaining data, or NULL.
+ * @return The number of bytes remaining in the buffer.
+ */
+size_t mpack_reader_remaining(mpack_reader_t* reader, const char** data);
+
+/**
+ * Reads a MessagePack object header (an MPack tag.)
+ *
+ * If an error occurs, the reader is placed in an error state and a
+ * nil tag is returned. If the reader is already in an error state,
+ * a nil tag is returned.
+ *
+ * If the type is compound (i.e. is a map, array, string, binary or
+ * extension type), additional reads are required to get the contained
+ * data, and the corresponding done function must be called when done.
+ *
+ * @note Maps in JSON are unordered, so it is recommended not to expect
+ * a specific ordering for your map values in case your data is converted
+ * to/from JSON.
+ *
+ * @see mpack_read_bytes()
+ * @see mpack_done_array()
+ * @see mpack_done_map()
+ * @see mpack_done_str()
+ * @see mpack_done_bin()
+ * @see mpack_done_ext()
+ */
+mpack_tag_t mpack_read_tag(mpack_reader_t* reader);
+
+/**
+ * Parses the next MessagePack object header (an MPack tag) without
+ * advancing the reader.
+ *
+ * If an error occurs, the reader is placed in an error state and a
+ * nil tag is returned. If the reader is already in an error state,
+ * a nil tag is returned.
+ *
+ * @note Maps in JSON are unordered, so it is recommended not to expect
+ * a specific ordering for your map values in case your data is converted
+ * to/from JSON.
+ *
+ * @see mpack_read_tag()
+ * @see mpack_discard()
+ */
+mpack_tag_t mpack_peek_tag(mpack_reader_t* reader);
+
+/**
+ * @}
+ */
+
+/**
+ * @name String and Data Functions
+ * @{
+ */
+
+/**
+ * Skips bytes from the underlying stream. This is used only to
+ * skip the contents of a string, binary blob or extension object.
+ */
+void mpack_skip_bytes(mpack_reader_t* reader, size_t count);
+
+/**
+ * Reads bytes from a string, binary blob or extension object, copying
+ * them into the given buffer.
+ *
+ * A str, bin or ext must have been opened by a call to mpack_read_tag()
+ * which yielded one of these types, or by a call to an expect function
+ * such as mpack_expect_str() or mpack_expect_bin().
+ *
+ * If an error occurs, the buffer contents are undefined.
+ *
+ * This can be called multiple times for a single str, bin or ext
+ * to read the data in chunks. The total data read must add up
+ * to the size of the object.
+ *
+ * @param reader The MPack reader
+ * @param p The buffer in which to copy the bytes
+ * @param count The number of bytes to read
+ */
+void mpack_read_bytes(mpack_reader_t* reader, char* p, size_t count);
+
+/**
+ * Reads bytes from a string, ensures that the string is valid UTF-8,
+ * and copies the bytes into the given buffer.
+ *
+ * A string must have been opened by a call to mpack_read_tag() which
+ * yielded a string, or by a call to an expect function such as
+ * mpack_expect_str().
+ *
+ * The given byte count must match the complete size of the string as
+ * returned by the tag or expect function. You must ensure that the
+ * buffer fits the data.
+ *
+ * This does not accept any UTF-8 variant such as Modified UTF-8, CESU-8 or
+ * WTF-8. Only pure UTF-8 is allowed.
+ *
+ * If an error occurs, the buffer contents are undefined.
+ *
+ * Unlike mpack_read_bytes(), this cannot be used to read the data in
+ * chunks (since this might split a character's UTF-8 bytes, and the
+ * reader does not keep track of the UTF-8 decoding state between reads.)
+ *
+ * @throws mpack_error_type if the string contains invalid UTF-8.
+ */
+void mpack_read_utf8(mpack_reader_t* reader, char* p, size_t byte_count);
+
+/**
+ * Reads bytes from a string, ensures that the string contains no NUL
+ * bytes, copies the bytes into the given buffer and adds a null-terminator.
+ *
+ * A string must have been opened by a call to mpack_read_tag() which
+ * yielded a string, or by a call to an expect function such as
+ * mpack_expect_str().
+ *
+ * The given byte count must match the size of the string as returned
+ * by the tag or expect function. The string will only be copied if
+ * the buffer is large enough to store it.
+ *
+ * If an error occurs, the buffer will contain an empty string.
+ *
+ * @note If you know the object will be a string before reading it,
+ * it is highly recommended to use mpack_expect_cstr() instead.
+ * Alternatively you could use mpack_peek_tag() and call
+ * mpack_expect_cstr() if it's a string.
+ *
+ * @throws mpack_error_too_big if the string plus null-terminator is larger than the given buffer size
+ * @throws mpack_error_type if the string contains a null byte.
+ *
+ * @see mpack_peek_tag()
+ * @see mpack_expect_cstr()
+ * @see mpack_expect_utf8_cstr()
+ */
+void mpack_read_cstr(mpack_reader_t* reader, char* buf, size_t buffer_size, size_t byte_count);
+
+/**
+ * Reads bytes from a string, ensures that the string is valid UTF-8
+ * with no NUL bytes, copies the bytes into the given buffer and adds a
+ * null-terminator.
+ *
+ * A string must have been opened by a call to mpack_read_tag() which
+ * yielded a string, or by a call to an expect function such as
+ * mpack_expect_str().
+ *
+ * The given byte count must match the size of the string as returned
+ * by the tag or expect function. The string will only be copied if
+ * the buffer is large enough to store it.
+ *
+ * This does not accept any UTF-8 variant such as Modified UTF-8, CESU-8 or
+ * WTF-8. Only pure UTF-8 is allowed, but without the NUL character, since
+ * it cannot be represented in a null-terminated string.
+ *
+ * If an error occurs, the buffer will contain an empty string.
+ *
+ * @note If you know the object will be a string before reading it,
+ * it is highly recommended to use mpack_expect_utf8_cstr() instead.
+ * Alternatively you could use mpack_peek_tag() and call
+ * mpack_expect_utf8_cstr() if it's a string.
+ *
+ * @throws mpack_error_too_big if the string plus null-terminator is larger than the given buffer size
+ * @throws mpack_error_type if the string contains invalid UTF-8 or a null byte.
+ *
+ * @see mpack_peek_tag()
+ * @see mpack_expect_utf8_cstr()
+ */
+void mpack_read_utf8_cstr(mpack_reader_t* reader, char* buf, size_t buffer_size, size_t byte_count);
+
+#ifdef MPACK_MALLOC
+/** @cond */
+// This can optionally add a null-terminator, but it does not check
+// whether the data contains null bytes. This must be done separately
+// in a cstring read function (possibly as part of a UTF-8 check.)
+char* mpack_read_bytes_alloc_impl(mpack_reader_t* reader, size_t count, bool null_terminated);
+/** @endcond */
+
+/**
+ * Reads bytes from a string, binary blob or extension object, allocating
+ * storage for them and returning the allocated pointer.
+ *
+ * The allocated string must be freed with MPACK_FREE() (or simply free()
+ * if MPack's allocator hasn't been customized.)
+ *
+ * Returns NULL if any error occurs, or if count is zero.
+ */
+MPACK_INLINE char* mpack_read_bytes_alloc(mpack_reader_t* reader, size_t count) {
+ return mpack_read_bytes_alloc_impl(reader, count, false);
+}
+#endif
+
+/**
+ * Reads bytes from a string, binary blob or extension object in-place in
+ * the buffer. This can be used to avoid copying the data.
+ *
+ * A str, bin or ext must have been opened by a call to mpack_read_tag()
+ * which yielded one of these types, or by a call to an expect function
+ * such as mpack_expect_str() or mpack_expect_bin().
+ *
+ * If the bytes are from a string, the string is not null-terminated! Use
+ * mpack_read_cstr() to copy the string into a buffer and add a null-terminator.
+ *
+ * The returned pointer is invalidated on the next read, or when the buffer
+ * is destroyed.
+ *
+ * The reader will move data around in the buffer if needed to ensure that
+ * the pointer can always be returned, so this should only be used if
+ * count is very small compared to the buffer size. If you need to check
+ * whether a small size is reasonable (for example you intend to handle small and
+ * large sizes differently), you can call mpack_should_read_bytes_inplace().
+ *
+ * This can be called multiple times for a single str, bin or ext
+ * to read the data in chunks. The total data read must add up
+ * to the size of the object.
+ *
+ * NULL is returned if the reader is in an error state.
+ *
+ * @throws mpack_error_too_big if the requested size is larger than the buffer size
+ *
+ * @see mpack_should_read_bytes_inplace()
+ */
+const char* mpack_read_bytes_inplace(mpack_reader_t* reader, size_t count);
+
+/**
+ * Reads bytes from a string in-place in the buffer and ensures they are
+ * valid UTF-8. This can be used to avoid copying the data.
+ *
+ * A string must have been opened by a call to mpack_read_tag() which
+ * yielded a string, or by a call to an expect function such as
+ * mpack_expect_str().
+ *
+ * The string is not null-terminated! Use mpack_read_utf8_cstr() to
+ * copy the string into a buffer and add a null-terminator.
+ *
+ * The returned pointer is invalidated on the next read, or when the buffer
+ * is destroyed.
+ *
+ * The reader will move data around in the buffer if needed to ensure that
+ * the pointer can always be returned, so this should only be used if
+ * count is very small compared to the buffer size. If you need to check
+ * whether a small size is reasonable (for example you intend to handle small and
+ * large sizes differently), you can call mpack_should_read_bytes_inplace().
+ *
+ * This does not accept any UTF-8 variant such as Modified UTF-8, CESU-8 or
+ * WTF-8. Only pure UTF-8 is allowed.
+ *
+ * Unlike mpack_read_bytes_inplace(), this cannot be used to read the data in
+ * chunks (since this might split a character's UTF-8 bytes, and the
+ * reader does not keep track of the UTF-8 decoding state between reads.)
+ *
+ * NULL is returned if the reader is in an error state.
+ *
+ * @throws mpack_error_type if the string contains invalid UTF-8
+ * @throws mpack_error_too_big if the requested size is larger than the buffer size
+ *
+ * @see mpack_should_read_bytes_inplace()
+ */
+const char* mpack_read_utf8_inplace(mpack_reader_t* reader, size_t count);
+
+/**
+ * Returns true if it's a good idea to read the given number of bytes
+ * in-place.
+ *
+ * If the read will be larger than some small fraction of the buffer size,
+ * this will return false to avoid shuffling too much data back and forth
+ * in the buffer.
+ *
+ * Use this if you're expecting arbitrary size data, and you want to read
+ * in-place for the best performance when possible but will fall back to
+ * a normal read if the data is too large.
+ *
+ * @see mpack_read_bytes_inplace()
+ */
+MPACK_INLINE bool mpack_should_read_bytes_inplace(mpack_reader_t* reader, size_t count) {
+ return (reader->size == 0 || count <= reader->size / MPACK_READER_SMALL_FRACTION_DENOMINATOR);
+}
+
+#if MPACK_EXTENSIONS
+/**
+ * Reads a timestamp contained in an ext object of the given size, closing the
+ * ext type.
+ *
+ * An ext object of exttype @ref MPACK_EXTTYPE_TIMESTAMP must have been opened
+ * by a call to e.g. mpack_read_tag() or mpack_expect_ext().
+ *
+ * You must NOT call mpack_done_ext() after calling this. A timestamp ext
+ * object can only contain a single timestamp value, so this calls
+ * mpack_done_ext() automatically.
+ *
+ * @note This requires @ref MPACK_EXTENSIONS.
+ *
+ * @throws mpack_error_invalid if the size is not one of the supported
+ * timestamp sizes, or if the nanoseconds are out of range.
+ */
+mpack_timestamp_t mpack_read_timestamp(mpack_reader_t* reader, size_t size);
+#endif
+
+/**
+ * @}
+ */
+
+/**
+ * @name Core Reader Functions
+ * @{
+ */
+
+#if MPACK_READ_TRACKING
+/**
+ * Finishes reading the given type.
+ *
+ * This will track reads to ensure that the correct number of elements
+ * or bytes are read.
+ */
+void mpack_done_type(mpack_reader_t* reader, mpack_type_t type);
+#else
+MPACK_INLINE void mpack_done_type(mpack_reader_t* reader, mpack_type_t type) {
+ MPACK_UNUSED(reader);
+ MPACK_UNUSED(type);
+}
+#endif
+
+/**
+ * Finishes reading an array.
+ *
+ * This will track reads to ensure that the correct number of elements are read.
+ */
+MPACK_INLINE void mpack_done_array(mpack_reader_t* reader) {
+ mpack_done_type(reader, mpack_type_array);
+}
+
+/**
+ * @fn mpack_done_map(mpack_reader_t* reader)
+ *
+ * Finishes reading a map.
+ *
+ * This will track reads to ensure that the correct number of elements are read.
+ */
+MPACK_INLINE void mpack_done_map(mpack_reader_t* reader) {
+ mpack_done_type(reader, mpack_type_map);
+}
+
+/**
+ * @fn mpack_done_str(mpack_reader_t* reader)
+ *
+ * Finishes reading a string.
+ *
+ * This will track reads to ensure that the correct number of bytes are read.
+ */
+MPACK_INLINE void mpack_done_str(mpack_reader_t* reader) {
+ mpack_done_type(reader, mpack_type_str);
+}
+
+/**
+ * @fn mpack_done_bin(mpack_reader_t* reader)
+ *
+ * Finishes reading a binary data blob.
+ *
+ * This will track reads to ensure that the correct number of bytes are read.
+ */
+MPACK_INLINE void mpack_done_bin(mpack_reader_t* reader) {
+ mpack_done_type(reader, mpack_type_bin);
+}
+
+#if MPACK_EXTENSIONS
+/**
+ * @fn mpack_done_ext(mpack_reader_t* reader)
+ *
+ * Finishes reading an extended type binary data blob.
+ *
+ * This will track reads to ensure that the correct number of bytes are read.
+ *
+ * @note This requires @ref MPACK_EXTENSIONS.
+ */
+MPACK_INLINE void mpack_done_ext(mpack_reader_t* reader) {
+ mpack_done_type(reader, mpack_type_ext);
+}
+#endif
+
+/**
+ * Reads and discards the next object. This will read and discard all
+ * contained data as well if it is a compound type.
+ */
+void mpack_discard(mpack_reader_t* reader);
+
+/**
+ * @}
+ */
+
+/** @cond */
+
+#if MPACK_DEBUG && MPACK_STDIO
+/**
+ * @name Debugging Functions
+ * @{
+ */
+/*
+ * Converts a blob of MessagePack to a pseudo-JSON string for debugging
+ * purposes, placing the result in the given buffer with a null-terminator.
+ *
+ * If the buffer does not have enough space, the result will be truncated (but
+ * it is guaranteed to be null-terminated.)
+ *
+ * This is only available in debug mode, and only if stdio is available (since
+ * it uses snprintf().) It's strictly for debugging purposes.
+ */
+void mpack_print_data_to_buffer(const char* data, size_t data_size, char* buffer, size_t buffer_size);
+
+/*
+ * Converts a node to pseudo-JSON for debugging purposes, calling the given
+ * callback as many times as is necessary to output the character data.
+ *
+ * No null-terminator or trailing newline will be written.
+ *
+ * This is only available in debug mode, and only if stdio is available (since
+ * it uses snprintf().) It's strictly for debugging purposes.
+ */
+void mpack_print_data_to_callback(const char* data, size_t size, mpack_print_callback_t callback, void* context);
+
+/*
+ * Converts a blob of MessagePack to pseudo-JSON for debugging purposes
+ * and pretty-prints it to the given file.
+ */
+void mpack_print_data_to_file(const char* data, size_t len, FILE* file);
+
+/*
+ * Converts a blob of MessagePack to pseudo-JSON for debugging purposes
+ * and pretty-prints it to stdout.
+ */
+MPACK_INLINE void mpack_print_data_to_stdout(const char* data, size_t len) {
+ mpack_print_data_to_file(data, len, stdout);
+}
+
+/*
+ * Converts the MessagePack contained in the given `FILE*` to pseudo-JSON for
+ * debugging purposes, calling the given callback as many times as is necessary
+ * to output the character data.
+ */
+void mpack_print_stdfile_to_callback(FILE* file, mpack_print_callback_t callback, void* context);
+
+/*
+ * Deprecated.
+ *
+ * \deprecated Renamed to mpack_print_data_to_stdout().
+ */
+MPACK_INLINE void mpack_print(const char* data, size_t len) {
+ mpack_print_data_to_stdout(data, len);
+}
+
+/**
+ * @}
+ */
+#endif
+
+/** @endcond */
+
+/**
+ * @}
+ */
+
+
+
+#if MPACK_INTERNAL
+
+bool mpack_reader_ensure_straddle(mpack_reader_t* reader, size_t count);
+
+/*
+ * Ensures there are at least @c count bytes left in the
+ * data, raising an error and returning false if more
+ * data cannot be made available.
+ */
+MPACK_INLINE bool mpack_reader_ensure(mpack_reader_t* reader, size_t count) {
+ mpack_assert(count != 0, "cannot ensure zero bytes!");
+ mpack_assert(reader->error == mpack_ok, "reader cannot be in an error state!");
+
+ if (count <= (size_t)(reader->end - reader->data))
+ return true;
+ return mpack_reader_ensure_straddle(reader, count);
+}
+
+void mpack_read_native_straddle(mpack_reader_t* reader, char* p, size_t count);
+
+// Reads count bytes into p, deferring to mpack_read_native_straddle() if more
+// bytes are needed than are available in the buffer.
+MPACK_INLINE void mpack_read_native(mpack_reader_t* reader, char* p, size_t count) {
+ mpack_assert(count == 0 || p != NULL, "data pointer for %i bytes is NULL", (int)count);
+
+ if (count > (size_t)(reader->end - reader->data)) {
+ mpack_read_native_straddle(reader, p, count);
+ } else {
+ mpack_memcpy(p, reader->data, count);
+ reader->data += count;
+ }
+}
+
+#if MPACK_READ_TRACKING
+#define MPACK_READER_TRACK(reader, error_expr) \
+ (((reader)->error == mpack_ok) ? mpack_reader_flag_if_error((reader), (error_expr)) : (reader)->error)
+#else
+#define MPACK_READER_TRACK(reader, error_expr) (MPACK_UNUSED(reader), mpack_ok)
+#endif
+
+MPACK_INLINE mpack_error_t mpack_reader_track_element(mpack_reader_t* reader) {
+ return MPACK_READER_TRACK(reader, mpack_track_element(&reader->track, true));
+}
+
+MPACK_INLINE mpack_error_t mpack_reader_track_peek_element(mpack_reader_t* reader) {
+ return MPACK_READER_TRACK(reader, mpack_track_peek_element(&reader->track, true));
+}
+
+MPACK_INLINE mpack_error_t mpack_reader_track_bytes(mpack_reader_t* reader, size_t count) {
+ MPACK_UNUSED(count);
+ return MPACK_READER_TRACK(reader, mpack_track_bytes(&reader->track, true, count));
+}
+
+MPACK_INLINE mpack_error_t mpack_reader_track_str_bytes_all(mpack_reader_t* reader, size_t count) {
+ MPACK_UNUSED(count);
+ return MPACK_READER_TRACK(reader, mpack_track_str_bytes_all(&reader->track, true, count));
+}
+
+#endif
+
+
+
+#endif
+
+MPACK_EXTERN_C_END
+MPACK_SILENCE_WARNINGS_END
+
+#endif
+
+
+/* mpack/mpack-expect.h.h */
+
+/**
+ * @file
+ *
+ * Declares the MPack static Expect API.
+ */
+
+#ifndef MPACK_EXPECT_H
+#define MPACK_EXPECT_H 1
+
+/* #include "mpack-reader.h" */
+
+MPACK_SILENCE_WARNINGS_BEGIN
+MPACK_EXTERN_C_BEGIN
+
+#if MPACK_EXPECT
+
+#if !MPACK_READER
+#error "MPACK_EXPECT requires MPACK_READER."
+#endif
+
+/**
+ * @defgroup expect Expect API
+ *
+ * The MPack Expect API allows you to easily read MessagePack data when you
+ * expect it to follow a predefined schema.
+ *
+ * @note If you are not writing code for an embedded device (or otherwise do
+ * not need maximum performance with minimal memory usage), you should not use
+ * this. You probably want to use the @link node Node API@endlink instead.
+ *
+ * See @ref docs/expect.md for examples.
+ *
+ * The main purpose of the Expect API is convenience, so the API is lax. It
+ * automatically converts between similar types where there is no loss of
+ * precision.
+ *
+ * When using any of the expect functions, if the type or value of what was
+ * read does not match what is expected, @ref mpack_error_type is raised.
+ *
+ * @{
+ */
+
+/**
+ * @name Basic Number Functions
+ * @{
+ */
+
+/**
+ * Reads an 8-bit unsigned integer.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in an 8-bit unsigned int.
+ *
+ * Returns zero if an error occurs.
+ */
+uint8_t mpack_expect_u8(mpack_reader_t* reader);
+
+/**
+ * Reads a 16-bit unsigned integer.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in a 16-bit unsigned int.
+ *
+ * Returns zero if an error occurs.
+ */
+uint16_t mpack_expect_u16(mpack_reader_t* reader);
+
+/**
+ * Reads a 32-bit unsigned integer.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in a 32-bit unsigned int.
+ *
+ * Returns zero if an error occurs.
+ */
+uint32_t mpack_expect_u32(mpack_reader_t* reader);
+
+/**
+ * Reads a 64-bit unsigned integer.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in a 64-bit unsigned int.
+ *
+ * Returns zero if an error occurs.
+ */
+uint64_t mpack_expect_u64(mpack_reader_t* reader);
+
+/**
+ * Reads an 8-bit signed integer.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in an 8-bit signed int.
+ *
+ * Returns zero if an error occurs.
+ */
+int8_t mpack_expect_i8(mpack_reader_t* reader);
+
+/**
+ * Reads a 16-bit signed integer.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in a 16-bit signed int.
+ *
+ * Returns zero if an error occurs.
+ */
+int16_t mpack_expect_i16(mpack_reader_t* reader);
+
+/**
+ * Reads a 32-bit signed integer.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in a 32-bit signed int.
+ *
+ * Returns zero if an error occurs.
+ */
+int32_t mpack_expect_i32(mpack_reader_t* reader);
+
+/**
+ * Reads a 64-bit signed integer.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in a 64-bit signed int.
+ *
+ * Returns zero if an error occurs.
+ */
+int64_t mpack_expect_i64(mpack_reader_t* reader);
+
+#if MPACK_FLOAT
+/**
+ * Reads a number, returning the value as a float. The underlying value can be an
+ * integer, float or double; the value is converted to a float.
+ *
+ * @note Reading a double or a large integer with this function can incur a
+ * loss of precision.
+ *
+ * @throws mpack_error_type if the underlying value is not a float, double or integer.
+ */
+float mpack_expect_float(mpack_reader_t* reader);
+#endif
+
+#if MPACK_DOUBLE
+/**
+ * Reads a number, returning the value as a double. The underlying value can be an
+ * integer, float or double; the value is converted to a double.
+ *
+ * @note Reading a very large integer with this function can incur a
+ * loss of precision.
+ *
+ * @throws mpack_error_type if the underlying value is not a float, double or integer.
+ */
+double mpack_expect_double(mpack_reader_t* reader);
+#endif
+
+#if MPACK_FLOAT
+/**
+ * Reads a float. The underlying value must be a float, not a double or an integer.
+ * This ensures no loss of precision can occur.
+ *
+ * @throws mpack_error_type if the underlying value is not a float.
+ */
+float mpack_expect_float_strict(mpack_reader_t* reader);
+#endif
+
+#if MPACK_DOUBLE
+/**
+ * Reads a double. The underlying value must be a float or double, not an integer.
+ * This ensures no loss of precision can occur.
+ *
+ * @throws mpack_error_type if the underlying value is not a float or double.
+ */
+double mpack_expect_double_strict(mpack_reader_t* reader);
+#endif
+
+#if !MPACK_FLOAT
+/**
+ * Reads a float as a raw uint32_t. The underlying value must be a float, not a
+ * double or an integer.
+ *
+ * @throws mpack_error_type if the underlying value is not a float.
+ */
+uint32_t mpack_expect_raw_float(mpack_reader_t* reader);
+#endif
+
+#if !MPACK_DOUBLE
+/**
+ * Reads a double as a raw uint64_t. The underlying value must be a double, not a
+ * float or an integer.
+ *
+ * @throws mpack_error_type if the underlying value is not a double.
+ */
+uint64_t mpack_expect_raw_double(mpack_reader_t* reader);
+#endif
+
+/**
+ * @}
+ */
+
+/**
+ * @name Ranged Number Functions
+ * @{
+ */
+
+/**
+ * Reads an 8-bit unsigned integer, ensuring that it falls within the given range.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in an 8-bit unsigned int.
+ *
+ * Returns min_value if an error occurs.
+ */
+uint8_t mpack_expect_u8_range(mpack_reader_t* reader, uint8_t min_value, uint8_t max_value);
+
+/**
+ * Reads a 16-bit unsigned integer, ensuring that it falls within the given range.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in a 16-bit unsigned int.
+ *
+ * Returns min_value if an error occurs.
+ */
+uint16_t mpack_expect_u16_range(mpack_reader_t* reader, uint16_t min_value, uint16_t max_value);
+
+/**
+ * Reads a 32-bit unsigned integer, ensuring that it falls within the given range.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in a 32-bit unsigned int.
+ *
+ * Returns min_value if an error occurs.
+ */
+uint32_t mpack_expect_u32_range(mpack_reader_t* reader, uint32_t min_value, uint32_t max_value);
+
+/**
+ * Reads a 64-bit unsigned integer, ensuring that it falls within the given range.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in a 64-bit unsigned int.
+ *
+ * Returns min_value if an error occurs.
+ */
+uint64_t mpack_expect_u64_range(mpack_reader_t* reader, uint64_t min_value, uint64_t max_value);
+
+/**
+ * Reads an unsigned integer, ensuring that it falls within the given range.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in an unsigned int.
+ *
+ * Returns min_value if an error occurs.
+ */
+MPACK_INLINE unsigned int mpack_expect_uint_range(mpack_reader_t* reader, unsigned int min_value, unsigned int max_value) {
+ // This should be true at compile-time, so this just wraps the 32-bit
+ // function. We fallback to 64-bit if for some reason sizeof(int) isn't 4.
+ if (sizeof(unsigned int) == 4)
+ return (unsigned int)mpack_expect_u32_range(reader, (uint32_t)min_value, (uint32_t)max_value);
+ return (unsigned int)mpack_expect_u64_range(reader, min_value, max_value);
+}
+
+/**
+ * Reads an 8-bit unsigned integer, ensuring that it is at most @a max_value.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in an 8-bit unsigned int.
+ *
+ * Returns 0 if an error occurs.
+ */
+MPACK_INLINE uint8_t mpack_expect_u8_max(mpack_reader_t* reader, uint8_t max_value) {
+ return mpack_expect_u8_range(reader, 0, max_value);
+}
+
+/**
+ * Reads a 16-bit unsigned integer, ensuring that it is at most @a max_value.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in a 16-bit unsigned int.
+ *
+ * Returns 0 if an error occurs.
+ */
+MPACK_INLINE uint16_t mpack_expect_u16_max(mpack_reader_t* reader, uint16_t max_value) {
+ return mpack_expect_u16_range(reader, 0, max_value);
+}
+
+/**
+ * Reads a 32-bit unsigned integer, ensuring that it is at most @a max_value.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in a 32-bit unsigned int.
+ *
+ * Returns 0 if an error occurs.
+ */
+MPACK_INLINE uint32_t mpack_expect_u32_max(mpack_reader_t* reader, uint32_t max_value) {
+ return mpack_expect_u32_range(reader, 0, max_value);
+}
+
+/**
+ * Reads a 64-bit unsigned integer, ensuring that it is at most @a max_value.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in a 64-bit unsigned int.
+ *
+ * Returns 0 if an error occurs.
+ */
+MPACK_INLINE uint64_t mpack_expect_u64_max(mpack_reader_t* reader, uint64_t max_value) {
+ return mpack_expect_u64_range(reader, 0, max_value);
+}
+
+/**
+ * Reads an unsigned integer, ensuring that it is at most @a max_value.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in an unsigned int.
+ *
+ * Returns 0 if an error occurs.
+ */
+MPACK_INLINE unsigned int mpack_expect_uint_max(mpack_reader_t* reader, unsigned int max_value) {
+ return mpack_expect_uint_range(reader, 0, max_value);
+}
+
+/**
+ * Reads an 8-bit signed integer, ensuring that it falls within the given range.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in an 8-bit signed int.
+ *
+ * Returns min_value if an error occurs.
+ */
+int8_t mpack_expect_i8_range(mpack_reader_t* reader, int8_t min_value, int8_t max_value);
+
+/**
+ * Reads a 16-bit signed integer, ensuring that it falls within the given range.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in a 16-bit signed int.
+ *
+ * Returns min_value if an error occurs.
+ */
+int16_t mpack_expect_i16_range(mpack_reader_t* reader, int16_t min_value, int16_t max_value);
+
+/**
+ * Reads a 32-bit signed integer, ensuring that it falls within the given range.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in a 32-bit signed int.
+ *
+ * Returns min_value if an error occurs.
+ */
+int32_t mpack_expect_i32_range(mpack_reader_t* reader, int32_t min_value, int32_t max_value);
+
+/**
+ * Reads a 64-bit signed integer, ensuring that it falls within the given range.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in a 64-bit signed int.
+ *
+ * Returns min_value if an error occurs.
+ */
+int64_t mpack_expect_i64_range(mpack_reader_t* reader, int64_t min_value, int64_t max_value);
+
+/**
+ * Reads a signed integer, ensuring that it falls within the given range.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in a signed int.
+ *
+ * Returns min_value if an error occurs.
+ */
+MPACK_INLINE int mpack_expect_int_range(mpack_reader_t* reader, int min_value, int max_value) {
+ // This should be true at compile-time, so this just wraps the 32-bit
+ // function. We fallback to 64-bit if for some reason sizeof(int) isn't 4.
+ if (sizeof(int) == 4)
+ return (int)mpack_expect_i32_range(reader, (int32_t)min_value, (int32_t)max_value);
+ return (int)mpack_expect_i64_range(reader, min_value, max_value);
+}
+
+/**
+ * Reads an 8-bit signed integer, ensuring that it is at least zero and at
+ * most @a max_value.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in an 8-bit signed int.
+ *
+ * Returns 0 if an error occurs.
+ */
+MPACK_INLINE int8_t mpack_expect_i8_max(mpack_reader_t* reader, int8_t max_value) {
+ return mpack_expect_i8_range(reader, 0, max_value);
+}
+
+/**
+ * Reads a 16-bit signed integer, ensuring that it is at least zero and at
+ * most @a max_value.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in a 16-bit signed int.
+ *
+ * Returns 0 if an error occurs.
+ */
+MPACK_INLINE int16_t mpack_expect_i16_max(mpack_reader_t* reader, int16_t max_value) {
+ return mpack_expect_i16_range(reader, 0, max_value);
+}
+
+/**
+ * Reads a 32-bit signed integer, ensuring that it is at least zero and at
+ * most @a max_value.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in a 32-bit signed int.
+ *
+ * Returns 0 if an error occurs.
+ */
+MPACK_INLINE int32_t mpack_expect_i32_max(mpack_reader_t* reader, int32_t max_value) {
+ return mpack_expect_i32_range(reader, 0, max_value);
+}
+
+/**
+ * Reads a 64-bit signed integer, ensuring that it is at least zero and at
+ * most @a max_value.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in a 64-bit signed int.
+ *
+ * Returns 0 if an error occurs.
+ */
+MPACK_INLINE int64_t mpack_expect_i64_max(mpack_reader_t* reader, int64_t max_value) {
+ return mpack_expect_i64_range(reader, 0, max_value);
+}
+
+/**
+ * Reads an int, ensuring that it is at least zero and at most @a max_value.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in a signed int.
+ *
+ * Returns 0 if an error occurs.
+ */
+MPACK_INLINE int mpack_expect_int_max(mpack_reader_t* reader, int max_value) {
+ return mpack_expect_int_range(reader, 0, max_value);
+}
+
+#if MPACK_FLOAT
+/**
+ * Reads a number, ensuring that it falls within the given range and returning
+ * the value as a float. The underlying value can be an integer, float or
+ * double; the value is converted to a float.
+ *
+ * @note Reading a double or a large integer with this function can incur a
+ * loss of precision.
+ *
+ * @throws mpack_error_type if the underlying value is not a float, double or integer.
+ */
+float mpack_expect_float_range(mpack_reader_t* reader, float min_value, float max_value);
+#endif
+
+#if MPACK_DOUBLE
+/**
+ * Reads a number, ensuring that it falls within the given range and returning
+ * the value as a double. The underlying value can be an integer, float or
+ * double; the value is converted to a double.
+ *
+ * @note Reading a very large integer with this function can incur a
+ * loss of precision.
+ *
+ * @throws mpack_error_type if the underlying value is not a float, double or integer.
+ */
+double mpack_expect_double_range(mpack_reader_t* reader, double min_value, double max_value);
+#endif
+
+/**
+ * @}
+ */
+
+
+
+// These are additional Basic Number functions that wrap inline range functions.
+
+/**
+ * @name Basic Number Functions
+ * @{
+ */
+
+/**
+ * Reads an unsigned int.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in an unsigned int.
+ *
+ * Returns zero if an error occurs.
+ */
+MPACK_INLINE unsigned int mpack_expect_uint(mpack_reader_t* reader) {
+
+ // This should be true at compile-time, so this just wraps the 32-bit function.
+ if (sizeof(unsigned int) == 4)
+ return (unsigned int)mpack_expect_u32(reader);
+
+ // Otherwise we wrap the max function to ensure it fits.
+ return (unsigned int)mpack_expect_u64_max(reader, MPACK_UINT_MAX);
+
+}
+
+/**
+ * Reads a signed int.
+ *
+ * The underlying type may be an integer type of any size and signedness,
+ * as long as the value can be represented in a signed int.
+ *
+ * Returns zero if an error occurs.
+ */
+MPACK_INLINE int mpack_expect_int(mpack_reader_t* reader) {
+
+ // This should be true at compile-time, so this just wraps the 32-bit function.
+ if (sizeof(int) == 4)
+ return (int)mpack_expect_i32(reader);
+
+ // Otherwise we wrap the range function to ensure it fits.
+ return (int)mpack_expect_i64_range(reader, MPACK_INT_MIN, MPACK_INT_MAX);
+
+}
+
+/**
+ * @}
+ */
+
+
+
+/**
+ * @name Matching Number Functions
+ * @{
+ */
+
+/**
+ * Reads an unsigned integer, ensuring that it exactly matches the given value.
+ *
+ * mpack_error_type is raised if the value is not representable as an unsigned
+ * integer or if it does not exactly match the given value.
+ */
+void mpack_expect_uint_match(mpack_reader_t* reader, uint64_t value);
+
+/**
+ * Reads a signed integer, ensuring that it exactly matches the given value.
+ *
+ * mpack_error_type is raised if the value is not representable as a signed
+ * integer or if it does not exactly match the given value.
+ */
+void mpack_expect_int_match(mpack_reader_t* reader, int64_t value);
+
+/**
+ * @}
+ */
+
+/**
+ * @name Other Basic Types
+ * @{
+ */
+
+/**
+ * Reads a nil, raising @ref mpack_error_type if the value is not nil.
+ */
+void mpack_expect_nil(mpack_reader_t* reader);
+
+/**
+ * Reads a boolean.
+ *
+ * @note Integers will raise mpack_error_type; the value must be strictly a boolean.
+ */
+bool mpack_expect_bool(mpack_reader_t* reader);
+
+/**
+ * Reads a boolean, raising @ref mpack_error_type if its value is not @c true.
+ */
+void mpack_expect_true(mpack_reader_t* reader);
+
+/**
+ * Reads a boolean, raising @ref mpack_error_type if its value is not @c false.
+ */
+void mpack_expect_false(mpack_reader_t* reader);
+
+/**
+ * @}
+ */
+
+/**
+ * @name Extension Functions
+ * @{
+ */
+
+#if MPACK_EXTENSIONS
+/**
+ * Reads a timestamp.
+ *
+ * @note This requires @ref MPACK_EXTENSIONS.
+ */
+mpack_timestamp_t mpack_expect_timestamp(mpack_reader_t* reader);
+
+/**
+ * Reads a timestamp in seconds, truncating the nanoseconds (if any).
+ *
+ * @note This requires @ref MPACK_EXTENSIONS.
+ */
+int64_t mpack_expect_timestamp_truncate(mpack_reader_t* reader);
+#endif
+
+/**
+ * @}
+ */
+
+/**
+ * @name Compound Types
+ * @{
+ */
+
+/**
+ * Reads the start of a map, returning its element count.
+ *
+ * A number of values follow equal to twice the element count of the map,
+ * alternating between keys and values. @ref mpack_done_map() must be called
+ * once all elements have been read.
+ *
+ * @note Maps in JSON are unordered, so it is recommended not to expect
+ * a specific ordering for your map values in case your data is converted
+ * to/from JSON.
+ *
+ * @warning This call is dangerous! It does not have a size limit, and it
+ * does not have any way of checking whether there is enough data in the
+ * message (since the data could be coming from a stream.) When looping
+ * through the map's contents, you must check for errors on each iteration
+ * of the loop. Otherwise an attacker could craft a message declaring a map
+ * of a billion elements which would throw your parsing code into an
+ * infinite loop! You should strongly consider using mpack_expect_map_max()
+ * with a safe maximum size instead.
+ *
+ * @throws mpack_error_type if the value is not a map.
+ */
+uint32_t mpack_expect_map(mpack_reader_t* reader);
+
+/**
+ * Reads the start of a map with a number of elements in the given range, returning
+ * its element count.
+ *
+ * A number of values follow equal to twice the element count of the map,
+ * alternating between keys and values. @ref mpack_done_map() must be called
+ * once all elements have been read.
+ *
+ * @note Maps in JSON are unordered, so it is recommended not to expect
+ * a specific ordering for your map values in case your data is converted
+ * to/from JSON.
+ *
+ * min_count is returned if an error occurs.
+ *
+ * @throws mpack_error_type if the value is not a map or if its size does
+ * not fall within the given range.
+ */
+uint32_t mpack_expect_map_range(mpack_reader_t* reader, uint32_t min_count, uint32_t max_count);
+
+/**
+ * Reads the start of a map with a number of elements at most @a max_count,
+ * returning its element count.
+ *
+ * A number of values follow equal to twice the element count of the map,
+ * alternating between keys and values. @ref mpack_done_map() must be called
+ * once all elements have been read.
+ *
+ * @note Maps in JSON are unordered, so it is recommended not to expect
+ * a specific ordering for your map values in case your data is converted
+ * to/from JSON.
+ *
+ * Zero is returned if an error occurs.
+ *
+ * @throws mpack_error_type if the value is not a map or if its size is
+ * greater than max_count.
+ */
+MPACK_INLINE uint32_t mpack_expect_map_max(mpack_reader_t* reader, uint32_t max_count) {
+ return mpack_expect_map_range(reader, 0, max_count);
+}
+
+/**
+ * Reads the start of a map of the exact size given.
+ *
+ * A number of values follow equal to twice the element count of the map,
+ * alternating between keys and values. @ref mpack_done_map() must be called
+ * once all elements have been read.
+ *
+ * @note Maps in JSON are unordered, so it is recommended not to expect
+ * a specific ordering for your map values in case your data is converted
+ * to/from JSON.
+ *
+ * @throws mpack_error_type if the value is not a map or if its size
+ * does not match the given count.
+ */
+void mpack_expect_map_match(mpack_reader_t* reader, uint32_t count);
+
+/**
+ * Reads a nil node or the start of a map, returning whether a map was
+ * read and placing its number of key/value pairs in count.
+ *
+ * If a map was read, a number of values follow equal to twice the element count
+ * of the map, alternating between keys and values. @ref mpack_done_map() should
+ * also be called once all elements have been read (only if a map was read.)
+ *
+ * @note Maps in JSON are unordered, so it is recommended not to expect
+ * a specific ordering for your map values in case your data is converted
+ * to/from JSON.
+ *
+ * @warning This call is dangerous! It does not have a size limit, and it
+ * does not have any way of checking whether there is enough data in the
+ * message (since the data could be coming from a stream.) When looping
+ * through the map's contents, you must check for errors on each iteration
+ * of the loop. Otherwise an attacker could craft a message declaring a map
+ * of a billion elements which would throw your parsing code into an
+ * infinite loop! You should strongly consider using mpack_expect_map_max_or_nil()
+ * with a safe maximum size instead.
+ *
+ * @returns @c true if a map was read successfully; @c false if nil was read
+ * or an error occurred.
+ * @throws mpack_error_type if the value is not a nil or map.
+ */
+bool mpack_expect_map_or_nil(mpack_reader_t* reader, uint32_t* count);
+
+/**
+ * Reads a nil node or the start of a map with a number of elements at most
+ * max_count, returning whether a map was read and placing its number of
+ * key/value pairs in count.
+ *
+ * If a map was read, a number of values follow equal to twice the element count
+ * of the map, alternating between keys and values. @ref mpack_done_map() should
+ * anlso be called once all elements have been read (only if a map was read.)
+ *
+ * @note Maps in JSON are unordered, so it is recommended not to expect
+ * a specific ordering for your map values in case your data is converted
+ * to/from JSON. Consider using mpack_expect_key_cstr() or mpack_expect_key_uint()
+ * to switch on the key; see @ref docs/expect.md for examples.
+ *
+ * @returns @c true if a map was read successfully; @c false if nil was read
+ * or an error occurred.
+ * @throws mpack_error_type if the value is not a nil or map.
+ */
+bool mpack_expect_map_max_or_nil(mpack_reader_t* reader, uint32_t max_count, uint32_t* count);
+
+/**
+ * Reads the start of an array, returning its element count.
+ *
+ * A number of values follow equal to the element count of the array.
+ * @ref mpack_done_array() must be called once all elements have been read.
+ *
+ * @warning This call is dangerous! It does not have a size limit, and it
+ * does not have any way of checking whether there is enough data in the
+ * message (since the data could be coming from a stream.) When looping
+ * through the array's contents, you must check for errors on each iteration
+ * of the loop. Otherwise an attacker could craft a message declaring an array
+ * of a billion elements which would throw your parsing code into an
+ * infinite loop! You should strongly consider using mpack_expect_array_max()
+ * with a safe maximum size instead.
+ */
+uint32_t mpack_expect_array(mpack_reader_t* reader);
+
+/**
+ * Reads the start of an array with a number of elements in the given range,
+ * returning its element count.
+ *
+ * A number of values follow equal to the element count of the array.
+ * @ref mpack_done_array() must be called once all elements have been read.
+ *
+ * min_count is returned if an error occurs.
+ *
+ * @throws mpack_error_type if the value is not an array or if its size does
+ * not fall within the given range.
+ */
+uint32_t mpack_expect_array_range(mpack_reader_t* reader, uint32_t min_count, uint32_t max_count);
+
+/**
+ * Reads the start of an array with a number of elements at most @a max_count,
+ * returning its element count.
+ *
+ * A number of values follow equal to the element count of the array.
+ * @ref mpack_done_array() must be called once all elements have been read.
+ *
+ * Zero is returned if an error occurs.
+ *
+ * @throws mpack_error_type if the value is not an array or if its size is
+ * greater than max_count.
+ */
+MPACK_INLINE uint32_t mpack_expect_array_max(mpack_reader_t* reader, uint32_t max_count) {
+ return mpack_expect_array_range(reader, 0, max_count);
+}
+
+/**
+ * Reads the start of an array of the exact size given.
+ *
+ * A number of values follow equal to the element count of the array.
+ * @ref mpack_done_array() must be called once all elements have been read.
+ *
+ * @throws mpack_error_type if the value is not an array or if its size does
+ * not match the given count.
+ */
+void mpack_expect_array_match(mpack_reader_t* reader, uint32_t count);
+
+/**
+ * Reads a nil node or the start of an array, returning whether an array was
+ * read and placing its number of elements in count.
+ *
+ * If an array was read, a number of values follow equal to the element count
+ * of the array. @ref mpack_done_array() should also be called once all elements
+ * have been read (only if an array was read.)
+ *
+ * @warning This call is dangerous! It does not have a size limit, and it
+ * does not have any way of checking whether there is enough data in the
+ * message (since the data could be coming from a stream.) When looping
+ * through the array's contents, you must check for errors on each iteration
+ * of the loop. Otherwise an attacker could craft a message declaring an array
+ * of a billion elements which would throw your parsing code into an
+ * infinite loop! You should strongly consider using mpack_expect_array_max_or_nil()
+ * with a safe maximum size instead.
+ *
+ * @returns @c true if an array was read successfully; @c false if nil was read
+ * or an error occurred.
+ * @throws mpack_error_type if the value is not a nil or array.
+ */
+bool mpack_expect_array_or_nil(mpack_reader_t* reader, uint32_t* count);
+
+/**
+ * Reads a nil node or the start of an array with a number of elements at most
+ * max_count, returning whether an array was read and placing its number of
+ * key/value pairs in count.
+ *
+ * If an array was read, a number of values follow equal to the element count
+ * of the array. @ref mpack_done_array() should also be called once all elements
+ * have been read (only if an array was read.)
+ *
+ * @returns @c true if an array was read successfully; @c false if nil was read
+ * or an error occurred.
+ * @throws mpack_error_type if the value is not a nil or array.
+ */
+bool mpack_expect_array_max_or_nil(mpack_reader_t* reader, uint32_t max_count, uint32_t* count);
+
+#ifdef MPACK_MALLOC
+/**
+ * @hideinitializer
+ *
+ * Reads the start of an array and allocates storage for it, placing its
+ * size in out_count. A number of objects follow equal to the element count
+ * of the array. You must call @ref mpack_done_array() when done (even
+ * if the element count is zero.)
+ *
+ * If an error occurs, NULL is returned and the reader is placed in an
+ * error state.
+ *
+ * If the count is zero, NULL is returned. This does not indicate error.
+ * You should not check the return value for NULL to check for errors; only
+ * check the reader's error state.
+ *
+ * The allocated array must be freed with MPACK_FREE() (or simply free()
+ * if MPack's allocator hasn't been customized.)
+ *
+ * @throws mpack_error_type if the value is not an array or if its size is
+ * greater than max_count.
+ */
+#define mpack_expect_array_alloc(reader, Type, max_count, out_count) \
+ ((Type*)mpack_expect_array_alloc_impl(reader, sizeof(Type), max_count, out_count, false))
+
+/**
+ * @hideinitializer
+ *
+ * Reads a nil node or the start of an array and allocates storage for it,
+ * placing its size in out_count. A number of objects follow equal to the element
+ * count of the array if a non-empty array was read.
+ *
+ * If an error occurs, NULL is returned and the reader is placed in an
+ * error state.
+ *
+ * If a nil node was read, NULL is returned. If an empty array was read,
+ * mpack_done_array() is called automatically and NULL is returned. These
+ * do not indicate error. You should not check the return value for NULL
+ * to check for errors; only check the reader's error state.
+ *
+ * The allocated array must be freed with MPACK_FREE() (or simply free()
+ * if MPack's allocator hasn't been customized.)
+ *
+ * @warning You must call @ref mpack_done_array() if and only if a non-zero
+ * element count is read. This function does not differentiate between nil
+ * and an empty array.
+ *
+ * @throws mpack_error_type if the value is not an array or if its size is
+ * greater than max_count.
+ */
+#define mpack_expect_array_or_nil_alloc(reader, Type, max_count, out_count) \
+ ((Type*)mpack_expect_array_alloc_impl(reader, sizeof(Type), max_count, out_count, true))
+#endif
+
+/**
+ * @}
+ */
+
+/** @cond */
+#ifdef MPACK_MALLOC
+void* mpack_expect_array_alloc_impl(mpack_reader_t* reader,
+ size_t element_size, uint32_t max_count, uint32_t* out_count, bool allow_nil);
+#endif
+/** @endcond */
+
+
+/**
+ * @name String Functions
+ * @{
+ */
+
+/**
+ * Reads the start of a string, returning its size in bytes.
+ *
+ * The bytes follow and must be read separately with mpack_read_bytes()
+ * or mpack_read_bytes_inplace(). mpack_done_str() must be called
+ * once all bytes have been read.
+ *
+ * NUL bytes are allowed in the string, and no encoding checks are done.
+ *
+ * mpack_error_type is raised if the value is not a string.
+ */
+uint32_t mpack_expect_str(mpack_reader_t* reader);
+
+/**
+ * Reads a string of at most the given size, writing it into the
+ * given buffer and returning its size in bytes.
+ *
+ * This does not add a null-terminator! Use mpack_expect_cstr() to
+ * add a null-terminator.
+ *
+ * NUL bytes are allowed in the string, and no encoding checks are done.
+ */
+size_t mpack_expect_str_buf(mpack_reader_t* reader, char* buf, size_t bufsize);
+
+/**
+ * Reads a string into the given buffer, ensuring it is a valid UTF-8 string
+ * and returning its size in bytes.
+ *
+ * This does not add a null-terminator! Use mpack_expect_utf8_cstr() to
+ * add a null-terminator.
+ *
+ * This does not accept any UTF-8 variant such as Modified UTF-8, CESU-8 or
+ * WTF-8. Only pure UTF-8 is allowed.
+ *
+ * NUL bytes are allowed in the string (as they are in UTF-8.)
+ *
+ * Raises mpack_error_too_big if there is not enough room for the string.
+ * Raises mpack_error_type if the value is not a string or is not a valid UTF-8 string.
+ */
+size_t mpack_expect_utf8(mpack_reader_t* reader, char* buf, size_t bufsize);
+
+/**
+ * Reads the start of a string, raising an error if its length is not
+ * at most the given number of bytes (not including any null-terminator.)
+ *
+ * The bytes follow and must be read separately with mpack_read_bytes()
+ * or mpack_read_bytes_inplace(). @ref mpack_done_str() must be called
+ * once all bytes have been read.
+ *
+ * @throws mpack_error_type If the value is not a string.
+ * @throws mpack_error_too_big If the string's length in bytes is larger than the given maximum size.
+ */
+MPACK_INLINE uint32_t mpack_expect_str_max(mpack_reader_t* reader, uint32_t maxsize) {
+ uint32_t length = mpack_expect_str(reader);
+ if (length > maxsize) {
+ mpack_reader_flag_error(reader, mpack_error_too_big);
+ return 0;
+ }
+ return length;
+}
+
+/**
+ * Reads the start of a string, raising an error if its length is not
+ * exactly the given number of bytes (not including any null-terminator.)
+ *
+ * The bytes follow and must be read separately with mpack_read_bytes()
+ * or mpack_read_bytes_inplace(). @ref mpack_done_str() must be called
+ * once all bytes have been read.
+ *
+ * mpack_error_type is raised if the value is not a string or if its
+ * length does not match.
+ */
+MPACK_INLINE void mpack_expect_str_length(mpack_reader_t* reader, uint32_t count) {
+ if (mpack_expect_str(reader) != count)
+ mpack_reader_flag_error(reader, mpack_error_type);
+}
+
+/**
+ * Reads a string, ensuring it exactly matches the given string.
+ *
+ * Remember that maps are unordered in JSON. Don't use this for map keys
+ * unless the map has only a single key!
+ */
+void mpack_expect_str_match(mpack_reader_t* reader, const char* str, size_t length);
+
+/**
+ * Reads a string into the given buffer, ensures it has no null bytes,
+ * and adds a null-terminator at the end.
+ *
+ * Raises mpack_error_too_big if there is not enough room for the string and null-terminator.
+ * Raises mpack_error_type if the value is not a string or contains a null byte.
+ */
+void mpack_expect_cstr(mpack_reader_t* reader, char* buf, size_t size);
+
+/**
+ * Reads a string into the given buffer, ensures it is a valid UTF-8 string
+ * without NUL characters, and adds a null-terminator at the end.
+ *
+ * This does not accept any UTF-8 variant such as Modified UTF-8, CESU-8 or
+ * WTF-8. Only pure UTF-8 is allowed, but without the NUL character, since
+ * it cannot be represented in a null-terminated string.
+ *
+ * Raises mpack_error_too_big if there is not enough room for the string and null-terminator.
+ * Raises mpack_error_type if the value is not a string or is not a valid UTF-8 string.
+ */
+void mpack_expect_utf8_cstr(mpack_reader_t* reader, char* buf, size_t size);
+
+#ifdef MPACK_MALLOC
+/**
+ * Reads a string with the given total maximum size (including space for a
+ * null-terminator), allocates storage for it, ensures it has no null-bytes,
+ * and adds a null-terminator at the end. You assume ownership of the
+ * returned pointer if reading succeeds.
+ *
+ * The allocated string must be freed with MPACK_FREE() (or simply free()
+ * if MPack's allocator hasn't been customized.)
+ *
+ * @throws mpack_error_too_big If the string plus null-terminator is larger than the given maxsize.
+ * @throws mpack_error_type If the value is not a string or contains a null byte.
+ */
+char* mpack_expect_cstr_alloc(mpack_reader_t* reader, size_t maxsize);
+
+/**
+ * Reads a string with the given total maximum size (including space for a
+ * null-terminator), allocates storage for it, ensures it is valid UTF-8
+ * with no null-bytes, and adds a null-terminator at the end. You assume
+ * ownership of the returned pointer if reading succeeds.
+ *
+ * The length in bytes of the string, not including the null-terminator,
+ * will be written to size.
+ *
+ * This does not accept any UTF-8 variant such as Modified UTF-8, CESU-8 or
+ * WTF-8. Only pure UTF-8 is allowed, but without the NUL character, since
+ * it cannot be represented in a null-terminated string.
+ *
+ * The allocated string must be freed with MPACK_FREE() (or simply free()
+ * if MPack's allocator hasn't been customized.)
+ * if you want a null-terminator.
+ *
+ * @throws mpack_error_too_big If the string plus null-terminator is larger
+ * than the given maxsize.
+ * @throws mpack_error_type If the value is not a string or contains
+ * invalid UTF-8 or a null byte.
+ */
+char* mpack_expect_utf8_cstr_alloc(mpack_reader_t* reader, size_t maxsize);
+#endif
+
+/**
+ * Reads a string, ensuring it exactly matches the given null-terminated
+ * string.
+ *
+ * Remember that maps are unordered in JSON. Don't use this for map keys
+ * unless the map has only a single key!
+ */
+MPACK_INLINE void mpack_expect_cstr_match(mpack_reader_t* reader, const char* cstr) {
+ mpack_assert(cstr != NULL, "cstr pointer is NULL");
+ mpack_expect_str_match(reader, cstr, mpack_strlen(cstr));
+}
+
+/**
+ * @}
+ */
+
+/**
+ * @name Binary Data
+ * @{
+ */
+
+/**
+ * Reads the start of a binary blob, returning its size in bytes.
+ *
+ * The bytes follow and must be read separately with mpack_read_bytes()
+ * or mpack_read_bytes_inplace(). @ref mpack_done_bin() must be called
+ * once all bytes have been read.
+ *
+ * mpack_error_type is raised if the value is not a binary blob.
+ */
+uint32_t mpack_expect_bin(mpack_reader_t* reader);
+
+/**
+ * Reads the start of a binary blob, raising an error if its length is not
+ * at most the given number of bytes.
+ *
+ * The bytes follow and must be read separately with mpack_read_bytes()
+ * or mpack_read_bytes_inplace(). @ref mpack_done_bin() must be called
+ * once all bytes have been read.
+ *
+ * mpack_error_type is raised if the value is not a binary blob or if its
+ * length does not match.
+ */
+MPACK_INLINE uint32_t mpack_expect_bin_max(mpack_reader_t* reader, uint32_t maxsize) {
+ uint32_t length = mpack_expect_bin(reader);
+ if (length > maxsize) {
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+ }
+ return length;
+}
+
+/**
+ * Reads the start of a binary blob, raising an error if its length is not
+ * exactly the given number of bytes.
+ *
+ * The bytes follow and must be read separately with mpack_read_bytes()
+ * or mpack_read_bytes_inplace(). @ref mpack_done_bin() must be called
+ * once all bytes have been read.
+ *
+ * @throws mpack_error_type if the value is not a binary blob or if its size
+ * does not match.
+ */
+MPACK_INLINE void mpack_expect_bin_size(mpack_reader_t* reader, uint32_t count) {
+ if (mpack_expect_bin(reader) != count)
+ mpack_reader_flag_error(reader, mpack_error_type);
+}
+
+/**
+ * Reads a binary blob into the given buffer, returning its size in bytes.
+ *
+ * For compatibility, this will accept if the underlying type is string or
+ * binary (since in MessagePack 1.0, strings and binary data were combined
+ * under the "raw" type which became string in 1.1.)
+ */
+size_t mpack_expect_bin_buf(mpack_reader_t* reader, char* buf, size_t size);
+
+/**
+ * Reads a binary blob with the exact given size into the given buffer.
+ *
+ * For compatibility, this will accept if the underlying type is string or
+ * binary (since in MessagePack 1.0, strings and binary data were combined
+ * under the "raw" type which became string in 1.1.)
+ *
+ * @throws mpack_error_type if the value is not a binary blob or if its size
+ * does not match.
+ */
+void mpack_expect_bin_size_buf(mpack_reader_t* reader, char* buf, uint32_t size);
+
+/**
+ * Reads a binary blob with the given total maximum size, allocating storage for it.
+ */
+char* mpack_expect_bin_alloc(mpack_reader_t* reader, size_t maxsize, size_t* size);
+
+/**
+ * @}
+ */
+
+/**
+ * @name Extension Functions
+ * @{
+ */
+
+#if MPACK_EXTENSIONS
+/**
+ * Reads the start of an extension blob, returning its size in bytes and
+ * placing the type into @p type.
+ *
+ * The bytes follow and must be read separately with mpack_read_bytes()
+ * or mpack_read_bytes_inplace(). @ref mpack_done_ext() must be called
+ * once all bytes have been read.
+ *
+ * @p type will be a user-defined type in the range [0,127] or a reserved type
+ * in the range [-128,-2].
+ *
+ * mpack_error_type is raised if the value is not an extension blob. The @p
+ * type value is zero if an error occurs.
+ *
+ * @note This cannot be used to match a timestamp. @ref mpack_error_type will
+ * be flagged if the value is a timestamp. Use mpack_expect_timestamp() or
+ * mpack_expect_timestamp_truncate() instead.
+ *
+ * @note This requires @ref MPACK_EXTENSIONS.
+ *
+ * @warning Be careful when using reserved types. They may no longer be ext
+ * types in the future, and previously valid data containing reserved types may
+ * become invalid in the future.
+ */
+uint32_t mpack_expect_ext(mpack_reader_t* reader, int8_t* type);
+
+/**
+ * Reads the start of an extension blob, raising an error if its length is not
+ * at most the given number of bytes and placing the type into @p type.
+ *
+ * The bytes follow and must be read separately with mpack_read_bytes()
+ * or mpack_read_bytes_inplace(). @ref mpack_done_ext() must be called
+ * once all bytes have been read.
+ *
+ * mpack_error_type is raised if the value is not an extension blob or if its
+ * length does not match. The @p type value is zero if an error is raised.
+ *
+ * @p type will be a user-defined type in the range [0,127] or a reserved type
+ * in the range [-128,-2].
+ *
+ * @note This cannot be used to match a timestamp. @ref mpack_error_type will
+ * be flagged if the value is a timestamp. Use mpack_expect_timestamp() or
+ * mpack_expect_timestamp_truncate() instead.
+ *
+ * @note This requires @ref MPACK_EXTENSIONS.
+ *
+ * @warning Be careful when using reserved types. They may no longer be ext
+ * types in the future, and previously valid data containing reserved types may
+ * become invalid in the future.
+ *
+ * @see mpack_expect_ext()
+ */
+MPACK_INLINE uint32_t mpack_expect_ext_max(mpack_reader_t* reader, int8_t* type, uint32_t maxsize) {
+ uint32_t length = mpack_expect_ext(reader, type);
+ if (length > maxsize) {
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+ }
+ return length;
+}
+
+/**
+ * Reads the start of an extension blob, raising an error if its length is not
+ * exactly the given number of bytes and placing the type into @p type.
+ *
+ * The bytes follow and must be read separately with mpack_read_bytes()
+ * or mpack_read_bytes_inplace(). @ref mpack_done_ext() must be called
+ * once all bytes have been read.
+ *
+ * mpack_error_type is raised if the value is not an extension blob or if its
+ * length does not match. The @p type value is zero if an error is raised.
+ *
+ * @p type will be a user-defined type in the range [0,127] or a reserved type
+ * in the range [-128,-2].
+ *
+ * @note This cannot be used to match a timestamp. @ref mpack_error_type will
+ * be flagged if the value is a timestamp. Use mpack_expect_timestamp() or
+ * mpack_expect_timestamp_truncate() instead.
+ *
+ * @note This requires @ref MPACK_EXTENSIONS.
+ *
+ * @warning Be careful when using reserved types. They may no longer be ext
+ * types in the future, and previously valid data containing reserved types may
+ * become invalid in the future.
+ *
+ * @see mpack_expect_ext()
+ */
+MPACK_INLINE void mpack_expect_ext_size(mpack_reader_t* reader, int8_t* type, uint32_t count) {
+ if (mpack_expect_ext(reader, type) != count) {
+ *type = 0;
+ mpack_reader_flag_error(reader, mpack_error_type);
+ }
+}
+
+/**
+ * Reads an extension blob into the given buffer, returning its size in bytes
+ * and placing the type into @p type.
+ *
+ * mpack_error_type is raised if the value is not an extension blob or if its
+ * length does not match. The @p type value is zero if an error is raised.
+ *
+ * @p type will be a user-defined type in the range [0,127] or a reserved type
+ * in the range [-128,-2].
+ *
+ * @note This cannot be used to match a timestamp. @ref mpack_error_type will
+ * be flagged if the value is a timestamp. Use mpack_expect_timestamp() or
+ * mpack_expect_timestamp_truncate() instead.
+ *
+ * @warning Be careful when using reserved types. They may no longer be ext
+ * types in the future, and previously valid data containing reserved types may
+ * become invalid in the future.
+ *
+ * @note This requires @ref MPACK_EXTENSIONS.
+ *
+ * @see mpack_expect_ext()
+ */
+size_t mpack_expect_ext_buf(mpack_reader_t* reader, int8_t* type, char* buf, size_t size);
+#endif
+
+#if MPACK_EXTENSIONS && defined(MPACK_MALLOC)
+/**
+ * Reads an extension blob with the given total maximum size, allocating
+ * storage for it, and placing the type into @p type.
+ *
+ * mpack_error_type is raised if the value is not an extension blob or if its
+ * length does not match. The @p type value is zero if an error is raised.
+ *
+ * @p type will be a user-defined type in the range [0,127] or a reserved type
+ * in the range [-128,-2].
+ *
+ * @note This cannot be used to match a timestamp. @ref mpack_error_type will
+ * be flagged if the value is a timestamp. Use mpack_expect_timestamp() or
+ * mpack_expect_timestamp_truncate() instead.
+ *
+ * @warning Be careful when using reserved types. They may no longer be ext
+ * types in the future, and previously valid data containing reserved types may
+ * become invalid in the future.
+ *
+ * @note This requires @ref MPACK_EXTENSIONS and @ref MPACK_MALLOC.
+ *
+ * @see mpack_expect_ext()
+ */
+char* mpack_expect_ext_alloc(mpack_reader_t* reader, int8_t* type, size_t maxsize, size_t* size);
+#endif
+
+/**
+ * @}
+ */
+
+/**
+ * @name Special Functions
+ * @{
+ */
+
+/**
+ * Reads a MessagePack object header (an MPack tag), expecting it to exactly
+ * match the given tag.
+ *
+ * If the type is compound (i.e. is a map, array, string, binary or
+ * extension type), additional reads are required to get the contained
+ * data, and the corresponding done function must be called when done.
+ *
+ * @throws mpack_error_type if the tag does not match
+ *
+ * @see mpack_read_bytes()
+ * @see mpack_done_array()
+ * @see mpack_done_map()
+ * @see mpack_done_str()
+ * @see mpack_done_bin()
+ * @see mpack_done_ext()
+ */
+void mpack_expect_tag(mpack_reader_t* reader, mpack_tag_t tag);
+
+/**
+ * Expects a string matching one of the strings in the given array,
+ * returning its array index.
+ *
+ * If the value does not match any of the given strings,
+ * @ref mpack_error_type is flagged. Use mpack_expect_enum_optional()
+ * if you want to allow other values than the given strings.
+ *
+ * If any error occurs or the reader is in an error state, @a count
+ * is returned.
+ *
+ * This can be used to quickly parse a string into an enum when the
+ * enum values range from 0 to @a count-1. If the last value in the
+ * enum is a special "count" value, it can be passed as the count,
+ * and the return value can be cast directly to the enum type.
+ *
+ * @code{.c}
+ * typedef enum { APPLE , BANANA , ORANGE , COUNT} fruit_t;
+ * const char* fruits[] = {"apple", "banana", "orange"};
+ *
+ * fruit_t fruit = (fruit_t)mpack_expect_enum(reader, fruits, COUNT);
+ * @endcode
+ *
+ * See @ref docs/expect.md for more examples.
+ *
+ * The maximum string length is the size of the buffer (strings are read in-place.)
+ *
+ * @param reader The reader
+ * @param strings An array of expected strings of length count
+ * @param count The number of strings
+ * @return The index of the matched string, or @a count in case of error
+ */
+size_t mpack_expect_enum(mpack_reader_t* reader, const char* strings[], size_t count);
+
+/**
+ * Expects a string matching one of the strings in the given array
+ * returning its array index, or @a count if no strings match.
+ *
+ * If the value is not a string, or it does not match any of the
+ * given strings, @a count is returned and no error is flagged.
+ *
+ * If any error occurs or the reader is in an error state, @a count
+ * is returned.
+ *
+ * This can be used to quickly parse a string into an enum when the
+ * enum values range from 0 to @a count-1. If the last value in the
+ * enum is a special "count" value, it can be passed as the count,
+ * and the return value can be cast directly to the enum type.
+ *
+ * @code{.c}
+ * typedef enum { APPLE , BANANA , ORANGE , COUNT} fruit_t;
+ * const char* fruits[] = {"apple", "banana", "orange"};
+ *
+ * fruit_t fruit = (fruit_t)mpack_expect_enum_optional(reader, fruits, COUNT);
+ * @endcode
+ *
+ * See @ref docs/expect.md for more examples.
+ *
+ * The maximum string length is the size of the buffer (strings are read in-place.)
+ *
+ * @param reader The reader
+ * @param strings An array of expected strings of length count
+ * @param count The number of strings
+ *
+ * @return The index of the matched string, or @a count if it does not
+ * match or an error occurs
+ */
+size_t mpack_expect_enum_optional(mpack_reader_t* reader, const char* strings[], size_t count);
+
+/**
+ * Expects an unsigned integer map key between 0 and count-1, marking it
+ * as found in the given bool array and returning it.
+ *
+ * This is a helper for switching among int keys in a map. It is
+ * typically used with an enum to define the key values. It should
+ * be called in the expression of a switch() statement. See @ref
+ * docs/expect.md for an example.
+ *
+ * The found array must be cleared before expecting the first key. If the
+ * flag for a given key is already set when found (i.e. the map contains a
+ * duplicate key), mpack_error_invalid is flagged.
+ *
+ * If the key is not a non-negative integer, or if the key is @a count or
+ * larger, @a count is returned and no error is flagged. If you want an error
+ * on unrecognized keys, flag an error in the default case in your switch;
+ * otherwise you must call mpack_discard() to discard its content.
+ *
+ * @param reader The reader
+ * @param found An array of bool flags of length count
+ * @param count The number of values in the found array, and one more than the
+ * maximum allowed key
+ *
+ * @see @ref docs/expect.md
+ */
+size_t mpack_expect_key_uint(mpack_reader_t* reader, bool found[], size_t count);
+
+/**
+ * Expects a string map key matching one of the strings in the given key list,
+ * marking it as found in the given bool array and returning its index.
+ *
+ * This is a helper for switching among string keys in a map. It is
+ * typically used with an enum with names matching the strings in the
+ * array to define the key indices. It should be called in the expression
+ * of a switch() statement. See @ref docs/expect.md for an example.
+ *
+ * The found array must be cleared before expecting the first key. If the
+ * flag for a given key is already set when found (i.e. the map contains a
+ * duplicate key), mpack_error_invalid is flagged.
+ *
+ * If the key is unrecognized, count is returned and no error is flagged. If
+ * you want an error on unrecognized keys, flag an error in the default case
+ * in your switch; otherwise you must call mpack_discard() to discard its content.
+ *
+ * The maximum key length is the size of the buffer (keys are read in-place.)
+ *
+ * @param reader The reader
+ * @param keys An array of expected string keys of length count
+ * @param found An array of bool flags of length count
+ * @param count The number of values in the keys and found arrays
+ *
+ * @see @ref docs/expect.md
+ */
+size_t mpack_expect_key_cstr(mpack_reader_t* reader, const char* keys[],
+ bool found[], size_t count);
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+#endif
+
+MPACK_EXTERN_C_END
+MPACK_SILENCE_WARNINGS_END
+
+#endif
+
+
+
+/* mpack/mpack-node.h.h */
+
+/**
+ * @file
+ *
+ * Declares the MPack dynamic Node API.
+ */
+
+#ifndef MPACK_NODE_H
+#define MPACK_NODE_H 1
+
+/* #include "mpack-reader.h" */
+
+MPACK_SILENCE_WARNINGS_BEGIN
+MPACK_EXTERN_C_BEGIN
+
+#if MPACK_NODE
+
+/**
+ * @defgroup node Node API
+ *
+ * The MPack Node API allows you to parse a chunk of MessagePack into a
+ * dynamically typed data structure, providing random access to the parsed
+ * data.
+ *
+ * See @ref docs/node.md for examples.
+ *
+ * @{
+ */
+
+/**
+ * A handle to node data in a parsed MPack tree.
+ *
+ * Nodes represent either primitive values or compound types. If a
+ * node is a compound type, it contains a pointer to its child nodes,
+ * or a pointer to its underlying data.
+ *
+ * Nodes are immutable.
+ *
+ * @note @ref mpack_node_t is an opaque reference to the node data, not the
+ * node data itself. (It contains pointers to both the node data and the tree.)
+ * It is passed by value in the Node API.
+ */
+typedef struct mpack_node_t mpack_node_t;
+
+/**
+ * The storage for nodes in an MPack tree.
+ *
+ * You only need to use this if you intend to provide your own storage
+ * for nodes instead of letting the tree allocate it.
+ *
+ * @ref mpack_node_data_t is 16 bytes on most common architectures (32-bit
+ * and 64-bit.)
+ */
+typedef struct mpack_node_data_t mpack_node_data_t;
+
+/**
+ * An MPack tree parser to parse a blob or stream of MessagePack.
+ *
+ * When a message is parsed, the tree contains a single root node which
+ * contains all parsed data. The tree and its nodes are immutable.
+ */
+typedef struct mpack_tree_t mpack_tree_t;
+
+/**
+ * An error handler function to be called when an error is flagged on
+ * the tree.
+ *
+ * The error handler will only be called once on the first error flagged;
+ * any subsequent node reads and errors are ignored, and the tree is
+ * permanently in that error state.
+ *
+ * MPack is safe against non-local jumps out of error handler callbacks.
+ * This means you are allowed to longjmp or throw an exception (in C++,
+ * Objective-C, or with SEH) out of this callback.
+ *
+ * Bear in mind when using longjmp that local non-volatile variables that
+ * have changed are undefined when setjmp() returns, so you can't put the
+ * tree on the stack in the same activation frame as the setjmp without
+ * declaring it volatile.
+ *
+ * You must still eventually destroy the tree. It is not destroyed
+ * automatically when an error is flagged. It is safe to destroy the
+ * tree within this error callback, but you will either need to perform
+ * a non-local jump, or store something in your context to identify
+ * that the tree is destroyed since any future accesses to it cause
+ * undefined behavior.
+ */
+typedef void (*mpack_tree_error_t)(mpack_tree_t* tree, mpack_error_t error);
+
+/**
+ * The MPack tree's read function. It should fill the buffer with as many bytes
+ * as are immediately available up to the given @c count, returning the number
+ * of bytes written to the buffer.
+ *
+ * In case of error, it should flag an appropriate error on the reader
+ * (usually @ref mpack_error_io.)
+ *
+ * The blocking or non-blocking behaviour of the read should match whether you
+ * are using mpack_tree_parse() or mpack_tree_try_parse().
+ *
+ * If you are using mpack_tree_parse(), the read should block until at least
+ * one byte is read. If you return 0, mpack_tree_parse() will raise @ref
+ * mpack_error_io.
+ *
+ * If you are using mpack_tree_try_parse(), the read function can always
+ * return 0, and must never block waiting for data (otherwise
+ * mpack_tree_try_parse() would be equivalent to mpack_tree_parse().)
+ * When you return 0, mpack_tree_try_parse() will return false without flagging
+ * an error.
+ */
+typedef size_t (*mpack_tree_read_t)(mpack_tree_t* tree, char* buffer, size_t count);
+
+/**
+ * A teardown function to be called when the tree is destroyed.
+ */
+typedef void (*mpack_tree_teardown_t)(mpack_tree_t* tree);
+
+
+
+/* Hide internals from documentation */
+/** @cond */
+
+struct mpack_node_t {
+ mpack_node_data_t* data;
+ mpack_tree_t* tree;
+};
+
+struct mpack_node_data_t {
+ mpack_type_t type;
+
+ /*
+ * The element count if the type is an array;
+ * the number of key/value pairs if the type is map;
+ * or the number of bytes if the type is str, bin or ext.
+ */
+ uint32_t len;
+
+ union {
+ bool b; /* The value if the type is bool. */
+
+ #if MPACK_FLOAT
+ float f; /* The value if the type is float. */
+ #else
+ uint32_t f; /*< The raw value if the type is float. */
+ #endif
+
+ #if MPACK_DOUBLE
+ double d; /* The value if the type is double. */
+ #else
+ uint64_t d; /*< The raw value if the type is double. */
+ #endif
+
+ int64_t i; /* The value if the type is signed int. */
+ uint64_t u; /* The value if the type is unsigned int. */
+ size_t offset; /* The byte offset for str, bin and ext */
+
+ mpack_node_data_t* children; /* The children for map or array */
+ } value;
+};
+
+typedef struct mpack_tree_page_t {
+ struct mpack_tree_page_t* next;
+ mpack_node_data_t nodes[1]; // variable size
+} mpack_tree_page_t;
+
+typedef enum mpack_tree_parse_state_t {
+ mpack_tree_parse_state_not_started,
+ mpack_tree_parse_state_in_progress,
+ mpack_tree_parse_state_parsed,
+} mpack_tree_parse_state_t;
+
+typedef struct mpack_level_t {
+ mpack_node_data_t* child;
+ size_t left; // children left in level
+} mpack_level_t;
+
+typedef struct mpack_tree_parser_t {
+ mpack_tree_parse_state_t state;
+
+ // We keep track of the number of "possible nodes" left in the data rather
+ // than the number of bytes.
+ //
+ // When a map or array is parsed, we ensure at least one byte for each child
+ // exists and subtract them right away. This ensures that if ever a map or
+ // array declares more elements than could possibly be contained in the data,
+ // we will error out immediately rather than allocating storage for them.
+ //
+ // For example malicious data that repeats 0xDE 0xFF 0xFF (start of a map
+ // with 65536 key-value pairs) would otherwise cause us to run out of
+ // memory. With this, the parser can allocate at most as many nodes as
+ // there are bytes in the data (plus the paging overhead, 12%.) An error
+ // will be flagged immediately if and when there isn't enough data left to
+ // fully read all children of all open compound types on the parsing stack.
+ //
+ // Once an entire message has been parsed (and there are no nodes left to
+ // parse whose bytes have been subtracted), this matches the number of left
+ // over bytes in the data.
+ size_t possible_nodes_left;
+
+ mpack_node_data_t* nodes; // next node in current page/pool
+ size_t nodes_left; // nodes left in current page/pool
+
+ size_t current_node_reserved;
+ size_t level;
+
+ #ifdef MPACK_MALLOC
+ // It's much faster to allocate the initial parsing stack inline within the
+ // parser. We replace it with a heap allocation if we need to grow it.
+ mpack_level_t* stack;
+ size_t stack_capacity;
+ bool stack_owned;
+ mpack_level_t stack_local[MPACK_NODE_INITIAL_DEPTH];
+ #else
+ // Without malloc(), we have to reserve a parsing stack the maximum allowed
+ // parsing depth.
+ mpack_level_t stack[MPACK_NODE_MAX_DEPTH_WITHOUT_MALLOC];
+ #endif
+} mpack_tree_parser_t;
+
+struct mpack_tree_t {
+ mpack_tree_error_t error_fn; /* Function to call on error */
+ mpack_tree_read_t read_fn; /* Function to call to read more data */
+ mpack_tree_teardown_t teardown; /* Function to teardown the context on destroy */
+ void* context; /* Context for tree callbacks */
+
+ mpack_node_data_t nil_node; /* a nil node to be returned in case of error */
+ mpack_node_data_t missing_node; /* a missing node to be returned in optional lookups */
+ mpack_error_t error;
+
+ #ifdef MPACK_MALLOC
+ char* buffer;
+ size_t buffer_capacity;
+ #endif
+
+ const char* data;
+ size_t data_length; // length of data (and content of buffer, if used)
+
+ size_t size; // size in bytes of tree (usually matches data_length, but not if tree has trailing data)
+ size_t node_count; // total number of nodes in tree (across all pages)
+
+ size_t max_size; // maximum message size
+ size_t max_nodes; // maximum nodes in a message
+
+ mpack_tree_parser_t parser;
+ mpack_node_data_t* root;
+
+ mpack_node_data_t* pool; // pool, or NULL if no pool provided
+ size_t pool_count;
+
+ #ifdef MPACK_MALLOC
+ mpack_tree_page_t* next;
+ #endif
+};
+
+// internal functions
+
+MPACK_INLINE mpack_node_t mpack_node(mpack_tree_t* tree, mpack_node_data_t* data) {
+ mpack_node_t node;
+ node.data = data;
+ node.tree = tree;
+ return node;
+}
+
+MPACK_INLINE mpack_node_data_t* mpack_node_child(mpack_node_t node, size_t child) {
+ return node.data->value.children + child;
+}
+
+MPACK_INLINE mpack_node_t mpack_tree_nil_node(mpack_tree_t* tree) {
+ return mpack_node(tree, &tree->nil_node);
+}
+
+MPACK_INLINE mpack_node_t mpack_tree_missing_node(mpack_tree_t* tree) {
+ return mpack_node(tree, &tree->missing_node);
+}
+
+/** @endcond */
+
+
+
+/**
+ * @name Tree Initialization
+ * @{
+ */
+
+#ifdef MPACK_MALLOC
+/**
+ * Initializes a tree parser with the given data.
+ *
+ * Configure the tree if desired, then call mpack_tree_parse() to parse it. The
+ * tree will allocate pages of nodes as needed and will free them when
+ * destroyed.
+ *
+ * The tree must be destroyed with mpack_tree_destroy().
+ *
+ * Any string or blob data types reference the original data, so the given data
+ * pointer must remain valid until after the tree is destroyed.
+ */
+void mpack_tree_init_data(mpack_tree_t* tree, const char* data, size_t length);
+
+/**
+ * Deprecated.
+ *
+ * \deprecated Renamed to mpack_tree_init_data().
+ */
+MPACK_INLINE void mpack_tree_init(mpack_tree_t* tree, const char* data, size_t length) {
+ mpack_tree_init_data(tree, data, length);
+}
+
+/**
+ * Initializes a tree parser from an unbounded stream, or a stream of
+ * unknown length.
+ *
+ * The parser can be used to read a single message from a stream of unknown
+ * length, or multiple messages from an unbounded stream, allowing it to
+ * be used for RPC communication. Call @ref mpack_tree_parse() to parse
+ * a message from a blocking stream, or @ref mpack_tree_try_parse() for a
+ * non-blocking stream.
+ *
+ * The stream will use a growable internal buffer to store the most recent
+ * message, as well as allocated pages of nodes for the parse tree.
+ *
+ * Maximum allowances for message size and node count must be specified in this
+ * function (since the stream is unbounded.) They can be changed later with
+ * @ref mpack_tree_set_limits().
+ *
+ * @param tree The tree parser
+ * @param read_fn The read function
+ * @param context The context for the read function
+ * @param max_message_size The maximum size of a message in bytes
+ * @param max_message_nodes The maximum number of nodes per message. See
+ * @ref mpack_node_data_t for the size of nodes.
+ *
+ * @see mpack_tree_read_t
+ * @see mpack_reader_context()
+ */
+void mpack_tree_init_stream(mpack_tree_t* tree, mpack_tree_read_t read_fn, void* context,
+ size_t max_message_size, size_t max_message_nodes);
+#endif
+
+/**
+ * Initializes a tree parser with the given data, using the given node data
+ * pool to store the results.
+ *
+ * Configure the tree if desired, then call mpack_tree_parse() to parse it.
+ *
+ * If the data does not fit in the pool, @ref mpack_error_too_big will be flagged
+ * on the tree.
+ *
+ * The tree must be destroyed with mpack_tree_destroy(), even if parsing fails.
+ */
+void mpack_tree_init_pool(mpack_tree_t* tree, const char* data, size_t length,
+ mpack_node_data_t* node_pool, size_t node_pool_count);
+
+/**
+ * Initializes an MPack tree directly into an error state. Use this if you
+ * are writing a wrapper to another <tt>mpack_tree_init*()</tt> function which
+ * can fail its setup.
+ */
+void mpack_tree_init_error(mpack_tree_t* tree, mpack_error_t error);
+
+#if MPACK_STDIO
+/**
+ * Initializes a tree to parse the given file. The tree must be destroyed with
+ * mpack_tree_destroy(), even if parsing fails.
+ *
+ * The file is opened, loaded fully into memory, and closed before this call
+ * returns.
+ *
+ * @param tree The tree to initialize
+ * @param filename The filename passed to fopen() to read the file
+ * @param max_bytes The maximum size of file to load, or 0 for unlimited size.
+ */
+void mpack_tree_init_filename(mpack_tree_t* tree, const char* filename, size_t max_bytes);
+
+/**
+ * Deprecated.
+ *
+ * \deprecated Renamed to mpack_tree_init_filename().
+ */
+MPACK_INLINE void mpack_tree_init_file(mpack_tree_t* tree, const char* filename, size_t max_bytes) {
+ mpack_tree_init_filename(tree, filename, max_bytes);
+}
+
+/**
+ * Initializes a tree to parse the given libc FILE. This can be used to
+ * read from stdin, or from a file opened separately.
+ *
+ * The tree must be destroyed with mpack_tree_destroy(), even if parsing fails.
+ *
+ * The FILE is fully loaded fully into memory (and closed if requested) before
+ * this call returns.
+ *
+ * @param tree The tree to initialize.
+ * @param stdfile The FILE.
+ * @param max_bytes The maximum size of file to load, or 0 for unlimited size.
+ * @param close_when_done If true, fclose() will be called on the FILE when it
+ * is no longer needed. If false, the file will not be closed when
+ * reading is done.
+ *
+ * @warning The tree will read all data in the FILE before parsing it. If this
+ * is used on stdin, the parser will block until it is closed, even if
+ * a complete message has been written to it!
+ */
+void mpack_tree_init_stdfile(mpack_tree_t* tree, FILE* stdfile, size_t max_bytes, bool close_when_done);
+#endif
+
+/**
+ * @}
+ */
+
+/**
+ * @name Tree Functions
+ * @{
+ */
+
+/**
+ * Sets the maximum byte size and maximum number of nodes allowed per message.
+ *
+ * The default is SIZE_MAX (no limit) unless @ref mpack_tree_init_stream() is
+ * called (where maximums are required.)
+ *
+ * If a pool of nodes is used, the node limit is the lesser of this limit and
+ * the pool size.
+ *
+ * @param tree The tree parser
+ * @param max_message_size The maximum size of a message in bytes
+ * @param max_message_nodes The maximum number of nodes per message. See
+ * @ref mpack_node_data_t for the size of nodes.
+ */
+void mpack_tree_set_limits(mpack_tree_t* tree, size_t max_message_size,
+ size_t max_message_nodes);
+
+/**
+ * Parses a MessagePack message into a tree of immutable nodes.
+ *
+ * If successful, the root node will be available under @ref mpack_tree_root().
+ * If not, an appropriate error will be flagged.
+ *
+ * This can be called repeatedly to parse a series of messages from a data
+ * source. When this is called, all previous nodes from this tree and their
+ * contents (including the root node) are invalidated.
+ *
+ * If this is called with a stream (see @ref mpack_tree_init_stream()), the
+ * stream must block until data is available. (Otherwise, if this is called on
+ * a non-blocking stream, parsing will fail with @ref mpack_error_io when the
+ * fill function returns 0.)
+ *
+ * There is no way to recover a tree in an error state. It must be destroyed.
+ */
+void mpack_tree_parse(mpack_tree_t* tree);
+
+/**
+ * Attempts to parse a MessagePack message from a non-blocking stream into a
+ * tree of immutable nodes.
+ *
+ * A non-blocking read function must have been passed to the tree in
+ * mpack_tree_init_stream().
+ *
+ * If this returns true, a message is available under
+ * @ref mpack_tree_root(). The tree nodes and data will be valid until
+ * the next time a parse is started.
+ *
+ * If this returns false, no message is available, because either not enough
+ * data is available yet or an error has occurred. You must check the tree for
+ * errors whenever this returns false. If there is no error, you should try
+ * again later when more data is available. (You will want to select()/poll()
+ * on the underlying socket or use some other asynchronous mechanism to
+ * determine when it has data.)
+ *
+ * There is no way to recover a tree in an error state. It must be destroyed.
+ *
+ * @see mpack_tree_init_stream()
+ */
+bool mpack_tree_try_parse(mpack_tree_t* tree);
+
+/**
+ * Returns the root node of the tree, if the tree is not in an error state.
+ * Returns a nil node otherwise.
+ *
+ * @warning You must call mpack_tree_parse() before calling this. If
+ * @ref mpack_tree_parse() was never called, the tree will assert.
+ */
+mpack_node_t mpack_tree_root(mpack_tree_t* tree);
+
+/**
+ * Returns the error state of the tree.
+ */
+MPACK_INLINE mpack_error_t mpack_tree_error(mpack_tree_t* tree) {
+ return tree->error;
+}
+
+/**
+ * Returns the size in bytes of the current parsed message.
+ *
+ * If there is something in the buffer after the MessagePack object, this can
+ * be used to find it.
+ *
+ * This is zero if an error occurred during tree parsing (since the
+ * portion of the data that the first complete object occupies cannot
+ * be determined if the data is invalid or corrupted.)
+ */
+MPACK_INLINE size_t mpack_tree_size(mpack_tree_t* tree) {
+ return tree->size;
+}
+
+/**
+ * Destroys the tree.
+ */
+mpack_error_t mpack_tree_destroy(mpack_tree_t* tree);
+
+/**
+ * Sets the custom pointer to pass to the tree callbacks, such as teardown.
+ *
+ * @param tree The MPack tree.
+ * @param context User data to pass to the tree callbacks.
+ *
+ * @see mpack_reader_context()
+ */
+MPACK_INLINE void mpack_tree_set_context(mpack_tree_t* tree, void* context) {
+ tree->context = context;
+}
+
+/**
+ * Returns the custom context for tree callbacks.
+ *
+ * @see mpack_tree_set_context
+ * @see mpack_tree_init_stream
+ */
+MPACK_INLINE void* mpack_tree_context(mpack_tree_t* tree) {
+ return tree->context;
+}
+
+/**
+ * Sets the error function to call when an error is flagged on the tree.
+ *
+ * This should normally be used with mpack_tree_set_context() to register
+ * a custom pointer to pass to the error function.
+ *
+ * See the definition of mpack_tree_error_t for more information about
+ * what you can do from an error callback.
+ *
+ * @see mpack_tree_error_t
+ * @param tree The MPack tree.
+ * @param error_fn The function to call when an error is flagged on the tree.
+ */
+MPACK_INLINE void mpack_tree_set_error_handler(mpack_tree_t* tree, mpack_tree_error_t error_fn) {
+ tree->error_fn = error_fn;
+}
+
+/**
+ * Sets the teardown function to call when the tree is destroyed.
+ *
+ * This should normally be used with mpack_tree_set_context() to register
+ * a custom pointer to pass to the teardown function.
+ *
+ * @param tree The MPack tree.
+ * @param teardown The function to call when the tree is destroyed.
+ */
+MPACK_INLINE void mpack_tree_set_teardown(mpack_tree_t* tree, mpack_tree_teardown_t teardown) {
+ tree->teardown = teardown;
+}
+
+/**
+ * Places the tree in the given error state, calling the error callback if one
+ * is set.
+ *
+ * This allows you to externally flag errors, for example if you are validating
+ * data as you read it.
+ *
+ * If the tree is already in an error state, this call is ignored and no
+ * error callback is called.
+ */
+void mpack_tree_flag_error(mpack_tree_t* tree, mpack_error_t error);
+
+/**
+ * @}
+ */
+
+/**
+ * @name Node Core Functions
+ * @{
+ */
+
+/**
+ * Places the node's tree in the given error state, calling the error callback
+ * if one is set.
+ *
+ * This allows you to externally flag errors, for example if you are validating
+ * data as you read it.
+ *
+ * If the tree is already in an error state, this call is ignored and no
+ * error callback is called.
+ */
+void mpack_node_flag_error(mpack_node_t node, mpack_error_t error);
+
+/**
+ * Returns the error state of the node's tree.
+ */
+MPACK_INLINE mpack_error_t mpack_node_error(mpack_node_t node) {
+ return mpack_tree_error(node.tree);
+}
+
+/**
+ * Returns a tag describing the given node, or a nil tag if the
+ * tree is in an error state.
+ */
+mpack_tag_t mpack_node_tag(mpack_node_t node);
+
+/** @cond */
+
+#if MPACK_DEBUG && MPACK_STDIO
+/*
+ * Converts a node to a pseudo-JSON string for debugging purposes, placing the
+ * result in the given buffer with a null-terminator.
+ *
+ * If the buffer does not have enough space, the result will be truncated (but
+ * it is guaranteed to be null-terminated.)
+ *
+ * This is only available in debug mode, and only if stdio is available (since
+ * it uses snprintf().) It's strictly for debugging purposes.
+ */
+void mpack_node_print_to_buffer(mpack_node_t node, char* buffer, size_t buffer_size);
+
+/*
+ * Converts a node to pseudo-JSON for debugging purposes, calling the given
+ * callback as many times as is necessary to output the character data.
+ *
+ * No null-terminator or trailing newline will be written.
+ *
+ * This is only available in debug mode, and only if stdio is available (since
+ * it uses snprintf().) It's strictly for debugging purposes.
+ */
+void mpack_node_print_to_callback(mpack_node_t node, mpack_print_callback_t callback, void* context);
+
+/*
+ * Converts a node to pseudo-JSON for debugging purposes
+ * and pretty-prints it to the given file.
+ *
+ * This is only available in debug mode, and only if stdio is available (since
+ * it uses snprintf().) It's strictly for debugging purposes.
+ */
+void mpack_node_print_to_file(mpack_node_t node, FILE* file);
+
+/*
+ * Converts a node to pseudo-JSON for debugging purposes
+ * and pretty-prints it to stdout.
+ *
+ * This is only available in debug mode, and only if stdio is available (since
+ * it uses snprintf().) It's strictly for debugging purposes.
+ */
+MPACK_INLINE void mpack_node_print_to_stdout(mpack_node_t node) {
+ mpack_node_print_to_file(node, stdout);
+}
+
+/*
+ * Deprecated.
+ *
+ * \deprecated Renamed to mpack_node_print_to_stdout().
+ */
+MPACK_INLINE void mpack_node_print(mpack_node_t node) {
+ mpack_node_print_to_stdout(node);
+}
+#endif
+
+/** @endcond */
+
+/**
+ * @}
+ */
+
+/**
+ * @name Node Primitive Value Functions
+ * @{
+ */
+
+/**
+ * Returns the type of the node.
+ */
+mpack_type_t mpack_node_type(mpack_node_t node);
+
+/**
+ * Returns true if the given node is a nil node; false otherwise.
+ *
+ * To ensure that a node is nil and flag an error otherwise, use
+ * mpack_node_nil().
+ */
+bool mpack_node_is_nil(mpack_node_t node);
+
+/**
+ * Returns true if the given node handle indicates a missing node; false otherwise.
+ *
+ * To ensure that a node is missing and flag an error otherwise, use
+ * mpack_node_missing().
+ */
+bool mpack_node_is_missing(mpack_node_t node);
+
+/**
+ * Checks that the given node is of nil type, raising @ref mpack_error_type
+ * otherwise.
+ *
+ * Use mpack_node_is_nil() to return whether the node is nil.
+ */
+void mpack_node_nil(mpack_node_t node);
+
+/**
+ * Checks that the given node indicates a missing node, raising @ref
+ * mpack_error_type otherwise.
+ *
+ * Use mpack_node_is_missing() to return whether the node is missing.
+ */
+void mpack_node_missing(mpack_node_t node);
+
+/**
+ * Returns the bool value of the node. If this node is not of the correct
+ * type, false is returned and mpack_error_type is raised.
+ */
+bool mpack_node_bool(mpack_node_t node);
+
+/**
+ * Checks if the given node is of bool type with value true, raising
+ * mpack_error_type otherwise.
+ */
+void mpack_node_true(mpack_node_t node);
+
+/**
+ * Checks if the given node is of bool type with value false, raising
+ * mpack_error_type otherwise.
+ */
+void mpack_node_false(mpack_node_t node);
+
+/**
+ * Returns the 8-bit unsigned value of the node. If this node is not
+ * of a compatible type, @ref mpack_error_type is raised and zero is returned.
+ */
+uint8_t mpack_node_u8(mpack_node_t node);
+
+/**
+ * Returns the 8-bit signed value of the node. If this node is not
+ * of a compatible type, @ref mpack_error_type is raised and zero is returned.
+ */
+int8_t mpack_node_i8(mpack_node_t node);
+
+/**
+ * Returns the 16-bit unsigned value of the node. If this node is not
+ * of a compatible type, @ref mpack_error_type is raised and zero is returned.
+ */
+uint16_t mpack_node_u16(mpack_node_t node);
+
+/**
+ * Returns the 16-bit signed value of the node. If this node is not
+ * of a compatible type, @ref mpack_error_type is raised and zero is returned.
+ */
+int16_t mpack_node_i16(mpack_node_t node);
+
+/**
+ * Returns the 32-bit unsigned value of the node. If this node is not
+ * of a compatible type, @ref mpack_error_type is raised and zero is returned.
+ */
+uint32_t mpack_node_u32(mpack_node_t node);
+
+/**
+ * Returns the 32-bit signed value of the node. If this node is not
+ * of a compatible type, @ref mpack_error_type is raised and zero is returned.
+ */
+int32_t mpack_node_i32(mpack_node_t node);
+
+/**
+ * Returns the 64-bit unsigned value of the node. If this node is not
+ * of a compatible type, @ref mpack_error_type is raised, and zero is returned.
+ */
+uint64_t mpack_node_u64(mpack_node_t node);
+
+/**
+ * Returns the 64-bit signed value of the node. If this node is not
+ * of a compatible type, @ref mpack_error_type is raised and zero is returned.
+ */
+int64_t mpack_node_i64(mpack_node_t node);
+
+/**
+ * Returns the unsigned int value of the node.
+ *
+ * Returns zero if an error occurs.
+ *
+ * @throws mpack_error_type If the node is not an integer type or does not fit in the range of an unsigned int
+ */
+unsigned int mpack_node_uint(mpack_node_t node);
+
+/**
+ * Returns the int value of the node.
+ *
+ * Returns zero if an error occurs.
+ *
+ * @throws mpack_error_type If the node is not an integer type or does not fit in the range of an int
+ */
+int mpack_node_int(mpack_node_t node);
+
+#if MPACK_FLOAT
+/**
+ * Returns the float value of the node. The underlying value can be an
+ * integer, float or double; the value is converted to a float.
+ *
+ * @note Reading a double or a large integer with this function can incur a
+ * loss of precision.
+ *
+ * @throws mpack_error_type if the underlying value is not a float, double or integer.
+ */
+float mpack_node_float(mpack_node_t node);
+#endif
+
+#if MPACK_DOUBLE
+/**
+ * Returns the double value of the node. The underlying value can be an
+ * integer, float or double; the value is converted to a double.
+ *
+ * @note Reading a very large integer with this function can incur a
+ * loss of precision.
+ *
+ * @throws mpack_error_type if the underlying value is not a float, double or integer.
+ */
+double mpack_node_double(mpack_node_t node);
+#endif
+
+#if MPACK_FLOAT
+/**
+ * Returns the float value of the node. The underlying value must be a float,
+ * not a double or an integer. This ensures no loss of precision can occur.
+ *
+ * @throws mpack_error_type if the underlying value is not a float.
+ */
+float mpack_node_float_strict(mpack_node_t node);
+#endif
+
+#if MPACK_DOUBLE
+/**
+ * Returns the double value of the node. The underlying value must be a float
+ * or double, not an integer. This ensures no loss of precision can occur.
+ *
+ * @throws mpack_error_type if the underlying value is not a float or double.
+ */
+double mpack_node_double_strict(mpack_node_t node);
+#endif
+
+#if !MPACK_FLOAT
+/**
+ * Returns the float value of the node as a raw uint32_t. The underlying value
+ * must be a float, not a double or an integer.
+ *
+ * @throws mpack_error_type if the underlying value is not a float.
+ */
+uint32_t mpack_node_raw_float(mpack_node_t node);
+#endif
+
+#if !MPACK_DOUBLE
+/**
+ * Returns the double value of the node as a raw uint64_t. The underlying value
+ * must be a double, not a float or an integer.
+ *
+ * @throws mpack_error_type if the underlying value is not a float or double.
+ */
+uint64_t mpack_node_raw_double(mpack_node_t node);
+#endif
+
+
+#if MPACK_EXTENSIONS
+/**
+ * Returns a timestamp.
+ *
+ * @note This requires @ref MPACK_EXTENSIONS.
+ *
+ * @throws mpack_error_type if the underlying value is not a timestamp.
+ */
+mpack_timestamp_t mpack_node_timestamp(mpack_node_t node);
+
+/**
+ * Returns a timestamp's (signed) seconds since 1970-01-01T00:00:00Z.
+ *
+ * @note This requires @ref MPACK_EXTENSIONS.
+ *
+ * @throws mpack_error_type if the underlying value is not a timestamp.
+ */
+int64_t mpack_node_timestamp_seconds(mpack_node_t node);
+
+/**
+ * Returns a timestamp's additional nanoseconds.
+ *
+ * @note This requires @ref MPACK_EXTENSIONS.
+ *
+ * @return A nanosecond count between 0 and 999,999,999 inclusive.
+ * @throws mpack_error_type if the underlying value is not a timestamp.
+ */
+uint32_t mpack_node_timestamp_nanoseconds(mpack_node_t node);
+#endif
+
+/**
+ * @}
+ */
+
+/**
+ * @name Node String and Data Functions
+ * @{
+ */
+
+/**
+ * Checks that the given node contains a valid UTF-8 string.
+ *
+ * If the string is invalid, this flags an error, which would cause subsequent calls
+ * to mpack_node_str() to return NULL and mpack_node_strlen() to return zero. So you
+ * can check the node for error immediately after calling this, or you can call those
+ * functions to use the data anyway and check for errors later.
+ *
+ * @throws mpack_error_type If this node is not a string or does not contain valid UTF-8.
+ *
+ * @param node The string node to test
+ *
+ * @see mpack_node_str()
+ * @see mpack_node_strlen()
+ */
+void mpack_node_check_utf8(mpack_node_t node);
+
+/**
+ * Checks that the given node contains a valid UTF-8 string with no NUL bytes.
+ *
+ * This does not check that the string has a null-terminator! It only checks whether
+ * the string could safely be represented as a C-string by appending a null-terminator.
+ * (If the string does already contain a null-terminator, this will flag an error.)
+ *
+ * This is performed automatically by other UTF-8 cstr helper functions. Only
+ * call this if you will do something else with the data directly, but you still
+ * want to ensure it will be valid as a UTF-8 C-string.
+ *
+ * @throws mpack_error_type If this node is not a string, does not contain valid UTF-8,
+ * or contains a NUL byte.
+ *
+ * @param node The string node to test
+ *
+ * @see mpack_node_str()
+ * @see mpack_node_strlen()
+ * @see mpack_node_copy_utf8_cstr()
+ * @see mpack_node_utf8_cstr_alloc()
+ */
+void mpack_node_check_utf8_cstr(mpack_node_t node);
+
+#if MPACK_EXTENSIONS
+/**
+ * Returns the extension type of the given ext node.
+ *
+ * This returns zero if the tree is in an error state.
+ *
+ * @note This requires @ref MPACK_EXTENSIONS.
+ */
+int8_t mpack_node_exttype(mpack_node_t node);
+#endif
+
+/**
+ * Returns the number of bytes in the given bin node.
+ *
+ * This returns zero if the tree is in an error state.
+ *
+ * If this node is not a bin, @ref mpack_error_type is raised and zero is returned.
+ */
+size_t mpack_node_bin_size(mpack_node_t node);
+
+/**
+ * Returns the length of the given str, bin or ext node.
+ *
+ * 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
+ * is returned.
+ */
+uint32_t mpack_node_data_len(mpack_node_t node);
+
+/**
+ * Returns the length in bytes of the given string node. This does not
+ * include any null-terminator.
+ *
+ * This returns zero if the tree is in an error state.
+ *
+ * If this node is not a str, @ref mpack_error_type is raised and zero is returned.
+ */
+size_t mpack_node_strlen(mpack_node_t node);
+
+/**
+ * Returns a pointer to the data contained by this node, ensuring the node is a
+ * string.
+ *
+ * @warning Strings are not null-terminated! Use one of the cstr functions
+ * to get a null-terminated string.
+ *
+ * The pointer is valid as long as the data backing the tree is valid.
+ *
+ * If this node is not a string, @ref mpack_error_type is raised and @c NULL is returned.
+ *
+ * @see mpack_node_copy_cstr()
+ * @see mpack_node_cstr_alloc()
+ * @see mpack_node_utf8_cstr_alloc()
+ */
+const char* mpack_node_str(mpack_node_t node);
+
+/**
+ * Returns a pointer to the data contained by this node.
+ *
+ * @note Strings are not null-terminated! Use one of the cstr functions
+ * to get a null-terminated string.
+ *
+ * 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
+ * @c NULL is returned.
+ *
+ * @see mpack_node_copy_cstr()
+ * @see mpack_node_cstr_alloc()
+ * @see mpack_node_utf8_cstr_alloc()
+ */
+const char* mpack_node_data(mpack_node_t node);
+
+/**
+ * Returns a pointer to the data contained by this bin node.
+ *
+ * The pointer is valid as long as the data backing the tree is valid.
+ *
+ * If this node is not a bin, @ref mpack_error_type is raised and @c NULL is
+ * returned.
+ */
+const char* mpack_node_bin_data(mpack_node_t node);
+
+/**
+ * Copies the bytes contained by this node into the given buffer, returning the
+ * number of bytes in the node.
+ *
+ * @throws mpack_error_type If this node is not a str, bin or ext type
+ * @throws mpack_error_too_big If the string does not fit in the given buffer
+ *
+ * @param node The string node from which to copy data
+ * @param buffer A buffer in which to copy the node's bytes
+ * @param bufsize The size of the given buffer
+ *
+ * @return The number of bytes in the node, or zero if an error occurs.
+ */
+size_t mpack_node_copy_data(mpack_node_t node, char* buffer, size_t bufsize);
+
+/**
+ * Checks that the given node contains a valid UTF-8 string and copies the
+ * string into the given buffer, returning the number of bytes in the string.
+ *
+ * @throws mpack_error_type If this node is not a string
+ * @throws mpack_error_too_big If the string does not fit in the given buffer
+ *
+ * @param node The string node from which to copy data
+ * @param buffer A buffer in which to copy the node's bytes
+ * @param bufsize The size of the given buffer
+ *
+ * @return The number of bytes in the node, or zero if an error occurs.
+ */
+size_t mpack_node_copy_utf8(mpack_node_t node, char* buffer, size_t bufsize);
+
+/**
+ * Checks that the given node contains a string with no NUL bytes, copies the string
+ * into the given buffer, and adds a null terminator.
+ *
+ * If this node is not of a string type, @ref mpack_error_type is raised. If the string
+ * does not fit, @ref mpack_error_data is raised.
+ *
+ * If any error occurs, the buffer will contain an empty null-terminated string.
+ *
+ * @param node The string node from which to copy data
+ * @param buffer A buffer in which to copy the node's string
+ * @param size The size of the given buffer
+ */
+void mpack_node_copy_cstr(mpack_node_t node, char* buffer, size_t size);
+
+/**
+ * Checks that the given node contains a valid UTF-8 string with no NUL bytes,
+ * copies the string into the given buffer, and adds a null terminator.
+ *
+ * If this node is not of a string type, @ref mpack_error_type is raised. If the string
+ * does not fit, @ref mpack_error_data is raised.
+ *
+ * If any error occurs, the buffer will contain an empty null-terminated string.
+ *
+ * @param node The string node from which to copy data
+ * @param buffer A buffer in which to copy the node's string
+ * @param size The size of the given buffer
+ */
+void mpack_node_copy_utf8_cstr(mpack_node_t node, char* buffer, size_t size);
+
+#ifdef MPACK_MALLOC
+/**
+ * Allocates a new chunk of data using MPACK_MALLOC() with the bytes
+ * contained by this node.
+ *
+ * The allocated data must be freed with MPACK_FREE() (or simply free()
+ * if MPack's allocator hasn't been customized.)
+ *
+ * @throws mpack_error_type If this node is not a str, bin or ext type
+ * @throws mpack_error_too_big If the size of the data is larger than the
+ * given maximum size
+ * @throws mpack_error_memory If an allocation failure occurs
+ *
+ * @param node The node from which to allocate and copy data
+ * @param maxsize The maximum size to allocate
+ *
+ * @return The allocated data, or NULL if any error occurs.
+ */
+char* mpack_node_data_alloc(mpack_node_t node, size_t maxsize);
+
+/**
+ * Allocates a new null-terminated string using MPACK_MALLOC() with the string
+ * contained by this node.
+ *
+ * The allocated string must be freed with MPACK_FREE() (or simply free()
+ * if MPack's allocator hasn't been customized.)
+ *
+ * @throws mpack_error_type If this node is not a string or contains NUL bytes
+ * @throws mpack_error_too_big If the size of the string plus null-terminator
+ * is larger than the given maximum size
+ * @throws mpack_error_memory If an allocation failure occurs
+ *
+ * @param node The node from which to allocate and copy string data
+ * @param maxsize The maximum size to allocate, including the null-terminator
+ *
+ * @return The allocated string, or NULL if any error occurs.
+ */
+char* mpack_node_cstr_alloc(mpack_node_t node, size_t maxsize);
+
+/**
+ * Allocates a new null-terminated string using MPACK_MALLOC() with the UTF-8
+ * string contained by this node.
+ *
+ * The allocated string must be freed with MPACK_FREE() (or simply free()
+ * if MPack's allocator hasn't been customized.)
+ *
+ * @throws mpack_error_type If this node is not a string, is not valid UTF-8,
+ * or contains NUL bytes
+ * @throws mpack_error_too_big If the size of the string plus null-terminator
+ * is larger than the given maximum size
+ * @throws mpack_error_memory If an allocation failure occurs
+ *
+ * @param node The node from which to allocate and copy string data
+ * @param maxsize The maximum size to allocate, including the null-terminator
+ *
+ * @return The allocated string, or NULL if any error occurs.
+ */
+char* mpack_node_utf8_cstr_alloc(mpack_node_t node, size_t maxsize);
+#endif
+
+/**
+ * Searches the given string array for a string matching the given
+ * node and returns its index.
+ *
+ * If the node does not match any of the given strings,
+ * @ref mpack_error_type is flagged. Use mpack_node_enum_optional()
+ * if you want to allow values other than the given strings.
+ *
+ * If any error occurs or if the tree is in an error state, @a count
+ * is returned.
+ *
+ * This can be used to quickly parse a string into an enum when the
+ * enum values range from 0 to @a count-1. If the last value in the
+ * enum is a special "count" value, it can be passed as the count,
+ * and the return value can be cast directly to the enum type.
+ *
+ * @code{.c}
+ * typedef enum { APPLE , BANANA , ORANGE , COUNT} fruit_t;
+ * const char* fruits[] = {"apple", "banana", "orange"};
+ *
+ * fruit_t fruit = (fruit_t)mpack_node_enum(node, fruits, COUNT);
+ * @endcode
+ *
+ * @param node The node
+ * @param strings An array of expected strings of length count
+ * @param count The number of strings
+ * @return The index of the matched string, or @a count in case of error
+ */
+size_t mpack_node_enum(mpack_node_t node, const char* strings[], size_t count);
+
+/**
+ * Searches the given string array for a string matching the given node,
+ * returning its index or @a count if no strings match.
+ *
+ * If the value is not a string, or it does not match any of the
+ * given strings, @a count is returned and no error is flagged.
+ *
+ * If any error occurs or if the tree is in an error state, @a count
+ * is returned.
+ *
+ * This can be used to quickly parse a string into an enum when the
+ * enum values range from 0 to @a count-1. If the last value in the
+ * enum is a special "count" value, it can be passed as the count,
+ * and the return value can be cast directly to the enum type.
+ *
+ * @code{.c}
+ * typedef enum { APPLE , BANANA , ORANGE , COUNT} fruit_t;
+ * const char* fruits[] = {"apple", "banana", "orange"};
+ *
+ * fruit_t fruit = (fruit_t)mpack_node_enum_optional(node, fruits, COUNT);
+ * @endcode
+ *
+ * @param node The node
+ * @param strings An array of expected strings of length count
+ * @param count The number of strings
+ * @return The index of the matched string, or @a count in case of error
+ */
+size_t mpack_node_enum_optional(mpack_node_t node, const char* strings[], size_t count);
+
+/**
+ * @}
+ */
+
+/**
+ * @name Compound Node Functions
+ * @{
+ */
+
+/**
+ * Returns the length of the given array node. Raises mpack_error_type
+ * and returns 0 if the given node is not an array.
+ */
+size_t mpack_node_array_length(mpack_node_t node);
+
+/**
+ * Returns the node in the given array at the given index. If the node
+ * is not an array, @ref mpack_error_type is raised and a nil node is returned.
+ * If the given index is out of bounds, @ref mpack_error_data is raised and
+ * a nil node is returned.
+ */
+mpack_node_t mpack_node_array_at(mpack_node_t node, size_t index);
+
+/**
+ * Returns the number of key/value pairs in the given map node. Raises
+ * mpack_error_type and returns 0 if the given node is not a map.
+ */
+size_t mpack_node_map_count(mpack_node_t node);
+
+/**
+ * Returns the key node in the given map at the given index.
+ *
+ * A nil node is returned in case of error.
+ *
+ * @throws mpack_error_type if the node is not a map
+ * @throws mpack_error_data if the given index is out of bounds
+ */
+mpack_node_t mpack_node_map_key_at(mpack_node_t node, size_t index);
+
+/**
+ * Returns the value node in the given map at the given index.
+ *
+ * A nil node is returned in case of error.
+ *
+ * @throws mpack_error_type if the node is not a map
+ * @throws mpack_error_data if the given index is out of bounds
+ */
+mpack_node_t mpack_node_map_value_at(mpack_node_t node, size_t index);
+
+/**
+ * Returns the value node in the given map for the given integer key.
+ *
+ * The key must exist within the map. Use mpack_node_map_int_optional() to
+ * check for optional keys.
+ *
+ * The key must be unique. An error is flagged if the node has multiple
+ * entries with the given key.
+ *
+ * @throws mpack_error_type If the node is not a map
+ * @throws mpack_error_data If the node does not contain exactly one entry with the given key
+ *
+ * @return The value node for the given key, or a nil node in case of error
+ */
+mpack_node_t mpack_node_map_int(mpack_node_t node, int64_t num);
+
+/**
+ * Returns the value node in the given map for the given integer key, or a
+ * missing node if the map does not contain the given key.
+ *
+ * The key must be unique. An error is flagged if the node has multiple
+ * entries with the given key.
+ *
+ * @throws mpack_error_type If the node is not a map
+ * @throws mpack_error_data If the node contains more than one entry with the given key
+ *
+ * @return The value node for the given key, or a missing node if the key does
+ * not exist, or a nil node in case of error
+ *
+ * @see mpack_node_is_missing()
+ */
+mpack_node_t mpack_node_map_int_optional(mpack_node_t node, int64_t num);
+
+/**
+ * Returns the value node in the given map for the given unsigned integer key.
+ *
+ * The key must exist within the map. Use mpack_node_map_uint_optional() to
+ * check for optional keys.
+ *
+ * The key must be unique. An error is flagged if the node has multiple
+ * entries with the given key.
+ *
+ * @throws mpack_error_type If the node is not a map
+ * @throws mpack_error_data If the node does not contain exactly one entry with the given key
+ *
+ * @return The value node for the given key, or a nil node in case of error
+ */
+mpack_node_t mpack_node_map_uint(mpack_node_t node, uint64_t num);
+
+/**
+ * Returns the value node in the given map for the given unsigned integer
+ * key, or a missing node if the map does not contain the given key.
+ *
+ * The key must be unique. An error is flagged if the node has multiple
+ * entries with the given key.
+ *
+ * @throws mpack_error_type If the node is not a map
+ * @throws mpack_error_data If the node contains more than one entry with the given key
+ *
+ * @return The value node for the given key, or a missing node if the key does
+ * not exist, or a nil node in case of error
+ *
+ * @see mpack_node_is_missing()
+ */
+mpack_node_t mpack_node_map_uint_optional(mpack_node_t node, uint64_t num);
+
+/**
+ * Returns the value node in the given map for the given string key.
+ *
+ * The key must exist within the map. Use mpack_node_map_str_optional() to
+ * check for optional keys.
+ *
+ * The key must be unique. An error is flagged if the node has multiple
+ * entries with the given key.
+ *
+ * @throws mpack_error_type If the node is not a map
+ * @throws mpack_error_data If the node does not contain exactly one entry with the given key
+ *
+ * @return The value node for the given key, or a nil node in case of error
+ */
+mpack_node_t mpack_node_map_str(mpack_node_t node, const char* str, size_t length);
+
+/**
+ * Returns the value node in the given map for the given string key, or a missing
+ * node if the map does not contain the given key.
+ *
+ * The key must be unique. An error is flagged if the node has multiple
+ * entries with the given key.
+ *
+ * @throws mpack_error_type If the node is not a map
+ * @throws mpack_error_data If the node contains more than one entry with the given key
+ *
+ * @return The value node for the given key, or a missing node if the key does
+ * not exist, or a nil node in case of error
+ *
+ * @see mpack_node_is_missing()
+ */
+mpack_node_t mpack_node_map_str_optional(mpack_node_t node, const char* str, size_t length);
+
+/**
+ * Returns the value node in the given map for the given null-terminated
+ * string key.
+ *
+ * The key must exist within the map. Use mpack_node_map_cstr_optional() to
+ * check for optional keys.
+ *
+ * The key must be unique. An error is flagged if the node has multiple
+ * entries with the given key.
+ *
+ * @throws mpack_error_type If the node is not a map
+ * @throws mpack_error_data If the node does not contain exactly one entry with the given key
+ *
+ * @return The value node for the given key, or a nil node in case of error
+ */
+mpack_node_t mpack_node_map_cstr(mpack_node_t node, const char* cstr);
+
+/**
+ * Returns the value node in the given map for the given null-terminated
+ * string key, or a missing node if the map does not contain the given key.
+ *
+ * The key must be unique. An error is flagged if the node has multiple
+ * entries with the given key.
+ *
+ * @throws mpack_error_type If the node is not a map
+ * @throws mpack_error_data If the node contains more than one entry with the given key
+ *
+ * @return The value node for the given key, or a missing node if the key does
+ * not exist, or a nil node in case of error
+ *
+ * @see mpack_node_is_missing()
+ */
+mpack_node_t mpack_node_map_cstr_optional(mpack_node_t node, const char* cstr);
+
+/**
+ * Returns true if the given node map contains exactly one entry with the
+ * given integer key.
+ *
+ * The key must be unique. An error is flagged if the node has multiple
+ * entries with the given key.
+ *
+ * @throws mpack_error_type If the node is not a map
+ * @throws mpack_error_data If the node contains more than one entry with the given key
+ */
+bool mpack_node_map_contains_int(mpack_node_t node, int64_t num);
+
+/**
+ * Returns true if the given node map contains exactly one entry with the
+ * given unsigned integer key.
+ *
+ * The key must be unique. An error is flagged if the node has multiple
+ * entries with the given key.
+ *
+ * @throws mpack_error_type If the node is not a map
+ * @throws mpack_error_data If the node contains more than one entry with the given key
+ */
+bool mpack_node_map_contains_uint(mpack_node_t node, uint64_t num);
+
+/**
+ * Returns true if the given node map contains exactly one entry with the
+ * given string key.
+ *
+ * The key must be unique. An error is flagged if the node has multiple
+ * entries with the given key.
+ *
+ * @throws mpack_error_type If the node is not a map
+ * @throws mpack_error_data If the node contains more than one entry with the given key
+ */
+bool mpack_node_map_contains_str(mpack_node_t node, const char* str, size_t length);
+
+/**
+ * Returns true if the given node map contains exactly one entry with the
+ * given null-terminated string key.
+ *
+ * The key must be unique. An error is flagged if the node has multiple
+ * entries with the given key.
+ *
+ * @throws mpack_error_type If the node is not a map
+ * @throws mpack_error_data If the node contains more than one entry with the given key
+ */
+bool mpack_node_map_contains_cstr(mpack_node_t node, const char* cstr);
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+#endif
+
+MPACK_EXTERN_C_END
+MPACK_SILENCE_WARNINGS_END
+
+#endif
+
+
+#endif
+
diff --git a/wpiutil/src/main/native/include/wpi/mutex.h b/wpiutil/src/main/native/include/wpi/mutex.h
index 0ce3a51..c29509a 100644
--- a/wpiutil/src/main/native/include/wpi/mutex.h
+++ b/wpiutil/src/main/native/include/wpi/mutex.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* 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. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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
diff --git a/wpiutil/src/main/native/include/wpi/nodiscard.h b/wpiutil/src/main/native/include/wpi/nodiscard.h
new file mode 100644
index 0000000..d924036
--- /dev/null
+++ b/wpiutil/src/main/native/include/wpi/nodiscard.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.
+
+#ifndef WPIUTIL_WPI_NODISCARD_H_
+#define WPIUTIL_WPI_NODISCARD_H_
+
+// This should only be used on APIs that can be compiled as C or C++. If the API
+// is C++ only, use [[nodiscard]] instead.
+#ifndef WPI_NODISCARD
+#ifdef __cplusplus
+#define WPI_NODISCARD [[nodiscard]]
+#elif defined(__GNUC__) || defined(__llvm__)
+#define WPI_NODISCARD __attribute__((warn_unused_result))
+#elif _MSC_VER
+#define WPI_NODISCARD _Check_return_
+#endif
+
+#endif
+
+#endif // WPIUTIL_WPI_NODISCARD_H_
diff --git a/wpiutil/src/main/native/include/wpi/numbers b/wpiutil/src/main/native/include/wpi/numbers
new file mode 100644
index 0000000..d45aee0
--- /dev/null
+++ b/wpiutil/src/main/native/include/wpi/numbers
@@ -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.
+
+#pragma once
+
+#include <type_traits>
+
+namespace wpi::numbers {
+
+template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
+inline constexpr T e_v = 2.718281828459045235360287471352662498L;
+
+template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
+inline constexpr T log2e_v = 1.442695040888963407359924681001892137L;
+
+template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
+inline constexpr T log10e_v = 0.434294481903251827651128918916605082L;
+
+template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
+inline constexpr T pi_v = 3.141592653589793238462643383279502884L;
+
+template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
+inline constexpr T inv_pi_v = 0.318309886183790671537767526745028724L;
+
+template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
+inline constexpr T inv_sqrtpi_v = 0.564189583547756286948079451560772586L;
+
+template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
+inline constexpr T ln2_v = 0.693147180559945309417232121458176568L;
+
+template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
+inline constexpr T ln10_v = 2.302585092994045684017991454684364208L;
+
+template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
+inline constexpr T sqrt2_v = 1.414213562373095048801688724209698078L;
+
+template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
+inline constexpr T sqrt3_v = 1.732050807568877293527446341505872366L;
+
+template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
+inline constexpr T inv_sqrt3_v = 0.577350269189625764509148780501957456L;
+
+template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
+inline constexpr T egamma_v = 0.577215664901532860606512090082402431L;
+
+template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
+inline constexpr T phi_v = 1.618033988749894848204586834365638117L;
+
+inline constexpr double e = e_v<double>;
+inline constexpr double log2e = log2e_v<double>;
+inline constexpr double log10e = log10e_v<double>;
+inline constexpr double pi = pi_v<double>;
+inline constexpr double inv_pi = inv_pi_v<double>;
+inline constexpr double inv_sqrtpi = inv_sqrtpi_v<double>;
+inline constexpr double ln2 = ln2_v<double>;
+inline constexpr double ln10 = ln10_v<double>;
+inline constexpr double sqrt2 = sqrt2_v<double>;
+inline constexpr double sqrt3 = sqrt3_v<double>;
+inline constexpr double inv_sqrt3 = inv_sqrt3_v<double>;
+inline constexpr double egamma = egamma_v<double>;
+inline constexpr double phi = phi_v<double>;
+
+} // namespace wpi::numbers
diff --git a/wpiutil/src/main/native/include/wpi/optional.h b/wpiutil/src/main/native/include/wpi/optional.h
deleted file mode 100644
index a92f22c..0000000
--- a/wpiutil/src/main/native/include/wpi/optional.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2019 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
-
-#ifndef WPIUTIL_WPI_OPTIONAL_H_
-#define WPIUTIL_WPI_OPTIONAL_H_
-
-// clang-format off
-
-#ifdef _MSC_VER
-#pragma message("warning: wpi/optional.h is deprecated; include <optional> instead")
-#else
-#warning "wpi/optional.h is deprecated; include <optional> instead"
-#endif
-
-#include <optional>
-
-namespace wpi {
-
-template <typename T>
-using optional [[deprecated("use std::optional")]] = std::optional<T>;
-
-using nullopt_t [[deprecated("use std::nullopt_t")]] = std::nullopt_t;
-
-[[deprecated("use std::nullopt")]] inline constexpr std::nullopt_t nullopt =
- std::nullopt;
-
-} // namespace wpi
-
-// clang-format on
-
-#endif // WPIUTIL_WPI_OPTIONAL_H_
diff --git a/wpiutil/src/main/native/include/wpi/priority_mutex.h b/wpiutil/src/main/native/include/wpi/priority_mutex.h
index bb16289..73389ae 100644
--- a/wpiutil/src/main/native/include/wpi/priority_mutex.h
+++ b/wpiutil/src/main/native/include/wpi/priority_mutex.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-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. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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
diff --git a/wpiutil/src/main/native/include/wpi/priority_queue.h b/wpiutil/src/main/native/include/wpi/priority_queue.h
index b194090..4610aba 100644
--- a/wpiutil/src/main/native/include/wpi/priority_queue.h
+++ b/wpiutil/src/main/native/include/wpi/priority_queue.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2020 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. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_PRIORITY_QUEUE_H_
#define WPIUTIL_WPI_PRIORITY_QUEUE_H_
diff --git a/wpiutil/src/main/native/include/wpi/raw_istream.h b/wpiutil/src/main/native/include/wpi/raw_istream.h
index 7fbbe0b..2371fc6 100644
--- a/wpiutil/src/main/native/include/wpi/raw_istream.h
+++ b/wpiutil/src/main/native/include/wpi/raw_istream.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2015-2019 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_RAW_ISTREAM_H_
#define WPIUTIL_WPI_RAW_ISTREAM_H_
@@ -13,13 +10,12 @@
#include <algorithm>
#include <cstddef>
#include <string>
+#include <string_view>
#include <system_error>
#include <vector>
-#include "wpi/ArrayRef.h"
#include "wpi/SmallVector.h"
-#include "wpi/StringRef.h"
-#include "wpi/Twine.h"
+#include "wpi/span.h"
namespace wpi {
@@ -50,7 +46,9 @@
size_t readsome(void* data, size_t len) {
size_t readlen = (std::min)(in_avail(), len);
- if (readlen == 0) return 0;
+ if (readlen == 0) {
+ return 0;
+ }
read_impl(data, readlen);
return m_read_count;
}
@@ -101,7 +99,7 @@
// @param buf Buffer for output
// @param maxLen Maximum length
// @return Line
- StringRef getline(SmallVectorImpl<char>& buf, int maxLen);
+ std::string_view getline(SmallVectorImpl<char>& buf, int maxLen);
virtual void close() = 0;
@@ -135,9 +133,9 @@
// not const as we don't want to allow temporaries
explicit raw_mem_istream(std::string& str)
: raw_mem_istream(str.data(), str.size()) {}
- explicit raw_mem_istream(ArrayRef<char> mem)
+ explicit raw_mem_istream(span<const char> mem)
: raw_mem_istream(mem.data(), mem.size()) {}
- explicit raw_mem_istream(ArrayRef<uint8_t> mem)
+ explicit raw_mem_istream(span<const uint8_t> mem)
: raw_mem_istream(reinterpret_cast<const char*>(mem.data()), mem.size()) {
}
explicit raw_mem_istream(const char* str)
@@ -155,11 +153,11 @@
class raw_fd_istream : public raw_istream {
public:
- raw_fd_istream(const Twine& filename, std::error_code& ec,
+ raw_fd_istream(std::string_view filename, std::error_code& ec,
size_t bufSize = 4096);
raw_fd_istream(int fd, bool shouldClose, size_t bufSize = 4096);
~raw_fd_istream() override;
- void close() override;
+ void close() final;
size_t in_avail() const override;
private:
diff --git a/wpiutil/src/main/native/include/wpi/raw_ostream.h b/wpiutil/src/main/native/include/wpi/raw_ostream.h
index bb577fe..84ec925 100644
--- a/wpiutil/src/main/native/include/wpi/raw_ostream.h
+++ b/wpiutil/src/main/native/include/wpi/raw_ostream.h
@@ -14,31 +14,24 @@
#ifndef WPIUTIL_WPI_RAW_OSTREAM_H
#define WPIUTIL_WPI_RAW_OSTREAM_H
-#include "wpi/ArrayRef.h"
#include "wpi/SmallVector.h"
-#include "wpi/StringRef.h"
+#include "wpi/span.h"
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <string>
+#include <string_view>
#include <vector>
#include <system_error>
-namespace wpi {
-
-class format_object_base;
-class FormattedString;
-class FormattedNumber;
-class FormattedBytes;
-
-namespace sys {
namespace fs {
enum FileAccess : unsigned;
enum OpenFlags : unsigned;
enum CreationDisposition : unsigned;
} // end namespace fs
-} // end namespace sys
+
+namespace wpi {
/// This class implements an extremely fast bulk output stream that can *only*
/// output to a stream. It does not support seeking, reopening, rewinding, line
@@ -166,7 +159,7 @@
return *this;
}
- raw_ostream &operator<<(ArrayRef<uint8_t> Arr) {
+ raw_ostream &operator<<(span<const uint8_t> Arr) {
// Inline fast path, particularly for arrays with a known length.
size_t Size = Arr.size();
@@ -181,7 +174,7 @@
return *this;
}
- raw_ostream &operator<<(StringRef Str) {
+ raw_ostream &operator<<(std::string_view Str) {
// Inline fast path, particularly for strings with a known length.
size_t Size = Str.size();
@@ -200,7 +193,7 @@
// 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) {
@@ -221,28 +214,9 @@
return write(Arr.data(), Arr.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);
-
/// Output \p Str, turning '\\', '\t', '\n', '"', and anything that doesn't
/// satisfy wpi::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);
@@ -250,18 +224,6 @@
return write(reinterpret_cast<const char *>(Ptr), 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 format_bytes() function in Support/Format.h.
- raw_ostream &operator<<(const FormattedBytes &);
-
/// indent - Insert 'NumSpaces' spaces.
raw_ostream &indent(unsigned NumSpaces);
@@ -429,16 +391,16 @@
/// 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,
- sys::fs::CreationDisposition Disp);
- raw_fd_ostream(StringRef Filename, std::error_code &EC,
- sys::fs::FileAccess Access);
- raw_fd_ostream(StringRef Filename, std::error_code &EC,
- sys::fs::OpenFlags Flags);
- raw_fd_ostream(StringRef Filename, std::error_code &EC,
- sys::fs::CreationDisposition Disp, sys::fs::FileAccess Access,
- sys::fs::OpenFlags Flags);
+ raw_fd_ostream(std::string_view Filename, std::error_code &EC);
+ raw_fd_ostream(std::string_view Filename, std::error_code &EC,
+ fs::CreationDisposition Disp);
+ raw_fd_ostream(std::string_view Filename, std::error_code &EC,
+ fs::FileAccess Access);
+ raw_fd_ostream(std::string_view Filename, std::error_code &EC,
+ fs::OpenFlags Flags);
+ raw_fd_ostream(std::string_view Filename, std::error_code &EC,
+ fs::CreationDisposition Disp, fs::FileAccess Access,
+ fs::OpenFlags Flags);
/// FD is the file descriptor that this writes to. If ShouldClose is true,
/// this closes the file when the stream is destroyed. If FD is for stdout or
@@ -545,8 +507,8 @@
void flush() = delete;
- /// Return a StringRef for the vector contents.
- StringRef str() { return StringRef(OS.data(), OS.size()); }
+ /// 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 a vector. This is a
@@ -578,8 +540,8 @@
void flush() = delete;
- /// Return a StringRef for the vector contents.
- StringRef str() { return StringRef(OS.data(), OS.size()); }
+ /// 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
@@ -611,8 +573,9 @@
void flush() = delete;
- /// Return an ArrayRef for the vector contents.
- ArrayRef<uint8_t> array() { return ArrayRef<uint8_t>(OS.data(), OS.size()); }
+ /// 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
@@ -644,8 +607,9 @@
void flush() = delete;
- /// Return a StringRef for the vector contents.
- ArrayRef<uint8_t> array() { return ArrayRef<uint8_t>(OS.data(), OS.size()); }
+ /// 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()}; }
};
diff --git a/wpiutil/src/main/native/include/wpi/raw_socket_istream.h b/wpiutil/src/main/native/include/wpi/raw_socket_istream.h
index 4ff8022..f6899e2 100644
--- a/wpiutil/src/main/native/include/wpi/raw_socket_istream.h
+++ b/wpiutil/src/main/native/include/wpi/raw_socket_istream.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2015-2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_RAW_SOCKET_ISTREAM_H_
#define WPIUTIL_WPI_RAW_SOCKET_ISTREAM_H_
diff --git a/wpiutil/src/main/native/include/wpi/raw_socket_ostream.h b/wpiutil/src/main/native/include/wpi/raw_socket_ostream.h
index 151af46..5cdeb41 100644
--- a/wpiutil/src/main/native/include/wpi/raw_socket_ostream.h
+++ b/wpiutil/src/main/native/include/wpi/raw_socket_ostream.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_RAW_SOCKET_OSTREAM_H_
#define WPIUTIL_WPI_RAW_SOCKET_OSTREAM_H_
@@ -18,7 +15,7 @@
public:
raw_socket_ostream(NetworkStream& stream, bool shouldClose)
: m_stream(stream), m_shouldClose(shouldClose) {}
- ~raw_socket_ostream();
+ ~raw_socket_ostream() override;
void close();
diff --git a/wpiutil/src/main/native/include/wpi/raw_uv_ostream.h b/wpiutil/src/main/native/include/wpi/raw_uv_ostream.h
index e35b4a8..4773c61 100644
--- a/wpiutil/src/main/native/include/wpi/raw_uv_ostream.h
+++ b/wpiutil/src/main/native/include/wpi/raw_uv_ostream.h
@@ -1,16 +1,16 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_RAW_UV_OSTREAM_H_
#define WPIUTIL_WPI_RAW_UV_OSTREAM_H_
-#include "wpi/ArrayRef.h"
+#include <functional>
+#include <utility>
+
#include "wpi/SmallVector.h"
#include "wpi/raw_ostream.h"
+#include "wpi/span.h"
#include "wpi/uv/Buffer.h"
namespace wpi {
@@ -29,8 +29,7 @@
* performed using Buffer::Allocate().
*/
raw_uv_ostream(SmallVectorImpl<uv::Buffer>& bufs, size_t allocSize)
- : m_bufs(bufs),
- m_alloc([=]() { return uv::Buffer::Allocate(allocSize); }) {
+ : m_bufs(bufs), m_alloc([=] { return uv::Buffer::Allocate(allocSize); }) {
SetUnbuffered();
}
@@ -41,19 +40,24 @@
*/
raw_uv_ostream(SmallVectorImpl<uv::Buffer>& bufs,
std::function<uv::Buffer()> alloc)
- : m_bufs(bufs), m_alloc(alloc) {
+ : m_bufs(bufs), m_alloc(std::move(alloc)) {
SetUnbuffered();
}
~raw_uv_ostream() override = default;
/**
- * Returns an ArrayRef to the buffers.
+ * Returns an span to the buffers.
*/
- ArrayRef<uv::Buffer> bufs() { return m_bufs; }
+ span<uv::Buffer> bufs() { return m_bufs; }
void flush() = delete;
+ /**
+ * Resets the amount of allocated space.
+ */
+ void reset() { m_left = 0; }
+
private:
void write_impl(const char* data, size_t len) override;
uint64_t current_pos() const override;
diff --git a/wpiutil/src/main/native/include/wpi/sendable/Sendable.h b/wpiutil/src/main/native/include/wpi/sendable/Sendable.h
new file mode 100644
index 0000000..d587c3c
--- /dev/null
+++ b/wpiutil/src/main/native/include/wpi/sendable/Sendable.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 "wpi/SymbolExports.h"
+
+namespace wpi {
+
+class SendableBuilder;
+
+/**
+ * Interface for Sendable objects.
+ */
+class WPILIB_DLLEXPORT Sendable {
+ public:
+ virtual ~Sendable() = default;
+
+ /**
+ * Initializes this Sendable object.
+ *
+ * @param builder sendable builder
+ */
+ virtual void InitSendable(SendableBuilder& builder) = 0;
+};
+
+} // namespace wpi
diff --git a/wpiutil/src/main/native/include/wpi/sendable/SendableBuilder.h b/wpiutil/src/main/native/include/wpi/sendable/SendableBuilder.h
new file mode 100644
index 0000000..e092a1e
--- /dev/null
+++ b/wpiutil/src/main/native/include/wpi/sendable/SendableBuilder.h
@@ -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.
+
+#pragma once
+
+#include <functional>
+#include <memory>
+#include <string>
+#include <string_view>
+#include <vector>
+
+#include "wpi/SmallVector.h"
+#include "wpi/span.h"
+
+namespace wpi {
+
+class SendableBuilder {
+ public:
+ /**
+ * The backend kinds used for the sendable builder.
+ */
+ enum BackendKind { kUnknown, kNetworkTables };
+
+ virtual ~SendableBuilder() = default;
+
+ /**
+ * Set the string representation of the named data type that will be used
+ * by the smart dashboard for this sendable.
+ *
+ * @param type data type
+ */
+ virtual void SetSmartDashboardType(std::string_view type) = 0;
+
+ /**
+ * Set a flag indicating if this sendable should be treated as an actuator.
+ * By default this flag is false.
+ *
+ * @param value true if actuator, false if not
+ */
+ virtual void SetActuator(bool value) = 0;
+
+ /**
+ * Set the function that should be called to set the Sendable into a safe
+ * state. This is called when entering and exiting Live Window mode.
+ *
+ * @param func function
+ */
+ virtual void SetSafeState(std::function<void()> func) = 0;
+
+ /**
+ * Add a boolean property.
+ *
+ * @param key property name
+ * @param getter getter function (returns current value)
+ * @param setter setter function (sets new value)
+ */
+ virtual void AddBooleanProperty(std::string_view key,
+ std::function<bool()> getter,
+ std::function<void(bool)> setter) = 0;
+
+ /**
+ * Add a double property.
+ *
+ * @param key property name
+ * @param getter getter function (returns current value)
+ * @param setter setter function (sets new value)
+ */
+ virtual void AddDoubleProperty(std::string_view key,
+ std::function<double()> getter,
+ std::function<void(double)> setter) = 0;
+
+ /**
+ * Add a string property.
+ *
+ * @param key property name
+ * @param getter getter function (returns current value)
+ * @param setter setter function (sets new value)
+ */
+ virtual void AddStringProperty(
+ std::string_view key, std::function<std::string()> getter,
+ std::function<void(std::string_view)> setter) = 0;
+
+ /**
+ * Add a boolean array property.
+ *
+ * @param key property name
+ * @param getter getter function (returns current value)
+ * @param setter setter function (sets new value)
+ */
+ virtual void AddBooleanArrayProperty(
+ std::string_view key, std::function<std::vector<int>()> getter,
+ std::function<void(wpi::span<const int>)> setter) = 0;
+
+ /**
+ * Add a double array property.
+ *
+ * @param key property name
+ * @param getter getter function (returns current value)
+ * @param setter setter function (sets new value)
+ */
+ virtual void AddDoubleArrayProperty(
+ std::string_view key, std::function<std::vector<double>()> getter,
+ std::function<void(wpi::span<const double>)> setter) = 0;
+
+ /**
+ * Add a string array property.
+ *
+ * @param key property name
+ * @param getter getter function (returns current value)
+ * @param setter setter function (sets new value)
+ */
+ virtual void AddStringArrayProperty(
+ std::string_view key, std::function<std::vector<std::string>()> getter,
+ std::function<void(wpi::span<const std::string>)> setter) = 0;
+
+ /**
+ * Add a raw property.
+ *
+ * @param key property name
+ * @param getter getter function (returns current value)
+ * @param setter setter function (sets new value)
+ */
+ virtual void AddRawProperty(std::string_view key,
+ std::function<std::string()> getter,
+ std::function<void(std::string_view)> setter) = 0;
+
+ /**
+ * Add a string property (SmallString form).
+ *
+ * @param key property name
+ * @param getter getter function (returns current value)
+ * @param setter setter function (sets new value)
+ */
+ virtual void AddSmallStringProperty(
+ std::string_view key,
+ std::function<std::string_view(wpi::SmallVectorImpl<char>& buf)> getter,
+ std::function<void(std::string_view)> setter) = 0;
+
+ /**
+ * Add a boolean array property (SmallVector form).
+ *
+ * @param key property name
+ * @param getter getter function (returns current value)
+ * @param setter setter function (sets new value)
+ */
+ virtual void AddSmallBooleanArrayProperty(
+ std::string_view key,
+ std::function<wpi::span<const int>(wpi::SmallVectorImpl<int>& buf)>
+ getter,
+ std::function<void(wpi::span<const int>)> setter) = 0;
+
+ /**
+ * Add a double array property (SmallVector form).
+ *
+ * @param key property name
+ * @param getter getter function (returns current value)
+ * @param setter setter function (sets new value)
+ */
+ virtual void AddSmallDoubleArrayProperty(
+ std::string_view key,
+ std::function<wpi::span<const double>(wpi::SmallVectorImpl<double>& buf)>
+ getter,
+ std::function<void(wpi::span<const double>)> setter) = 0;
+
+ /**
+ * Add a string array property (SmallVector form).
+ *
+ * @param key property name
+ * @param getter getter function (returns current value)
+ * @param setter setter function (sets new value)
+ */
+ virtual void AddSmallStringArrayProperty(
+ std::string_view key,
+ std::function<
+ wpi::span<const std::string>(wpi::SmallVectorImpl<std::string>& buf)>
+ getter,
+ std::function<void(wpi::span<const std::string>)> setter) = 0;
+
+ /**
+ * Add a raw property (SmallVector form).
+ *
+ * @param key property name
+ * @param getter getter function (returns current value)
+ * @param setter setter function (sets new value)
+ */
+ virtual void AddSmallRawProperty(
+ std::string_view key,
+ std::function<std::string_view(wpi::SmallVectorImpl<char>& buf)> getter,
+ std::function<void(std::string_view)> setter) = 0;
+
+ /**
+ * Gets the kind of backend being used.
+ *
+ * @return Backend kind
+ */
+ virtual BackendKind GetBackendKind() const = 0;
+
+ /**
+ * Return whether this sendable has been published.
+ *
+ * @return True if it has been published, false if not.
+ */
+ virtual bool IsPublished() const = 0;
+
+ /**
+ * Update the published values by calling the getters for all properties.
+ */
+ virtual void Update() = 0;
+
+ /**
+ * Clear properties.
+ */
+ virtual void ClearProperties() = 0;
+};
+
+} // namespace wpi
diff --git a/wpiutil/src/main/native/include/wpi/sendable/SendableHelper.h b/wpiutil/src/main/native/include/wpi/sendable/SendableHelper.h
new file mode 100644
index 0000000..5b6bbe3
--- /dev/null
+++ b/wpiutil/src/main/native/include/wpi/sendable/SendableHelper.h
@@ -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.
+
+#pragma once
+
+#include <memory>
+#include <string>
+#include <string_view>
+
+#include "wpi/deprecated.h"
+#include "wpi/sendable/SendableRegistry.h"
+
+namespace wpi {
+
+/**
+ * A helper class for use with objects that add themselves to SendableRegistry.
+ * It takes care of properly calling Move() and Remove() on move and
+ * destruction. No action is taken if the object is copied.
+ * Use public inheritance with CRTP when using this class.
+ * @tparam CRTP derived class
+ */
+template <typename Derived>
+class SendableHelper {
+ public:
+ SendableHelper(const SendableHelper& rhs) = default;
+ SendableHelper& operator=(const SendableHelper& rhs) = default;
+
+#if !defined(_MSC_VER) && (defined(__llvm__) || __GNUC__ > 7)
+ // See https://bugzilla.mozilla.org/show_bug.cgi?id=1442819
+ __attribute__((no_sanitize("vptr")))
+#endif
+ SendableHelper(SendableHelper&& rhs) {
+ // it is safe to call Move() multiple times with the same rhs
+ SendableRegistry::Move(static_cast<Derived*>(this),
+ static_cast<Derived*>(&rhs));
+ }
+
+#if !defined(_MSC_VER) && (defined(__llvm__) || __GNUC__ > 7)
+ // See https://bugzilla.mozilla.org/show_bug.cgi?id=1442819
+ __attribute__((no_sanitize("vptr")))
+#endif
+ SendableHelper&
+ operator=(SendableHelper&& rhs) {
+ // it is safe to call Move() multiple times with the same rhs
+ SendableRegistry::Move(static_cast<Derived*>(this),
+ static_cast<Derived*>(&rhs));
+ return *this;
+ }
+
+ /**
+ * Gets the name of this Sendable object.
+ *
+ * @deprecated use SendableRegistry::GetName()
+ *
+ * @return Name
+ */
+ WPI_DEPRECATED("use SendableRegistry::GetName()")
+ std::string GetName() const {
+ return SendableRegistry::GetName(static_cast<const Derived*>(this));
+ }
+
+ /**
+ * Sets the name of this Sendable object.
+ *
+ * @deprecated use SendableRegistry::SetName()
+ *
+ * @param name name
+ */
+ WPI_DEPRECATED("use SendableRegistry::SetName()")
+ void SetName(std::string_view name) {
+ SendableRegistry::SetName(static_cast<Derived*>(this), name);
+ }
+
+ /**
+ * Sets both the subsystem name and device name of this Sendable object.
+ *
+ * @deprecated use SendableRegistry::SetName()
+ *
+ * @param subsystem subsystem name
+ * @param name device name
+ */
+ WPI_DEPRECATED("use SendableRegistry::SetName()")
+ void SetName(std::string_view subsystem, std::string_view name) {
+ SendableRegistry::SetName(static_cast<Derived*>(this), subsystem, name);
+ }
+
+ /**
+ * Gets the subsystem name of this Sendable object.
+ *
+ * @deprecated use SendableRegistry::GetSubsystem().
+ *
+ * @return Subsystem name
+ */
+ WPI_DEPRECATED("use SendableRegistry::GetSubsystem()")
+ std::string GetSubsystem() const {
+ return SendableRegistry::GetSubsystem(static_cast<const Derived*>(this));
+ }
+
+ /**
+ * Sets the subsystem name of this Sendable object.
+ *
+ * @deprecated use SendableRegistry::SetSubsystem()
+ *
+ * @param subsystem subsystem name
+ */
+ WPI_DEPRECATED("use SendableRegistry::SetSubsystem()")
+ void SetSubsystem(std::string_view subsystem) {
+ SendableRegistry::SetSubsystem(static_cast<Derived*>(this), subsystem);
+ }
+
+ protected:
+ /**
+ * Add a child component.
+ *
+ * @deprecated use SendableRegistry::AddChild()
+ *
+ * @param child child component
+ */
+ WPI_DEPRECATED("use SendableRegistry::AddChild()")
+ void AddChild(std::shared_ptr<Sendable> child) {
+ SendableRegistry::AddChild(static_cast<Derived*>(this), child.get());
+ }
+
+ /**
+ * Add a child component.
+ *
+ * @deprecated use SendableRegistry::AddChild()
+ *
+ * @param child child component
+ */
+ WPI_DEPRECATED("use SendableRegistry::AddChild()")
+ void AddChild(void* child) {
+ SendableRegistry::AddChild(static_cast<Derived*>(this), child);
+ }
+
+ /**
+ * Sets the name of the sensor with a channel number.
+ *
+ * @deprecated use SendableRegistry::SetName()
+ *
+ * @param moduleType A string that defines the module name in the label for
+ * the value
+ * @param channel The channel number the device is plugged into
+ */
+ WPI_DEPRECATED("use SendableRegistry::SetName()")
+ void SetName(std::string_view moduleType, int channel) {
+ SendableRegistry::SetName(static_cast<Derived*>(this), moduleType, channel);
+ }
+
+ /**
+ * Sets the name of the sensor with a module and channel number.
+ *
+ * @deprecated use SendableRegistry::SetName()
+ *
+ * @param moduleType A string that defines the module name in the label for
+ * the value
+ * @param moduleNumber The number of the particular module type
+ * @param channel The channel number the device is plugged into (usually
+ * PWM)
+ */
+ WPI_DEPRECATED("use SendableRegistry::SetName()")
+ void SetName(std::string_view moduleType, int moduleNumber, int channel) {
+ SendableRegistry::SetName(static_cast<Derived*>(this), moduleType,
+ moduleNumber, channel);
+ }
+
+ protected:
+ SendableHelper() = default;
+
+ ~SendableHelper() {
+ // it is safe to call Remove() multiple times with the same object
+ SendableRegistry::Remove(static_cast<Derived*>(this));
+ }
+};
+
+} // namespace wpi
diff --git a/wpiutil/src/main/native/include/wpi/sendable/SendableRegistry.h b/wpiutil/src/main/native/include/wpi/sendable/SendableRegistry.h
new file mode 100644
index 0000000..a08f9e9
--- /dev/null
+++ b/wpiutil/src/main/native/include/wpi/sendable/SendableRegistry.h
@@ -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.
+
+#pragma once
+
+#include <functional>
+#include <memory>
+#include <string>
+#include <string_view>
+
+#include "wpi/function_ref.h"
+
+namespace wpi {
+
+class Sendable;
+class SendableBuilder;
+
+/**
+ * The SendableRegistry class is the public interface for registering sensors
+ * and actuators for use on dashboards and LiveWindow.
+ */
+class SendableRegistry {
+ public:
+ SendableRegistry() = delete;
+
+ using UID = size_t;
+
+ /**
+ * Sets the factory for LiveWindow builders.
+ *
+ * @param factory factory function
+ */
+ static void SetLiveWindowBuilderFactory(
+ std::function<std::unique_ptr<SendableBuilder>()> factory);
+
+ /**
+ * Adds an object to the registry.
+ *
+ * @param sendable object to add
+ * @param name component name
+ */
+ static void Add(Sendable* sendable, std::string_view name);
+
+ /**
+ * Adds an object to the registry.
+ *
+ * @param sendable object to add
+ * @param moduleType A string that defines the module name in the label for
+ * the value
+ * @param channel The channel number the device is plugged into
+ */
+ static void Add(Sendable* sendable, std::string_view moduleType, int channel);
+
+ /**
+ * Adds an object to the registry.
+ *
+ * @param sendable object to add
+ * @param moduleType A string that defines the module name in the label for
+ * the value
+ * @param moduleNumber The number of the particular module type
+ * @param channel The channel number the device is plugged into
+ */
+ static void Add(Sendable* sendable, std::string_view moduleType,
+ int moduleNumber, int channel);
+
+ /**
+ * Adds an object to the registry.
+ *
+ * @param sendable object to add
+ * @param subsystem subsystem name
+ * @param name component name
+ */
+ static void Add(Sendable* sendable, std::string_view subsystem,
+ std::string_view name);
+
+ /**
+ * Adds an object to the registry and LiveWindow.
+ *
+ * @param sendable object to add
+ * @param name component name
+ */
+ static void AddLW(Sendable* sendable, std::string_view name);
+
+ /**
+ * Adds an object to the registry and LiveWindow.
+ *
+ * @param sendable object to add
+ * @param moduleType A string that defines the module name in the label for
+ * the value
+ * @param channel The channel number the device is plugged into
+ */
+ static void AddLW(Sendable* sendable, std::string_view moduleType,
+ int channel);
+
+ /**
+ * Adds an object to the registry and LiveWindow.
+ *
+ * @param sendable object to add
+ * @param moduleType A string that defines the module name in the label for
+ * the value
+ * @param moduleNumber The number of the particular module type
+ * @param channel The channel number the device is plugged into
+ */
+ static void AddLW(Sendable* sendable, std::string_view moduleType,
+ int moduleNumber, int channel);
+
+ /**
+ * Adds an object to the registry and LiveWindow.
+ *
+ * @param sendable object to add
+ * @param subsystem subsystem name
+ * @param name component name
+ */
+ static void AddLW(Sendable* sendable, std::string_view subsystem,
+ std::string_view name);
+
+ /**
+ * Adds a child object to an object. Adds the child object to the registry
+ * if it's not already present.
+ *
+ * @param parent parent object
+ * @param child child object
+ */
+ static void AddChild(Sendable* parent, Sendable* child);
+
+ /**
+ * Adds a child object to an object. Adds the child object to the registry
+ * if it's not already present.
+ *
+ * @param parent parent object
+ * @param child child object
+ */
+ static void AddChild(Sendable* parent, void* child);
+
+ /**
+ * Removes an object from the registry.
+ *
+ * @param sendable object to remove
+ * @return true if the object was removed; false if it was not present
+ */
+ static bool Remove(Sendable* sendable);
+
+ /**
+ * Moves an object in the registry (for use in move constructors/assignments).
+ *
+ * @param to new object
+ * @param from old object
+ */
+ static void Move(Sendable* to, Sendable* from);
+
+ /**
+ * Determines if an object is in the registry.
+ *
+ * @param sendable object to check
+ * @return True if in registry, false if not.
+ */
+ static bool Contains(const Sendable* sendable);
+
+ /**
+ * Gets the name of an object.
+ *
+ * @param sendable object
+ * @return Name (empty if object is not in registry)
+ */
+ static std::string GetName(const Sendable* sendable);
+
+ /**
+ * Sets the name of an object.
+ *
+ * @param sendable object
+ * @param name name
+ */
+ static void SetName(Sendable* sendable, std::string_view name);
+
+ /**
+ * Sets the name of an object with a channel number.
+ *
+ * @param sendable object
+ * @param moduleType A string that defines the module name in the label for
+ * the value
+ * @param channel The channel number the device is plugged into
+ */
+ static void SetName(Sendable* sendable, std::string_view moduleType,
+ int channel);
+
+ /**
+ * Sets the name of an object with a module and channel number.
+ *
+ * @param sendable object
+ * @param moduleType A string that defines the module name in the label for
+ * the value
+ * @param moduleNumber The number of the particular module type
+ * @param channel The channel number the device is plugged into
+ */
+ static void SetName(Sendable* sendable, std::string_view moduleType,
+ int moduleNumber, int channel);
+
+ /**
+ * Sets both the subsystem name and device name of an object.
+ *
+ * @param sendable object
+ * @param subsystem subsystem name
+ * @param name device name
+ */
+ static void SetName(Sendable* sendable, std::string_view subsystem,
+ std::string_view name);
+
+ /**
+ * Gets the subsystem name of an object.
+ *
+ * @param sendable object
+ * @return Subsystem name (empty if object is not in registry)
+ */
+ static std::string GetSubsystem(const Sendable* sendable);
+
+ /**
+ * Sets the subsystem name of an object.
+ *
+ * @param sendable object
+ * @param subsystem subsystem name
+ */
+ static void SetSubsystem(Sendable* sendable, std::string_view subsystem);
+
+ /**
+ * Gets a unique handle for setting/getting data with SetData() and GetData().
+ *
+ * @return Handle
+ */
+ static int GetDataHandle();
+
+ /**
+ * Associates arbitrary data with an object in the registry.
+ *
+ * @param sendable object
+ * @param handle data handle returned by GetDataHandle()
+ * @param data data to set
+ * @return Previous data (may be null)
+ */
+ static std::shared_ptr<void> SetData(Sendable* sendable, int handle,
+ std::shared_ptr<void> data);
+
+ /**
+ * Gets arbitrary data associated with an object in the registry.
+ *
+ * @param sendable object
+ * @param handle data handle returned by GetDataHandle()
+ * @return data (may be null if none associated)
+ */
+ static std::shared_ptr<void> GetData(Sendable* sendable, int handle);
+
+ /**
+ * Enables LiveWindow for an object.
+ *
+ * @param sendable object
+ */
+ static void EnableLiveWindow(Sendable* sendable);
+
+ /**
+ * Disables LiveWindow for an object.
+ *
+ * @param sendable object
+ */
+ static void DisableLiveWindow(Sendable* sendable);
+
+ /**
+ * Get unique id for an object. Since objects can move, use this instead
+ * of storing Sendable* directly if ownership is in question.
+ *
+ * @param sendable object
+ * @return unique id
+ */
+ static UID GetUniqueId(Sendable* sendable);
+
+ /**
+ * Get sendable object for a given unique id.
+ *
+ * @param uid unique id
+ * @return sendable object (may be null)
+ */
+ static Sendable* GetSendable(UID uid);
+
+ /**
+ * Publishes an object in the registry.
+ *
+ * @param sendableUid sendable unique id
+ * @param builder publisher backend
+ */
+ static void Publish(UID sendableUid,
+ std::unique_ptr<SendableBuilder> builder);
+
+ /**
+ * Updates published information from an object.
+ *
+ * @param sendableUid sendable unique id
+ */
+ static void Update(UID sendableUid);
+
+ /**
+ * Data passed to ForeachLiveWindow() callback function
+ */
+ struct CallbackData {
+ CallbackData(Sendable* sendable_, std::string_view name_,
+ std::string_view subsystem_, wpi::Sendable* parent_,
+ std::shared_ptr<void>& data_, SendableBuilder& builder_)
+ : sendable(sendable_),
+ name(name_),
+ subsystem(subsystem_),
+ parent(parent_),
+ data(data_),
+ builder(builder_) {}
+
+ Sendable* sendable;
+ std::string_view name;
+ std::string_view subsystem;
+ Sendable* parent;
+ std::shared_ptr<void>& data;
+ SendableBuilder& builder;
+ };
+
+ /**
+ * Iterates over LiveWindow-enabled objects in the registry.
+ * It is *not* safe to call other SendableRegistry functions from the
+ * callback (this will likely deadlock).
+ *
+ * @param dataHandle data handle to get data pointer passed to callback
+ * @param callback function to call for each object
+ */
+ static void ForeachLiveWindow(
+ int dataHandle, wpi::function_ref<void(CallbackData& cbdata)> callback);
+};
+
+} // namespace wpi
diff --git a/wpiutil/src/main/native/include/wpi/sha1.h b/wpiutil/src/main/native/include/wpi/sha1.h
index 6a0817f..75464bf 100644
--- a/wpiutil/src/main/native/include/wpi/sha1.h
+++ b/wpiutil/src/main/native/include/wpi/sha1.h
@@ -23,8 +23,7 @@
#include <stdint.h>
#include <string>
-
-#include "wpi/StringRef.h"
+#include <string_view>
namespace wpi {
template <typename T>
@@ -34,12 +33,12 @@
class SHA1 {
public:
SHA1();
- void Update(StringRef s);
+ void Update(std::string_view s);
void Update(raw_istream& is);
std::string Final();
- StringRef Final(SmallVectorImpl<char>& buf);
- StringRef RawFinal(SmallVectorImpl<char>& buf);
- static std::string FromFile(StringRef filename);
+ std::string_view Final(SmallVectorImpl<char>& buf);
+ std::string_view RawFinal(SmallVectorImpl<char>& buf);
+ static std::string FromFile(std::string_view filename);
private:
uint32_t digest[5];
diff --git a/wpiutil/src/main/native/include/wpi/span.h b/wpiutil/src/main/native/include/wpi/span.h
new file mode 100644
index 0000000..cf71d79
--- /dev/null
+++ b/wpiutil/src/main/native/include/wpi/span.h
@@ -0,0 +1,415 @@
+
+/*
+This is an implementation of C++20's std::span
+http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/n4820.pdf
+*/
+
+// Copyright Tristan Brindle 2018.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file ../../LICENSE_1_0.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef WPIUTIL_WPI_SPAN_HPP_INCLUDED
+#define WPIUTIL_WPI_SPAN_HPP_INCLUDED
+
+#include <array>
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <type_traits>
+#if __cplusplus >= 202002L && __has_include(<span>)
+#include <span>
+#endif
+
+namespace wpi {
+
+inline constexpr std::size_t dynamic_extent = SIZE_MAX;
+
+template <typename ElementType, std::size_t Extent = dynamic_extent>
+class span;
+
+namespace detail {
+
+template <typename E, std::size_t S>
+struct span_storage {
+ constexpr span_storage() noexcept = default;
+
+ constexpr span_storage(E* p_ptr, std::size_t /*unused*/) noexcept
+ : ptr(p_ptr)
+ {}
+
+ E* ptr = nullptr;
+ static constexpr std::size_t size = S;
+};
+
+template <typename E>
+struct span_storage<E, dynamic_extent> {
+ constexpr span_storage() noexcept = default;
+
+ constexpr span_storage(E* p_ptr, std::size_t p_size) noexcept
+ : ptr(p_ptr), size(p_size)
+ {}
+
+ E* ptr = nullptr;
+ std::size_t size = 0;
+};
+
+template <typename T>
+using uncvref_t =
+ typename std::remove_cv<typename std::remove_reference<T>::type>::type;
+
+template <typename>
+struct is_span : std::false_type {};
+
+template <typename T, std::size_t S>
+struct is_span<span<T, S>> : std::true_type {};
+
+template <typename>
+struct is_std_array : std::false_type {};
+
+template <typename T, std::size_t N>
+struct is_std_array<std::array<T, N>> : std::true_type {};
+
+template <typename, typename = void>
+struct has_size_and_data : std::false_type {};
+
+template <typename T>
+struct has_size_and_data<T, std::void_t<decltype(std::size(std::declval<T>())),
+ decltype(std::data(std::declval<T>()))>>
+ : std::true_type {};
+
+template <typename C, typename U = uncvref_t<C>>
+struct is_container {
+ static constexpr bool value =
+ !is_span<U>::value && !is_std_array<U>::value &&
+ !std::is_array<U>::value && has_size_and_data<C>::value;
+};
+
+template <typename T>
+using remove_pointer_t = typename std::remove_pointer<T>::type;
+
+template <typename, typename, typename = void>
+struct is_container_element_type_compatible : std::false_type {};
+
+template <typename T, typename E>
+struct is_container_element_type_compatible<
+ T, E,
+ typename std::enable_if<
+ !std::is_same<typename std::remove_cv<decltype(
+ std::data(std::declval<T>()))>::type,
+ void>::value>::type>
+ : std::is_convertible<
+ remove_pointer_t<decltype(std::data(std::declval<T>()))> (*)[],
+ E (*)[]> {};
+
+template <typename, typename = size_t>
+struct is_complete : std::false_type {};
+
+template <typename T>
+struct is_complete<T, decltype(sizeof(T))> : std::true_type {};
+
+} // namespace detail
+
+template <typename ElementType, std::size_t Extent>
+class span {
+ static_assert(std::is_object<ElementType>::value,
+ "A span's ElementType must be an object type (not a "
+ "reference type or void)");
+ static_assert(detail::is_complete<ElementType>::value,
+ "A span's ElementType must be a complete type (not a forward "
+ "declaration)");
+ static_assert(!std::is_abstract<ElementType>::value,
+ "A span's ElementType cannot be an abstract class type");
+
+ using storage_type = detail::span_storage<ElementType, Extent>;
+
+public:
+ // constants and types
+ using element_type = ElementType;
+ using value_type = typename std::remove_cv<ElementType>::type;
+ using size_type = std::size_t;
+ using difference_type = std::ptrdiff_t;
+ using pointer = element_type*;
+ using const_pointer = const element_type*;
+ using reference = element_type&;
+ using const_reference = const element_type&;
+ using iterator = pointer;
+ using reverse_iterator = std::reverse_iterator<iterator>;
+
+ static constexpr size_type extent = Extent;
+
+ // [span.cons], span constructors, copy, assignment, and destructor
+ template <
+ std::size_t E = Extent,
+ typename std::enable_if<(E == dynamic_extent || E <= 0), int>::type = 0>
+ constexpr span() noexcept
+ {}
+
+ constexpr span(pointer ptr, size_type count)
+ : storage_(ptr, count)
+ {
+ assert(extent == dynamic_extent || count == extent);
+ }
+
+ constexpr span(pointer first_elem, pointer last_elem)
+ : storage_(first_elem, last_elem - first_elem)
+ {
+ assert(extent == dynamic_extent ||
+ last_elem - first_elem ==
+ static_cast<std::ptrdiff_t>(extent));
+ }
+
+ template <std::size_t N, std::size_t E = Extent,
+ typename std::enable_if<
+ (E == dynamic_extent || N == E) &&
+ detail::is_container_element_type_compatible<
+ element_type (&)[N], ElementType>::value,
+ int>::type = 0>
+ constexpr span(element_type (&arr)[N]) noexcept : storage_(arr, N)
+ {}
+
+ template <std::size_t N, std::size_t E = Extent,
+ typename std::enable_if<
+ (E == dynamic_extent || N == E) &&
+ detail::is_container_element_type_compatible<
+ std::array<value_type, N>&, ElementType>::value,
+ int>::type = 0>
+ constexpr span(std::array<value_type, N>& arr) noexcept
+ : storage_(arr.data(), N)
+ {}
+
+ template <std::size_t N, std::size_t E = Extent,
+ typename std::enable_if<
+ (E == dynamic_extent || N == E) &&
+ detail::is_container_element_type_compatible<
+ const std::array<value_type, N>&, ElementType>::value,
+ int>::type = 0>
+ constexpr span(const std::array<value_type, N>& arr) noexcept
+ : storage_(arr.data(), N)
+ {}
+
+ template <
+ typename Container, std::size_t E = Extent,
+ typename std::enable_if<
+ E == dynamic_extent && detail::is_container<Container>::value &&
+ detail::is_container_element_type_compatible<
+ Container&, ElementType>::value,
+ int>::type = 0>
+ constexpr span(Container& cont)
+ : storage_(std::data(cont), std::size(cont))
+ {}
+
+ template <
+ typename Container, std::size_t E = Extent,
+ typename std::enable_if<
+ E == dynamic_extent && detail::is_container<Container>::value &&
+ detail::is_container_element_type_compatible<
+ const Container&, ElementType>::value,
+ int>::type = 0>
+ constexpr span(const Container& cont)
+ : storage_(std::data(cont), std::size(cont))
+ {}
+
+ constexpr span(const span& other) noexcept = default;
+
+ template <typename OtherElementType, std::size_t OtherExtent,
+ typename std::enable_if<
+ (Extent == OtherExtent || Extent == dynamic_extent) &&
+ std::is_convertible<OtherElementType (*)[],
+ ElementType (*)[]>::value,
+ int>::type = 0>
+ constexpr span(const span<OtherElementType, OtherExtent>& other) noexcept
+ : storage_(other.data(), other.size())
+ {}
+
+#ifdef __cpp_lib_span
+ constexpr span(std::span<ElementType> other) noexcept
+ : storage_(other.data(), other.size())
+ {}
+#endif
+
+ ~span() noexcept = default;
+
+ constexpr span&
+ operator=(const span& other) noexcept = default;
+
+ // [span.sub], span subviews
+ template <std::size_t Count>
+ constexpr span<element_type, Count> first() const
+ {
+ assert(Count <= size());
+ return {data(), Count};
+ }
+
+ template <std::size_t Count>
+ constexpr span<element_type, Count> last() const
+ {
+ assert(Count <= size());
+ return {data() + (size() - Count), Count};
+ }
+
+ template <std::size_t Offset, std::size_t Count = dynamic_extent>
+ using subspan_return_t =
+ span<ElementType, Count != dynamic_extent
+ ? Count
+ : (Extent != dynamic_extent ? Extent - Offset
+ : dynamic_extent)>;
+
+ template <std::size_t Offset, std::size_t Count = dynamic_extent>
+ constexpr subspan_return_t<Offset, Count> subspan() const
+ {
+ assert(Offset <= size() &&
+ (Count == dynamic_extent || Offset + Count <= size()));
+ return {data() + Offset,
+ Count != dynamic_extent ? Count : size() - Offset};
+ }
+
+ constexpr span<element_type, dynamic_extent>
+ first(size_type count) const
+ {
+ assert(count <= size());
+ return {data(), count};
+ }
+
+ constexpr span<element_type, dynamic_extent>
+ last(size_type count) const
+ {
+ assert(count <= size());
+ return {data() + (size() - count), count};
+ }
+
+ constexpr span<element_type, dynamic_extent>
+ subspan(size_type offset, size_type count = dynamic_extent) const
+ {
+ assert(offset <= size() &&
+ (count == dynamic_extent || offset + count <= size()));
+ return {data() + offset,
+ count == dynamic_extent ? size() - offset : count};
+ }
+
+ // [span.obs], span observers
+ constexpr size_type size() const noexcept { return storage_.size; }
+
+ constexpr size_type size_bytes() const noexcept
+ {
+ return size() * sizeof(element_type);
+ }
+
+ [[nodiscard]] constexpr bool empty() const noexcept
+ {
+ return size() == 0;
+ }
+
+ // [span.elem], span element access
+ constexpr reference operator[](size_type idx) const
+ {
+ assert(idx < size());
+ return *(data() + idx);
+ }
+
+ constexpr reference front() const
+ {
+ assert(!empty());
+ return *data();
+ }
+
+ constexpr reference back() const
+ {
+ assert(!empty());
+ return *(data() + (size() - 1));
+ }
+
+ constexpr pointer data() const noexcept { return storage_.ptr; }
+
+ // [span.iterators], span iterator support
+ constexpr iterator begin() const noexcept { return data(); }
+
+ constexpr iterator end() const noexcept { return data() + size(); }
+
+ constexpr reverse_iterator rbegin() const noexcept
+ {
+ return reverse_iterator(end());
+ }
+
+ constexpr reverse_iterator rend() const noexcept
+ {
+ return reverse_iterator(begin());
+ }
+
+#ifdef __cpp_lib_span
+ constexpr operator auto() const {
+ return std::span < ElementType,
+ (Extent == dynamic_extent)
+ ? std::dynamic_extent
+ : Extent > (storage_.ptr, storage_.size);
+ }
+#endif
+
+private:
+ storage_type storage_{};
+};
+
+/* Deduction Guides */
+template <class T, size_t N>
+span(T (&)[N])->span<T, N>;
+
+template <class T, size_t N>
+span(std::array<T, N>&)->span<T, N>;
+
+template <class T, size_t N>
+span(const std::array<T, N>&)->span<const T, N>;
+
+template <class Container>
+span(Container&)->span<typename Container::value_type>;
+
+template <class Container>
+span(const Container&)->span<const typename Container::value_type>;
+
+template <typename ElementType, std::size_t Extent>
+span<const std::byte, ((Extent == dynamic_extent) ? dynamic_extent
+ : sizeof(ElementType) * Extent)>
+as_bytes(span<ElementType, Extent> s) noexcept
+{
+ return {reinterpret_cast<const std::byte*>(s.data()), s.size_bytes()};
+}
+
+template <
+ class ElementType, size_t Extent,
+ typename std::enable_if<!std::is_const<ElementType>::value, int>::type = 0>
+span<std::byte, ((Extent == dynamic_extent) ? dynamic_extent
+ : sizeof(ElementType) * Extent)>
+as_writable_bytes(span<ElementType, Extent> s) noexcept
+{
+ return {reinterpret_cast<std::byte*>(s.data()), s.size_bytes()};
+}
+
+template <std::size_t N, typename E, std::size_t S>
+constexpr auto get(span<E, S> s) -> decltype(s[N])
+{
+ return s[N];
+}
+
+} // namespace wpi
+
+namespace std {
+
+template <typename ElementType, size_t Extent>
+class tuple_size<wpi::span<ElementType, Extent>>
+ : public integral_constant<size_t, Extent> {};
+
+template <typename ElementType>
+class tuple_size<wpi::span<
+ ElementType, wpi::dynamic_extent>>; // not defined
+
+template <size_t I, typename ElementType, size_t Extent>
+class tuple_element<I, wpi::span<ElementType, Extent>> {
+public:
+ static_assert(Extent != wpi::dynamic_extent &&
+ I < Extent,
+ "");
+ using type = ElementType;
+};
+
+} // end namespace std
+
+#endif // WPIUTIL_WPI_SPAN_HPP_INCLUDED
diff --git a/wpiutil/src/main/native/include/wpi/spinlock.h b/wpiutil/src/main/native/include/wpi/spinlock.h
index 73137f1..9e777fa 100644
--- a/wpiutil/src/main/native/include/wpi/spinlock.h
+++ b/wpiutil/src/main/native/include/wpi/spinlock.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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
@@ -30,8 +27,11 @@
LLVM_ATTRIBUTE_ALWAYS_INLINE
void lock() {
- for (unsigned int i = 1; !try_lock(); ++i)
- if ((i & 0xff) == 0) std::this_thread::yield();
+ for (unsigned int i = 1; !try_lock(); ++i) {
+ if ((i & 0xff) == 0) {
+ std::this_thread::yield();
+ }
+ }
}
LLVM_ATTRIBUTE_ALWAYS_INLINE
@@ -58,8 +58,9 @@
std::memory_order_release);
} else {
if (owner_thread_id.load(std::memory_order_acquire) !=
- std::this_thread::get_id())
+ std::this_thread::get_id()) {
return false;
+ }
}
++recursive_counter;
return true;
@@ -67,8 +68,11 @@
LLVM_ATTRIBUTE_ALWAYS_INLINE
void lock() {
- for (unsigned int i = 1; !try_lock(); ++i)
- if ((i & 0xffff) == 0) std::this_thread::yield();
+ for (unsigned int i = 1; !try_lock(); ++i) {
+ if ((i & 0xffff) == 0) {
+ std::this_thread::yield();
+ }
+ }
}
LLVM_ATTRIBUTE_ALWAYS_INLINE
@@ -100,7 +104,9 @@
auto us = std::this_thread::get_id();
if (!owner_thread_id.compare_exchange_weak(owner, us,
std::memory_order_acquire)) {
- if (owner != us) return false;
+ if (owner != us) {
+ return false;
+ }
}
++recursive_counter;
return true;
@@ -108,8 +114,11 @@
LLVM_ATTRIBUTE_ALWAYS_INLINE
void lock() {
- for (unsigned int i = 1; !try_lock(); ++i)
- if ((i & 0xffff) == 0) std::this_thread::yield();
+ for (unsigned int i = 1; !try_lock(); ++i) {
+ if ((i & 0xffff) == 0) {
+ std::this_thread::yield();
+ }
+ }
}
LLVM_ATTRIBUTE_ALWAYS_INLINE
@@ -118,8 +127,9 @@
std::this_thread::get_id());
assert(recursive_counter > 0);
- if (--recursive_counter == 0)
+ if (--recursive_counter == 0) {
owner_thread_id.store(std::thread::id{}, std::memory_order_release);
+ }
}
};
diff --git a/wpiutil/src/main/native/include/wpi/static_circular_buffer.h b/wpiutil/src/main/native/include/wpi/static_circular_buffer.h
index 8329917..8cbf3fe 100644
--- a/wpiutil/src/main/native/include/wpi/static_circular_buffer.h
+++ b/wpiutil/src/main/native/include/wpi/static_circular_buffer.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2020 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. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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
@@ -19,9 +16,7 @@
template <class T, size_t N>
class static_circular_buffer {
public:
- static_assert(N > 0, "The circular buffer size shouldn't be zero.");
-
- constexpr static_circular_buffer() = default;
+ static_assert(N > 0, "Circular buffer size cannot be zero.");
class iterator {
public:
@@ -87,17 +82,38 @@
size_t m_index;
};
+ /**
+ * Returns begin iterator.
+ */
iterator begin() { return iterator(this, 0); }
+
+ /**
+ * Returns end iterator.
+ */
iterator end() {
return iterator(this, ::wpi::static_circular_buffer<T, N>::size());
}
+ /**
+ * Returns begin iterator.
+ */
const_iterator begin() const { return const_iterator(this, 0); }
+
+ /**
+ * Returns end iterator.
+ */
const_iterator end() const {
return const_iterator(this, ::wpi::static_circular_buffer<T, N>::size());
}
+ /**
+ * Returns begin iterator.
+ */
const_iterator cbegin() const { return const_iterator(this, 0); }
+
+ /**
+ * Returns end iterator.
+ */
const_iterator cend() const {
return const_iterator(this, ::wpi::static_circular_buffer<T, N>::size());
}
diff --git a/wpiutil/src/main/native/include/wpi/timestamp.h b/wpiutil/src/main/native/include/wpi/timestamp.h
index aefcd56..789e125 100644
--- a/wpiutil/src/main/native/include/wpi/timestamp.h
+++ b/wpiutil/src/main/native/include/wpi/timestamp.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2015-2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_TIMESTAMP_H_
#define WPIUTIL_WPI_TIMESTAMP_H_
diff --git a/wpiutil/src/main/native/include/wpi/uv/Async.h b/wpiutil/src/main/native/include/wpi/uv/Async.h
index 2da870d..49f3dde 100644
--- a/wpiutil/src/main/native/include/wpi/uv/Async.h
+++ b/wpiutil/src/main/native/include/wpi/uv/Async.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UV_ASYNC_H_
#define WPIUTIL_WPI_UV_ASYNC_H_
@@ -21,8 +18,7 @@
#include "wpi/uv/Handle.h"
#include "wpi/uv/Loop.h"
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
/**
* Async handle.
@@ -43,10 +39,11 @@
Async(const std::shared_ptr<Loop>& loop, const private_init&)
: m_loop{loop} {}
~Async() noexcept override {
- if (auto loop = m_loop.lock())
+ if (auto loop = m_loop.lock()) {
this->Close();
- else
+ } else {
this->ForceClosed();
+ }
}
/**
@@ -69,7 +66,9 @@
uv_async_init(loop->GetRaw(), h->GetRaw(), [](uv_async_t* handle) {
auto& h = *static_cast<Async*>(handle->data);
std::scoped_lock lock(h.m_mutex);
- for (auto&& v : h.m_data) std::apply(h.wakeup, v);
+ for (auto&& v : h.m_data) {
+ std::apply(h.wakeup, v);
+ }
h.m_data.clear();
});
if (err < 0) {
@@ -99,7 +98,9 @@
std::scoped_lock lock(m_mutex);
m_data.emplace_back(std::forward_as_tuple(std::forward<U>(u)...));
}
- if (loop) this->Invoke(&uv_async_send, this->GetRaw());
+ if (loop) {
+ this->Invoke(&uv_async_send, this->GetRaw());
+ }
}
/**
@@ -149,7 +150,14 @@
* An async event will be emitted on the loop thread.
*/
void Send() {
- if (auto loop = m_loop.lock()) Invoke(&uv_async_send, GetRaw());
+ if (auto loop = m_loop.lock()) {
+ if (loop->GetThreadId() == std::this_thread::get_id()) {
+ // called from within the loop, just call the function directly
+ wakeup();
+ } else {
+ Invoke(&uv_async_send, GetRaw());
+ }
+ }
}
/**
@@ -161,7 +169,6 @@
std::weak_ptr<Loop> m_loop;
};
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv
#endif // WPIUTIL_WPI_UV_ASYNC_H_
diff --git a/wpiutil/src/main/native/include/wpi/uv/AsyncFunction.h b/wpiutil/src/main/native/include/wpi/uv/AsyncFunction.h
index 4510b84..fa4eb90 100644
--- a/wpiutil/src/main/native/include/wpi/uv/AsyncFunction.h
+++ b/wpiutil/src/main/native/include/wpi/uv/AsyncFunction.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UV_ASYNCFUNCTION_H_
#define WPIUTIL_WPI_UV_ASYNCFUNCTION_H_
@@ -23,8 +20,7 @@
#include "wpi/uv/Handle.h"
#include "wpi/uv/Loop.h"
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
template <typename T>
class AsyncFunction;
@@ -42,12 +38,13 @@
public:
AsyncFunction(const std::shared_ptr<Loop>& loop,
std::function<void(promise<R>, T...)> func, const private_init&)
- : wakeup{func}, m_loop{loop} {}
+ : wakeup{std::move(func)}, m_loop{loop} {}
~AsyncFunction() noexcept override {
- if (auto loop = m_loop.lock())
+ if (auto loop = m_loop.lock()) {
this->Close();
- else
+ } else {
this->ForceClosed();
+ }
}
/**
@@ -89,10 +86,11 @@
// waiting for it
for (auto&& v : h.m_params) {
auto p = h.m_promises.CreatePromise(v.first);
- if (h.wakeup)
+ if (h.wakeup) {
std::apply(h.wakeup,
std::tuple_cat(std::make_tuple(std::move(p)),
std::move(v.second)));
+ }
}
h.m_params.clear();
// wake up any threads that might be waiting for the result
@@ -139,7 +137,9 @@
}
// signal the loop
- if (loop) this->Invoke(&uv_async_send, this->GetRaw());
+ if (loop) {
+ this->Invoke(&uv_async_send, this->GetRaw());
+ }
// return future
return m_promises.CreateFuture(req);
@@ -162,7 +162,6 @@
std::weak_ptr<Loop> m_loop;
};
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv
#endif // WPIUTIL_WPI_UV_ASYNCFUNCTION_H_
diff --git a/wpiutil/src/main/native/include/wpi/uv/Buffer.h b/wpiutil/src/main/native/include/wpi/uv/Buffer.h
index 04e96b9..5126841 100644
--- a/wpiutil/src/main/native/include/wpi/uv/Buffer.h
+++ b/wpiutil/src/main/native/include/wpi/uv/Buffer.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2020 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. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UV_BUFFER_H_
#define WPIUTIL_WPI_UV_BUFFER_H_
@@ -12,14 +9,13 @@
#include <cstring>
#include <initializer_list>
+#include <string_view>
#include <utility>
-#include "wpi/ArrayRef.h"
#include "wpi/SmallVector.h"
-#include "wpi/StringRef.h"
+#include "wpi/span.h"
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
/**
* Data buffer. Convenience wrapper around uv_buf_t.
@@ -30,13 +26,13 @@
base = nullptr;
len = 0;
}
- /*implicit*/ Buffer(const uv_buf_t& oth) { // NOLINT(runtime/explicit)
+ /*implicit*/ Buffer(const uv_buf_t& oth) { // NOLINT
base = oth.base;
len = oth.len;
}
- /*implicit*/ Buffer(StringRef str) // NOLINT(runtime/explicit)
+ /*implicit*/ Buffer(std::string_view str) // NOLINT
: Buffer{str.data(), str.size()} {}
- /*implicit*/ Buffer(ArrayRef<uint8_t> arr) // NOLINT(runtime/explicit)
+ /*implicit*/ Buffer(span<const uint8_t> arr) // NOLINT
: Buffer{reinterpret_cast<const char*>(arr.data()), arr.size()} {}
Buffer(char* base_, size_t len_) {
base = base_;
@@ -47,21 +43,21 @@
len = static_cast<decltype(len)>(len_);
}
- ArrayRef<char> data() const { return ArrayRef<char>{base, len}; }
- MutableArrayRef<char> data() { return MutableArrayRef<char>{base, len}; }
+ span<const char> data() const { return {base, len}; }
+ span<char> data() { return {base, len}; }
- operator ArrayRef<char>() const { return data(); }
- operator MutableArrayRef<char>() { return data(); }
+ operator span<const char>() const { return data(); } // NOLINT
+ operator span<char>() { return data(); } // NOLINT
static Buffer Allocate(size_t size) { return Buffer{new char[size], size}; }
- static Buffer Dup(StringRef in) {
+ static Buffer Dup(std::string_view in) {
Buffer buf = Allocate(in.size());
std::memcpy(buf.base, in.data(), in.size());
return buf;
}
- static Buffer Dup(ArrayRef<uint8_t> in) {
+ static Buffer Dup(span<const uint8_t> in) {
Buffer buf = Allocate(in.size());
std::memcpy(buf.base, in.begin(), in.size());
return buf;
@@ -116,7 +112,9 @@
* Allocate a buffer.
*/
Buffer Allocate() {
- if (m_pool.empty()) return Buffer::Allocate(m_size);
+ if (m_pool.empty()) {
+ return Buffer::Allocate(m_size);
+ }
auto buf = m_pool.back();
m_pool.pop_back();
buf.len = m_size;
@@ -133,15 +131,19 @@
* This is NOT safe to use with arbitrary buffers unless they were
* allocated with the same size as the buffer pool allocation size.
*/
- void Release(MutableArrayRef<Buffer> bufs) {
- for (auto& buf : bufs) m_pool.emplace_back(buf.Move());
+ void Release(span<Buffer> bufs) {
+ for (auto& buf : bufs) {
+ m_pool.emplace_back(buf.Move());
+ }
}
/**
* Clear the pool, releasing all buffers.
*/
void Clear() {
- for (auto& buf : m_pool) buf.Deallocate();
+ for (auto& buf : m_pool) {
+ buf.Deallocate();
+ }
m_pool.clear();
}
@@ -153,10 +155,9 @@
private:
SmallVector<Buffer, DEPTH> m_pool;
- size_t m_size;
+ size_t m_size; // NOLINT
};
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv
#endif // WPIUTIL_WPI_UV_BUFFER_H_
diff --git a/wpiutil/src/main/native/include/wpi/uv/Check.h b/wpiutil/src/main/native/include/wpi/uv/Check.h
index e64a9f0..7555452 100644
--- a/wpiutil/src/main/native/include/wpi/uv/Check.h
+++ b/wpiutil/src/main/native/include/wpi/uv/Check.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UV_CHECK_H_
#define WPIUTIL_WPI_UV_CHECK_H_
@@ -15,8 +12,7 @@
#include "wpi/Signal.h"
#include "wpi/uv/Handle.h"
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
class Loop;
@@ -64,7 +60,6 @@
sig::Signal<> check;
};
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv
#endif // WPIUTIL_WPI_UV_CHECK_H_
diff --git a/wpiutil/src/main/native/include/wpi/uv/Error.h b/wpiutil/src/main/native/include/wpi/uv/Error.h
index 07a0ab8..1e717a4 100644
--- a/wpiutil/src/main/native/include/wpi/uv/Error.h
+++ b/wpiutil/src/main/native/include/wpi/uv/Error.h
@@ -1,24 +1,20 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UV_ERROR_H_
#define WPIUTIL_WPI_UV_ERROR_H_
#include <uv.h>
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
/**
* Error code.
*/
class Error {
public:
- Error() : m_err(UV_UNKNOWN) {}
+ Error() = default;
explicit Error(int err) : m_err(err) {}
/**
@@ -42,10 +38,9 @@
const char* name() const { return uv_err_name(m_err); }
private:
- int m_err;
+ int m_err{UV_UNKNOWN};
};
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv
#endif // WPIUTIL_WPI_UV_ERROR_H_
diff --git a/wpiutil/src/main/native/include/wpi/uv/FsEvent.h b/wpiutil/src/main/native/include/wpi/uv/FsEvent.h
index cf91848..0ed2dcb 100644
--- a/wpiutil/src/main/native/include/wpi/uv/FsEvent.h
+++ b/wpiutil/src/main/native/include/wpi/uv/FsEvent.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UV_FSEVENT_H_
#define WPIUTIL_WPI_UV_FSEVENT_H_
@@ -12,13 +9,12 @@
#include <memory>
#include <string>
+#include <string_view>
#include "wpi/Signal.h"
-#include "wpi/Twine.h"
#include "wpi/uv/Handle.h"
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
class Loop;
@@ -55,7 +51,7 @@
* @param events Bitmask of event flags. Only UV_FS_EVENT_RECURSIVE is
* supported (and only on OSX and Windows).
*/
- void Start(const Twine& path, unsigned int flags = 0);
+ void Start(std::string_view path, unsigned int flags = 0);
/**
* Stop watching for changes.
@@ -78,7 +74,6 @@
sig::Signal<const char*, int> fsEvent;
};
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv
#endif // WPIUTIL_WPI_UV_FSEVENT_H_
diff --git a/wpiutil/src/main/native/include/wpi/uv/GetAddrInfo.h b/wpiutil/src/main/native/include/wpi/uv/GetAddrInfo.h
index deb4a24..18d4f32 100644
--- a/wpiutil/src/main/native/include/wpi/uv/GetAddrInfo.h
+++ b/wpiutil/src/main/native/include/wpi/uv/GetAddrInfo.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UV_GETADDRINFO_H_
#define WPIUTIL_WPI_UV_GETADDRINFO_H_
@@ -12,13 +9,13 @@
#include <functional>
#include <memory>
+#include <string_view>
+#include <utility>
#include "wpi/Signal.h"
-#include "wpi/Twine.h"
#include "wpi/uv/Request.h"
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
class Loop;
@@ -44,7 +41,7 @@
* request when the resolution completes. HandleError() is called on the
* request if any errors occur.
*
- * Either node or service may be null (`Twine::createNull()`) but not both.
+ * Either node or service may be empty but not both.
*
* @param loop Event loop
* @param req request
@@ -54,7 +51,7 @@
* type constraints.
*/
void GetAddrInfo(Loop& loop, const std::shared_ptr<GetAddrInfoReq>& req,
- const Twine& node, const Twine& service = Twine::createNull(),
+ std::string_view node, std::string_view service = {},
const addrinfo* hints = nullptr);
/**
@@ -62,7 +59,7 @@
* request when the resolution completes. HandleError() is called on the
* request if any errors occur.
*
- * Either node or service may be null (`Twine::createNull()`) but not both.
+ * Either node or service may be empty but not both.
*
* @param loop Event loop
* @param req request
@@ -73,8 +70,7 @@
*/
inline void GetAddrInfo(const std::shared_ptr<Loop>& loop,
const std::shared_ptr<GetAddrInfoReq>& req,
- const Twine& node,
- const Twine& service = Twine::createNull(),
+ std::string_view node, std::string_view service = {},
const addrinfo* hints = nullptr) {
GetAddrInfo(*loop, req, node, service, hints);
}
@@ -84,7 +80,7 @@
* completes, and errors are forwarded to the loop. This is a convenience
* wrapper.
*
- * Either node or service may be null (`Twine::createNull()`) but not both.
+ * Either node or service may be empty but not both.
*
* @param loop Event loop
* @param callback Callback function to call when resolution completes
@@ -94,7 +90,7 @@
* type constraints.
*/
void GetAddrInfo(Loop& loop, std::function<void(const addrinfo&)> callback,
- const Twine& node, const Twine& service = Twine::createNull(),
+ std::string_view node, std::string_view service = {},
const addrinfo* hints = nullptr);
/**
@@ -102,7 +98,7 @@
* completes, and errors are forwarded to the loop. This is a convenience
* wrapper.
*
- * Either node or service may be null (`Twine::createNull()`) but not both.
+ * Either node or service may be empty but not both.
*
* @param loop Event loop
* @param callback Callback function to call when resolution completes
@@ -113,13 +109,11 @@
*/
inline void GetAddrInfo(const std::shared_ptr<Loop>& loop,
std::function<void(const addrinfo&)> callback,
- const Twine& node,
- const Twine& service = Twine::createNull(),
+ std::string_view node, std::string_view service = {},
const addrinfo* hints = nullptr) {
- GetAddrInfo(*loop, callback, node, service, hints);
+ GetAddrInfo(*loop, std::move(callback), node, service, hints);
}
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv
#endif // WPIUTIL_WPI_UV_GETADDRINFO_H_
diff --git a/wpiutil/src/main/native/include/wpi/uv/GetNameInfo.h b/wpiutil/src/main/native/include/wpi/uv/GetNameInfo.h
index d6f4d62..d35dd95 100644
--- a/wpiutil/src/main/native/include/wpi/uv/GetNameInfo.h
+++ b/wpiutil/src/main/native/include/wpi/uv/GetNameInfo.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UV_GETNAMEINFO_H_
#define WPIUTIL_WPI_UV_GETNAMEINFO_H_
@@ -12,13 +9,13 @@
#include <functional>
#include <memory>
+#include <string_view>
+#include <utility>
#include "wpi/Signal.h"
-#include "wpi/Twine.h"
#include "wpi/uv/Request.h"
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
class Loop;
@@ -94,7 +91,7 @@
inline void GetNameInfo(const std::shared_ptr<Loop>& loop,
std::function<void(const char*, const char*)> callback,
const sockaddr& addr, int flags = 0) {
- GetNameInfo(*loop, callback, addr, flags);
+ GetNameInfo(*loop, std::move(callback), addr, flags);
}
/**
@@ -109,7 +106,7 @@
* @param flags Optional flags to modify the behavior of `getnameinfo`.
*/
void GetNameInfo4(Loop& loop, const std::shared_ptr<GetNameInfoReq>& req,
- const Twine& ip, unsigned int port, int flags = 0);
+ std::string_view ip, unsigned int port, int flags = 0);
/**
* Asynchronous IPv4 getnameinfo(3). HandleResolvedName() is called on the
@@ -124,7 +121,8 @@
*/
inline void GetNameInfo4(const std::shared_ptr<Loop>& loop,
const std::shared_ptr<GetNameInfoReq>& req,
- const Twine& ip, unsigned int port, int flags = 0) {
+ std::string_view ip, unsigned int port,
+ int flags = 0) {
return GetNameInfo4(*loop, req, ip, port, flags);
}
@@ -140,7 +138,7 @@
*/
void GetNameInfo4(Loop& loop,
std::function<void(const char*, const char*)> callback,
- const Twine& ip, unsigned int port, int flags = 0);
+ std::string_view ip, unsigned int port, int flags = 0);
/**
* Asynchronous IPv4 getnameinfo(3). The callback is called when the resolution
@@ -155,8 +153,9 @@
*/
inline void GetNameInfo4(const std::shared_ptr<Loop>& loop,
std::function<void(const char*, const char*)> callback,
- const Twine& ip, unsigned int port, int flags = 0) {
- return GetNameInfo4(*loop, callback, ip, port, flags);
+ std::string_view ip, unsigned int port,
+ int flags = 0) {
+ return GetNameInfo4(*loop, std::move(callback), ip, port, flags);
}
/**
@@ -171,7 +170,7 @@
* @param flags Optional flags to modify the behavior of `getnameinfo`.
*/
void GetNameInfo6(Loop& loop, const std::shared_ptr<GetNameInfoReq>& req,
- const Twine& ip, unsigned int port, int flags = 0);
+ std::string_view ip, unsigned int port, int flags = 0);
/**
* Asynchronous IPv6 getnameinfo(3). HandleResolvedName() is called on the
@@ -186,7 +185,8 @@
*/
inline void GetNameInfo6(const std::shared_ptr<Loop>& loop,
const std::shared_ptr<GetNameInfoReq>& req,
- const Twine& ip, unsigned int port, int flags = 0) {
+ std::string_view ip, unsigned int port,
+ int flags = 0) {
GetNameInfo6(*loop, req, ip, port, flags);
}
@@ -203,7 +203,7 @@
*/
void GetNameInfo6(Loop& loop,
std::function<void(const char*, const char*)> callback,
- const Twine& ip, unsigned int port, int flags = 0);
+ std::string_view ip, unsigned int port, int flags = 0);
/**
* Asynchronous IPv6 getnameinfo(3). The callback is called when the resolution
@@ -218,11 +218,11 @@
*/
inline void GetNameInfo6(const std::shared_ptr<Loop>& loop,
std::function<void(const char*, const char*)> callback,
- const Twine& ip, unsigned int port, int flags = 0) {
- return GetNameInfo6(*loop, callback, ip, port, flags);
+ std::string_view ip, unsigned int port,
+ int flags = 0) {
+ return GetNameInfo6(*loop, std::move(callback), ip, port, flags);
}
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv
#endif // WPIUTIL_WPI_UV_GETNAMEINFO_H_
diff --git a/wpiutil/src/main/native/include/wpi/uv/Handle.h b/wpiutil/src/main/native/include/wpi/uv/Handle.h
index 9d91d37..b61cd81 100644
--- a/wpiutil/src/main/native/include/wpi/uv/Handle.h
+++ b/wpiutil/src/main/native/include/wpi/uv/Handle.h
@@ -1,27 +1,24 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UV_HANDLE_H_
#define WPIUTIL_WPI_UV_HANDLE_H_
#include <uv.h>
+#include <cstdlib>
#include <functional>
#include <memory>
+#include <string_view>
#include <utility>
#include "wpi/Signal.h"
-#include "wpi/StringRef.h"
#include "wpi/uv/Buffer.h"
#include "wpi/uv/Error.h"
#include "wpi/uv/Loop.h"
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
/**
* Handle.
@@ -53,7 +50,7 @@
/**
* Get the name of the type of the handle. E.g. "pipe" for pipe handles.
*/
- StringRef GetTypeName() const noexcept {
+ std::string_view GetTypeName() const noexcept {
return uv_handle_type_name(m_uv_handle->type);
}
@@ -194,8 +191,8 @@
*/
void SetBufferAllocator(std::function<Buffer(size_t)> alloc,
std::function<void(Buffer&)> dealloc) {
- m_allocBuf = alloc;
- m_freeBuf = dealloc;
+ m_allocBuf = std::move(alloc);
+ m_freeBuf = std::move(dealloc);
}
/**
@@ -252,7 +249,9 @@
template <typename F, typename... Args>
bool Invoke(F&& f, Args&&... args) const {
auto err = std::forward<F>(f)(std::forward<Args>(args)...);
- if (err < 0) ReportError(err);
+ if (err < 0) {
+ ReportError(err);
+ }
return err == 0;
}
@@ -290,10 +289,9 @@
}
protected:
- HandleImpl() : Handle{reinterpret_cast<uv_handle_t*>(new U)} {}
+ HandleImpl() : Handle{static_cast<uv_handle_t*>(std::malloc(sizeof(U)))} {}
};
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv
#endif // WPIUTIL_WPI_UV_HANDLE_H_
diff --git a/wpiutil/src/main/native/include/wpi/uv/Idle.h b/wpiutil/src/main/native/include/wpi/uv/Idle.h
index e8278b2..869d43d 100644
--- a/wpiutil/src/main/native/include/wpi/uv/Idle.h
+++ b/wpiutil/src/main/native/include/wpi/uv/Idle.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UV_IDLE_H_
#define WPIUTIL_WPI_UV_IDLE_H_
@@ -15,8 +12,7 @@
#include "wpi/Signal.h"
#include "wpi/uv/Handle.h"
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
class Loop;
@@ -73,7 +69,6 @@
sig::Signal<> idle;
};
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv
#endif // WPIUTIL_WPI_UV_IDLE_H_
diff --git a/wpiutil/src/main/native/include/wpi/uv/Loop.h b/wpiutil/src/main/native/include/wpi/uv/Loop.h
index 2b3b27f..9693053 100644
--- a/wpiutil/src/main/native/include/wpi/uv/Loop.h
+++ b/wpiutil/src/main/native/include/wpi/uv/Loop.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UV_LOOP_H_
#define WPIUTIL_WPI_UV_LOOP_H_
@@ -18,10 +15,10 @@
#include <utility>
#include "wpi/Signal.h"
+#include "wpi/function_ref.h"
#include "wpi/uv/Error.h"
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
class Handle;
@@ -175,7 +172,7 @@
*
* @param callback A function to be invoked once for each active handle.
*/
- void Walk(std::function<void(Handle&)> callback);
+ void Walk(function_ref<void(Handle&)> callback);
/**
* Reinitialize any kernel state necessary in the child process after
@@ -251,7 +248,6 @@
std::atomic<std::thread::id> m_tid;
};
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv
#endif // WPIUTIL_WPI_UV_LOOP_H_
diff --git a/wpiutil/src/main/native/include/wpi/uv/NetworkStream.h b/wpiutil/src/main/native/include/wpi/uv/NetworkStream.h
index 05c1c92..faac9fe 100644
--- a/wpiutil/src/main/native/include/wpi/uv/NetworkStream.h
+++ b/wpiutil/src/main/native/include/wpi/uv/NetworkStream.h
@@ -1,23 +1,20 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UV_NETWORKSTREAM_H_
#define WPIUTIL_WPI_UV_NETWORKSTREAM_H_
#include <uv.h>
+#include <cstdlib>
#include <functional>
#include <memory>
#include "wpi/Signal.h"
#include "wpi/uv/Stream.h"
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
class NetworkStream;
@@ -147,10 +144,10 @@
}
protected:
- NetworkStreamImpl() : NetworkStream{reinterpret_cast<uv_stream_t*>(new U)} {}
+ NetworkStreamImpl()
+ : NetworkStream{static_cast<uv_stream_t*>(std::malloc(sizeof(U)))} {}
};
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv
#endif // WPIUTIL_WPI_UV_NETWORKSTREAM_H_
diff --git a/wpiutil/src/main/native/include/wpi/uv/Pipe.h b/wpiutil/src/main/native/include/wpi/uv/Pipe.h
index 72fc23b..e223268 100644
--- a/wpiutil/src/main/native/include/wpi/uv/Pipe.h
+++ b/wpiutil/src/main/native/include/wpi/uv/Pipe.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UV_PIPE_H_
#define WPIUTIL_WPI_UV_PIPE_H_
@@ -13,12 +10,11 @@
#include <functional>
#include <memory>
#include <string>
+#include <string_view>
-#include "wpi/Twine.h"
#include "wpi/uv/NetworkStream.h"
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
class Loop;
class PipeConnectReq;
@@ -123,7 +119,7 @@
*
* @param name File path (Unix) or name (Windows).
*/
- void Bind(const Twine& name);
+ void Bind(std::string_view name);
/**
* Connect to the Unix domain socket or the named pipe.
@@ -139,7 +135,8 @@
* @param name File path (Unix) or name (Windows).
* @param req connection request
*/
- void Connect(const Twine& name, const std::shared_ptr<PipeConnectReq>& req);
+ void Connect(std::string_view name,
+ const std::shared_ptr<PipeConnectReq>& req);
/**
* Connect to the Unix domain socket or the named pipe.
@@ -153,7 +150,7 @@
* @param name File path (Unix) or name (Windows).
* @param callback Callback function to call when connection established
*/
- void Connect(const Twine& name, std::function<void()> callback);
+ void Connect(std::string_view name, std::function<void()> callback);
/**
* Get the name of the Unix domain socket or the named pipe.
@@ -206,7 +203,6 @@
}
};
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv
#endif // WPIUTIL_WPI_UV_PIPE_H_
diff --git a/wpiutil/src/main/native/include/wpi/uv/Poll.h b/wpiutil/src/main/native/include/wpi/uv/Poll.h
index fad00e4..6e1e26d 100644
--- a/wpiutil/src/main/native/include/wpi/uv/Poll.h
+++ b/wpiutil/src/main/native/include/wpi/uv/Poll.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UV_POLL_H_
#define WPIUTIL_WPI_UV_POLL_H_
@@ -15,8 +12,7 @@
#include "wpi/Signal.h"
#include "wpi/uv/Handle.h"
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
class Loop;
@@ -120,7 +116,6 @@
std::unique_ptr<ReuseData> m_reuseData;
};
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv
#endif // WPIUTIL_WPI_UV_POLL_H_
diff --git a/wpiutil/src/main/native/include/wpi/uv/Prepare.h b/wpiutil/src/main/native/include/wpi/uv/Prepare.h
index 600922f..88ae2d9 100644
--- a/wpiutil/src/main/native/include/wpi/uv/Prepare.h
+++ b/wpiutil/src/main/native/include/wpi/uv/Prepare.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UV_PREPARE_H_
#define WPIUTIL_WPI_UV_PREPARE_H_
@@ -15,8 +12,7 @@
#include "wpi/Signal.h"
#include "wpi/uv/Handle.h"
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
class Loop;
@@ -64,7 +60,6 @@
sig::Signal<> prepare;
};
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv
#endif // WPIUTIL_WPI_UV_PREPARE_H_
diff --git a/wpiutil/src/main/native/include/wpi/uv/Process.h b/wpiutil/src/main/native/include/wpi/uv/Process.h
index 47d09df..fc2315e 100644
--- a/wpiutil/src/main/native/include/wpi/uv/Process.h
+++ b/wpiutil/src/main/native/include/wpi/uv/Process.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UV_PROCESS_H_
#define WPIUTIL_WPI_UV_PROCESS_H_
@@ -13,15 +10,14 @@
#include <initializer_list>
#include <memory>
#include <string>
+#include <string_view>
-#include "wpi/ArrayRef.h"
#include "wpi/Signal.h"
#include "wpi/SmallVector.h"
-#include "wpi/Twine.h"
+#include "wpi/span.h"
#include "wpi/uv/Handle.h"
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
class Loop;
class Pipe;
@@ -39,9 +35,9 @@
~Process() noexcept override = default;
/**
- * Structure for Spawn() option temporaries. This is a reference type
- * similar to StringRef, so if this value is stored outside of a temporary,
- * be careful about overwriting what it points to.
+ * Structure for Spawn() option temporaries. This is a reference type, so if
+ * this value is stored outside of a temporary, be careful about overwriting
+ * what it points to.
*/
struct Option {
enum Type {
@@ -61,30 +57,24 @@
Option() : m_type(kNone) {}
- /*implicit*/ Option(const char* arg) { // NOLINT(runtime/explicit)
+ /*implicit*/ Option(const char* arg) { // NOLINT
m_data.str = arg;
}
- /*implicit*/ Option(const std::string& arg) { // NOLINT(runtime/explicit)
+ /*implicit*/ Option(const std::string& arg) { // NOLINT
m_data.str = arg.data();
}
- /*implicit*/ Option(StringRef arg) // NOLINT(runtime/explicit)
+ /*implicit*/ Option(std::string_view arg) // NOLINT
: m_strData(arg) {
m_data.str = m_strData.c_str();
}
- /*implicit*/ Option(
- const SmallVectorImpl<char>& arg) // NOLINT(runtime/explicit)
+ /*implicit*/ Option(const SmallVectorImpl<char>& arg) // NOLINT
: m_strData(arg.data(), arg.size()) {
m_data.str = m_strData.c_str();
}
- /*implicit*/ Option(const Twine& arg) // NOLINT(runtime/explicit)
- : m_strData(arg.str()) {
- m_data.str = m_strData.c_str();
- }
-
explicit Option(Type type) : m_type(type) {}
Type m_type = kArg;
@@ -110,9 +100,9 @@
* environment is used.
* @param env environment variable
*/
- static Option Env(const Twine& env) {
+ static Option Env(std::string_view env) {
Option o(Option::kEnv);
- o.m_strData = env.str();
+ o.m_strData = env;
o.m_data.str = o.m_strData.c_str();
return o;
}
@@ -121,9 +111,9 @@
* Set the current working directory for the subprocess.
* @param cwd current working directory
*/
- static Option Cwd(const Twine& cwd) {
+ static Option Cwd(std::string_view cwd) {
Option o(Option::kCwd);
- o.m_strData = cwd.str();
+ o.m_strData = cwd;
o.m_data.str = o.m_strData.c_str();
return o;
}
@@ -241,16 +231,17 @@
* @param file Path pointing to the program to be executed
* @param options Process options
*/
- static std::shared_ptr<Process> SpawnArray(Loop& loop, const Twine& file,
- ArrayRef<Option> options);
+ static std::shared_ptr<Process> SpawnArray(Loop& loop, std::string_view file,
+ span<const Option> options);
static std::shared_ptr<Process> SpawnArray(
- Loop& loop, const Twine& file, std::initializer_list<Option> options) {
- return SpawnArray(loop, file, makeArrayRef(options.begin(), options.end()));
+ Loop& loop, std::string_view file,
+ std::initializer_list<Option> options) {
+ return SpawnArray(loop, file, {options.begin(), options.end()});
}
template <typename... Args>
- static std::shared_ptr<Process> Spawn(Loop& loop, const Twine& file,
+ static std::shared_ptr<Process> Spawn(Loop& loop, std::string_view file,
const Args&... options) {
return SpawnArray(loop, file, {options...});
}
@@ -269,20 +260,20 @@
* @param options Process options
*/
static std::shared_ptr<Process> SpawnArray(const std::shared_ptr<Loop>& loop,
- const Twine& file,
- ArrayRef<Option> options) {
+ std::string_view file,
+ span<const Option> options) {
return SpawnArray(*loop, file, options);
}
static std::shared_ptr<Process> SpawnArray(
- const std::shared_ptr<Loop>& loop, const Twine& file,
+ const std::shared_ptr<Loop>& loop, std::string_view file,
std::initializer_list<Option> options) {
return SpawnArray(*loop, file, options);
}
template <typename... Args>
static std::shared_ptr<Process> Spawn(const std::shared_ptr<Loop>& loop,
- const Twine& file,
+ std::string_view file,
const Args&... options) {
return SpawnArray(*loop, file, {options...});
}
@@ -314,7 +305,6 @@
sig::Signal<int64_t, int> exited;
};
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv
#endif // WPIUTIL_WPI_UV_PROCESS_H_
diff --git a/wpiutil/src/main/native/include/wpi/uv/Request.h b/wpiutil/src/main/native/include/wpi/uv/Request.h
index c33ca83..3f6a19d 100644
--- a/wpiutil/src/main/native/include/wpi/uv/Request.h
+++ b/wpiutil/src/main/native/include/wpi/uv/Request.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UV_REQUEST_H_
#define WPIUTIL_WPI_UV_REQUEST_H_
@@ -15,8 +12,7 @@
#include "wpi/uv/Error.h"
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
/**
* Request. Requests are not moveable or copyable.
@@ -165,7 +161,6 @@
U m_uv_req;
};
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv
#endif // WPIUTIL_WPI_UV_REQUEST_H_
diff --git a/wpiutil/src/main/native/include/wpi/uv/Signal.h b/wpiutil/src/main/native/include/wpi/uv/Signal.h
index 5fe34c6..9abcad3 100644
--- a/wpiutil/src/main/native/include/wpi/uv/Signal.h
+++ b/wpiutil/src/main/native/include/wpi/uv/Signal.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UV_SIGNAL_H_
#define WPIUTIL_WPI_UV_SIGNAL_H_
@@ -15,8 +12,7 @@
#include "wpi/Signal.h"
#include "wpi/uv/Handle.h"
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
class Loop;
@@ -78,7 +74,6 @@
sig::Signal<int> signal;
};
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv
#endif // WPIUTIL_WPI_UV_SIGNAL_H_
diff --git a/wpiutil/src/main/native/include/wpi/uv/Stream.h b/wpiutil/src/main/native/include/wpi/uv/Stream.h
index be8120d..0ade972 100644
--- a/wpiutil/src/main/native/include/wpi/uv/Stream.h
+++ b/wpiutil/src/main/native/include/wpi/uv/Stream.h
@@ -1,27 +1,25 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UV_STREAM_H_
#define WPIUTIL_WPI_UV_STREAM_H_
#include <uv.h>
+#include <cstdlib>
#include <functional>
#include <initializer_list>
#include <memory>
+#include <utility>
-#include "wpi/ArrayRef.h"
#include "wpi/Signal.h"
+#include "wpi/span.h"
#include "wpi/uv/Buffer.h"
#include "wpi/uv/Handle.h"
#include "wpi/uv/Request.h"
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
class Stream;
@@ -129,7 +127,7 @@
* @param bufs The buffers to be written to the stream.
* @param req write request
*/
- void Write(ArrayRef<Buffer> bufs, const std::shared_ptr<WriteReq>& req);
+ void Write(span<const Buffer> bufs, const std::shared_ptr<WriteReq>& req);
/**
* Write data to the stream.
@@ -149,7 +147,7 @@
*/
void Write(std::initializer_list<Buffer> bufs,
const std::shared_ptr<WriteReq>& req) {
- Write(makeArrayRef(bufs.begin(), bufs.end()), req);
+ Write({bufs.begin(), bufs.end()}, req);
}
/**
@@ -165,8 +163,8 @@
* @param bufs The buffers to be written to the stream.
* @param callback Callback function to call when the write completes
*/
- void Write(ArrayRef<Buffer> bufs,
- std::function<void(MutableArrayRef<Buffer>, Error)> callback);
+ void Write(span<const Buffer> bufs,
+ std::function<void(span<Buffer>, Error)> callback);
/**
* Write data to the stream.
@@ -182,8 +180,8 @@
* @param callback Callback function to call when the write completes
*/
void Write(std::initializer_list<Buffer> bufs,
- std::function<void(MutableArrayRef<Buffer>, Error)> callback) {
- Write(makeArrayRef(bufs.begin(), bufs.end()), callback);
+ std::function<void(span<Buffer>, Error)> callback) {
+ Write({bufs.begin(), bufs.end()}, std::move(callback));
}
/**
@@ -196,7 +194,7 @@
* @param bufs The buffers to be written to the stream.
* @return Number of bytes written.
*/
- int TryWrite(ArrayRef<Buffer> bufs);
+ int TryWrite(span<const Buffer> bufs);
/**
* Queue a write request if it can be completed immediately.
@@ -209,7 +207,7 @@
* @return Number of bytes written.
*/
int TryWrite(std::initializer_list<Buffer> bufs) {
- return TryWrite(makeArrayRef(bufs.begin(), bufs.end()));
+ return TryWrite({bufs.begin(), bufs.end()});
}
/**
@@ -296,10 +294,9 @@
}
protected:
- StreamImpl() : Stream{reinterpret_cast<uv_stream_t*>(new U)} {}
+ StreamImpl() : Stream{static_cast<uv_stream_t*>(std::malloc(sizeof(U)))} {}
};
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv
#endif // WPIUTIL_WPI_UV_STREAM_H_
diff --git a/wpiutil/src/main/native/include/wpi/uv/Tcp.h b/wpiutil/src/main/native/include/wpi/uv/Tcp.h
index 56c2b8f..c712f0b 100644
--- a/wpiutil/src/main/native/include/wpi/uv/Tcp.h
+++ b/wpiutil/src/main/native/include/wpi/uv/Tcp.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UV_TCP_H_
#define WPIUTIL_WPI_UV_TCP_H_
@@ -13,12 +10,12 @@
#include <chrono>
#include <functional>
#include <memory>
+#include <string_view>
+#include <utility>
-#include "wpi/Twine.h"
#include "wpi/uv/NetworkStream.h"
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
class Loop;
class TcpConnectReq;
@@ -116,7 +113,7 @@
void Open(uv_os_sock_t sock) { Invoke(&uv_tcp_open, GetRaw(), sock); }
/**
- * Enable/Disable Nagle's algorithm.
+ * Enable no delay operation (turns off Nagle's algorithm).
* @param enable True to enable it, false otherwise.
* @return True in case of success, false otherwise.
*/
@@ -189,7 +186,7 @@
* @param port The port to which to bind.
* @param flags Optional additional flags.
*/
- void Bind(const Twine& ip, unsigned int port, unsigned int flags = 0);
+ void Bind(std::string_view ip, unsigned int port, unsigned int flags = 0);
/**
* Bind the handle to an IPv6 address and port.
@@ -205,7 +202,7 @@
* @param port The port to which to bind.
* @param flags Optional additional flags.
*/
- void Bind6(const Twine& ip, unsigned int port, unsigned int flags = 0);
+ void Bind6(std::string_view ip, unsigned int port, unsigned int flags = 0);
/**
* Get the current address to which the handle is bound.
@@ -262,11 +259,11 @@
void Connect(const sockaddr& addr, std::function<void()> callback);
void Connect(const sockaddr_in& addr, std::function<void()> callback) {
- Connect(reinterpret_cast<const sockaddr&>(addr), callback);
+ Connect(reinterpret_cast<const sockaddr&>(addr), std::move(callback));
}
void Connect(const sockaddr_in6& addr, std::function<void()> callback) {
- Connect(reinterpret_cast<const sockaddr&>(addr), callback);
+ Connect(reinterpret_cast<const sockaddr&>(addr), std::move(callback));
}
/**
@@ -285,7 +282,7 @@
* @param port The port to which to connect to.
* @param req connection request
*/
- void Connect(const Twine& ip, unsigned int port,
+ void Connect(std::string_view ip, unsigned int port,
const std::shared_ptr<TcpConnectReq>& req);
/**
@@ -302,7 +299,7 @@
* @param port The port to which to connect to.
* @param callback Callback function to call when connection established
*/
- void Connect(const Twine& ip, unsigned int port,
+ void Connect(std::string_view ip, unsigned int port,
std::function<void()> callback);
/**
@@ -321,7 +318,7 @@
* @param port The port to which to connect to.
* @param req connection request
*/
- void Connect6(const Twine& ip, unsigned int port,
+ void Connect6(std::string_view ip, unsigned int port,
const std::shared_ptr<TcpConnectReq>& req);
/**
@@ -338,7 +335,7 @@
* @param port The port to which to connect to.
* @param callback Callback function to call when connection established
*/
- void Connect6(const Twine& ip, unsigned int port,
+ void Connect6(std::string_view ip, unsigned int port,
std::function<void()> callback);
private:
@@ -361,7 +358,6 @@
}
};
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv
#endif // WPIUTIL_WPI_UV_TCP_H_
diff --git a/wpiutil/src/main/native/include/wpi/uv/Timer.h b/wpiutil/src/main/native/include/wpi/uv/Timer.h
index bdafee3..00363a5 100644
--- a/wpiutil/src/main/native/include/wpi/uv/Timer.h
+++ b/wpiutil/src/main/native/include/wpi/uv/Timer.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UV_TIMER_H_
#define WPIUTIL_WPI_UV_TIMER_H_
@@ -13,12 +10,12 @@
#include <chrono>
#include <functional>
#include <memory>
+#include <utility>
#include "wpi/Signal.h"
#include "wpi/uv/Handle.h"
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
class Loop;
@@ -69,7 +66,7 @@
*/
static void SingleShot(const std::shared_ptr<Loop>& loop, Time timeout,
std::function<void()> func) {
- return SingleShot(*loop, timeout, func);
+ return SingleShot(*loop, timeout, std::move(func));
}
/**
@@ -133,7 +130,6 @@
sig::Signal<> timeout;
};
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv
#endif // WPIUTIL_WPI_UV_TIMER_H_
diff --git a/wpiutil/src/main/native/include/wpi/uv/Tty.h b/wpiutil/src/main/native/include/wpi/uv/Tty.h
index a15d8eb..bb0a6db 100644
--- a/wpiutil/src/main/native/include/wpi/uv/Tty.h
+++ b/wpiutil/src/main/native/include/wpi/uv/Tty.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UV_TTY_H_
#define WPIUTIL_WPI_UV_TTY_H_
@@ -15,8 +12,7 @@
#include "wpi/uv/Stream.h"
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
class Loop;
class Tty;
@@ -62,7 +58,9 @@
*/
void SetMode(uv_tty_mode_t mode) {
int err = uv_tty_set_mode(GetRaw(), mode);
- if (err < 0) ReportError(err);
+ if (err < 0) {
+ ReportError(err);
+ }
}
/**
@@ -82,7 +80,6 @@
}
};
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv
#endif // WPIUTIL_WPI_UV_TTY_H_
diff --git a/wpiutil/src/main/native/include/wpi/uv/Udp.h b/wpiutil/src/main/native/include/wpi/uv/Udp.h
index 2544c50..71fab13 100644
--- a/wpiutil/src/main/native/include/wpi/uv/Udp.h
+++ b/wpiutil/src/main/native/include/wpi/uv/Udp.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UV_UDP_H_
#define WPIUTIL_WPI_UV_UDP_H_
@@ -12,15 +9,15 @@
#include <functional>
#include <memory>
+#include <string_view>
+#include <utility>
-#include "wpi/ArrayRef.h"
#include "wpi/Signal.h"
-#include "wpi/Twine.h"
+#include "wpi/span.h"
#include "wpi/uv/Handle.h"
#include "wpi/uv/Request.h"
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
class Loop;
class Udp;
@@ -104,7 +101,7 @@
* @param port The port to which to bind.
* @param flags Optional additional flags.
*/
- void Bind(const Twine& ip, unsigned int port, unsigned int flags = 0);
+ void Bind(std::string_view ip, unsigned int port, unsigned int flags = 0);
/**
* Bind the handle to an IPv6 address and port.
@@ -113,7 +110,7 @@
* @param port The port to which to bind.
* @param flags Optional additional flags.
*/
- void Bind6(const Twine& ip, unsigned int port, unsigned int flags = 0);
+ void Bind6(std::string_view ip, unsigned int port, unsigned int flags = 0);
/**
* Associate the handle to a remote address and port, so every message sent
@@ -140,7 +137,7 @@
* @param ip The address to which to bind.
* @param port The port to which to bind.
*/
- void Connect(const Twine& ip, unsigned int port);
+ void Connect(std::string_view ip, unsigned int port);
/**
* Associate the handle to an IPv6 address and port, so every message sent
@@ -150,7 +147,7 @@
* @param port The port to which to bind.
* @param flags Optional additional flags.
*/
- void Connect6(const Twine& ip, unsigned int port);
+ void Connect6(std::string_view ip, unsigned int port);
/**
* Get the remote IP and port on connected UDP handles.
@@ -171,8 +168,8 @@
* @param interfaceAddr Interface address
* @param membership Should be UV_JOIN_GROUP or UV_LEAVE_GROUP
*/
- void SetMembership(const Twine& multicastAddr, const Twine& interfaceAddr,
- uv_membership membership);
+ void SetMembership(std::string_view multicastAddr,
+ std::string_view interfaceAddr, uv_membership membership);
/**
* Set IP multicast loop flag. Makes multicast packets loop back to local
@@ -198,7 +195,7 @@
*
* @param interfaceAddr Interface address
*/
- void SetMulticastInterface(const Twine& interfaceAddr);
+ void SetMulticastInterface(std::string_view interfaceAddr);
/**
* Set broadcast on or off.
@@ -234,15 +231,15 @@
* @param bufs The buffers to be written to the stream.
* @param req write request
*/
- void Send(const sockaddr& addr, ArrayRef<Buffer> bufs,
+ void Send(const sockaddr& addr, span<const Buffer> bufs,
const std::shared_ptr<UdpSendReq>& req);
- void Send(const sockaddr_in& addr, ArrayRef<Buffer> bufs,
+ void Send(const sockaddr_in& addr, span<const Buffer> bufs,
const std::shared_ptr<UdpSendReq>& req) {
Send(reinterpret_cast<const sockaddr&>(addr), bufs, req);
}
- void Send(const sockaddr_in6& addr, ArrayRef<Buffer> bufs,
+ void Send(const sockaddr_in6& addr, span<const Buffer> bufs,
const std::shared_ptr<UdpSendReq>& req) {
Send(reinterpret_cast<const sockaddr&>(addr), bufs, req);
}
@@ -254,7 +251,7 @@
* @param bufs The buffers to be written to the stream.
* @param req write request
*/
- void Send(ArrayRef<Buffer> bufs, const std::shared_ptr<UdpSendReq>& req);
+ void Send(span<const Buffer> bufs, const std::shared_ptr<UdpSendReq>& req);
/**
* Send data over the UDP socket. If the socket has not previously been bound
@@ -273,17 +270,17 @@
* @param bufs The buffers to be sent.
* @param callback Callback function to call when the data has been sent.
*/
- void Send(const sockaddr& addr, ArrayRef<Buffer> bufs,
- std::function<void(MutableArrayRef<Buffer>, Error)> callback);
+ void Send(const sockaddr& addr, span<const Buffer> bufs,
+ std::function<void(span<Buffer>, Error)> callback);
- void Send(const sockaddr_in& addr, ArrayRef<Buffer> bufs,
- std::function<void(MutableArrayRef<Buffer>, Error)> callback) {
- Send(reinterpret_cast<const sockaddr&>(addr), bufs, callback);
+ void Send(const sockaddr_in& addr, span<const Buffer> bufs,
+ std::function<void(span<Buffer>, Error)> callback) {
+ Send(reinterpret_cast<const sockaddr&>(addr), bufs, std::move(callback));
}
- void Send(const sockaddr_in6& addr, ArrayRef<Buffer> bufs,
- std::function<void(MutableArrayRef<Buffer>, Error)> callback) {
- Send(reinterpret_cast<const sockaddr&>(addr), bufs, callback);
+ void Send(const sockaddr_in6& addr, span<const Buffer> bufs,
+ std::function<void(span<Buffer>, Error)> callback) {
+ Send(reinterpret_cast<const sockaddr&>(addr), bufs, std::move(callback));
}
/**
@@ -293,8 +290,8 @@
* @param bufs The buffers to be written to the stream.
* @param callback Callback function to call when the data has been sent.
*/
- void Send(ArrayRef<Buffer> bufs,
- std::function<void(MutableArrayRef<Buffer>, Error)> callback);
+ void Send(span<const Buffer> bufs,
+ std::function<void(span<Buffer>, Error)> callback);
/**
* Same as Send(), but won't queue a send request if it can't be completed
@@ -305,7 +302,7 @@
* @param bufs The buffers to be send.
* @return Number of bytes sent.
*/
- int TrySend(const sockaddr& addr, ArrayRef<Buffer> bufs) {
+ int TrySend(const sockaddr& addr, span<const Buffer> bufs) {
int val = uv_udp_try_send(GetRaw(), bufs.data(),
static_cast<unsigned>(bufs.size()), &addr);
if (val < 0) {
@@ -315,11 +312,11 @@
return val;
}
- int TrySend(const sockaddr_in& addr, ArrayRef<Buffer> bufs) {
+ int TrySend(const sockaddr_in& addr, span<const Buffer> bufs) {
return TrySend(reinterpret_cast<const sockaddr&>(addr), bufs);
}
- int TrySend(const sockaddr_in6& addr, ArrayRef<Buffer> bufs) {
+ int TrySend(const sockaddr_in6& addr, span<const Buffer> bufs) {
return TrySend(reinterpret_cast<const sockaddr&>(addr), bufs);
}
@@ -330,7 +327,7 @@
* @param bufs The buffers to be written to the stream.
* @return Number of bytes sent.
*/
- int TrySend(ArrayRef<Buffer> bufs) {
+ int TrySend(span<const Buffer> bufs) {
int val = uv_udp_try_send(GetRaw(), bufs.data(),
static_cast<unsigned>(bufs.size()), nullptr);
if (val < 0) {
@@ -376,7 +373,6 @@
sig::Signal<Buffer&, size_t, const sockaddr&, unsigned> received;
};
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv
#endif // WPIUTIL_WPI_UV_UDP_H_
diff --git a/wpiutil/src/main/native/include/wpi/uv/Work.h b/wpiutil/src/main/native/include/wpi/uv/Work.h
index bf061fb..5a3a5d2 100644
--- a/wpiutil/src/main/native/include/wpi/uv/Work.h
+++ b/wpiutil/src/main/native/include/wpi/uv/Work.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UV_WORK_H_
#define WPIUTIL_WPI_UV_WORK_H_
@@ -12,12 +9,12 @@
#include <functional>
#include <memory>
+#include <utility>
#include "wpi/Signal.h"
#include "wpi/uv/Request.h"
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
class Loop;
@@ -88,10 +85,9 @@
inline void QueueWork(const std::shared_ptr<Loop>& loop,
std::function<void()> work,
std::function<void()> afterWork) {
- QueueWork(*loop, work, afterWork);
+ QueueWork(*loop, std::move(work), std::move(afterWork));
}
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv
#endif // WPIUTIL_WPI_UV_WORK_H_
diff --git a/wpiutil/src/main/native/include/wpi/uv/util.h b/wpiutil/src/main/native/include/wpi/uv/util.h
index 14a021d..01dec0b 100644
--- a/wpiutil/src/main/native/include/wpi/uv/util.h
+++ b/wpiutil/src/main/native/include/wpi/uv/util.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2020 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. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UV_UTIL_H_
#define WPIUTIL_WPI_UV_UTIL_H_
@@ -11,11 +8,9 @@
#include <uv.h>
#include <cstring>
+#include <string_view>
-#include "wpi/Twine.h"
-
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
/**
* Convert a binary structure containing an IPv4 address to a string.
@@ -66,10 +61,12 @@
*/
template <typename T>
int AddrToName(const sockaddr_storage& addr, T* ip, unsigned int* port) {
- if (addr.ss_family == AF_INET)
+ if (addr.ss_family == AF_INET) {
return AddrToName(reinterpret_cast<const sockaddr_in&>(addr), ip, port);
- if (addr.ss_family == AF_INET6)
+ }
+ if (addr.ss_family == AF_INET6) {
return AddrToName(reinterpret_cast<const sockaddr_in6&>(addr), ip, port);
+ }
char name[1];
ip->assign(name, name);
return -1;
@@ -85,10 +82,11 @@
int AddrToName(const in_addr& addr, T* ip) {
char name[128];
int err = uv_inet_ntop(AF_INET, &addr, name, 128);
- if (err == 0)
+ if (err == 0) {
ip->assign(name, name + std::strlen(name));
- else
+ } else {
ip->assign(name, name);
+ }
return err;
}
@@ -102,10 +100,11 @@
int AddrToName(const in6_addr& addr, T* ip) {
char name[128];
int err = uv_inet_ntop(AF_INET6, &addr, name, 128);
- if (err == 0)
+ if (err == 0) {
ip->assign(name, name + std::strlen(name));
- else
+ } else {
ip->assign(name, name);
+ }
return err;
}
@@ -116,7 +115,7 @@
* @param addr Output binary structure
* @return Error (same as `uv_ip4_addr()`).
*/
-int NameToAddr(const Twine& ip, unsigned int port, sockaddr_in* addr);
+int NameToAddr(std::string_view ip, unsigned int port, sockaddr_in* addr);
/**
* Convert a string containing an IPv6 address to a binary structure.
@@ -125,7 +124,7 @@
* @param addr Output binary structure
* @return Error (same as `uv_ip6_addr()`).
*/
-int NameToAddr(const Twine& ip, unsigned int port, sockaddr_in6* addr);
+int NameToAddr(std::string_view ip, unsigned int port, sockaddr_in6* addr);
/**
* Convert a string containing an IPv4 address to binary format.
@@ -133,7 +132,7 @@
* @param addr Output binary
* @return Error (same as `uv_inet_pton()`).
*/
-int NameToAddr(const Twine& ip, in_addr* addr);
+int NameToAddr(std::string_view ip, in_addr* addr);
/**
* Convert a string containing an IPv6 address to binary format.
@@ -141,9 +140,8 @@
* @param addr Output binary
* @return Error (same as `uv_inet_pton()`).
*/
-int NameToAddr(const Twine& ip, in6_addr* addr);
+int NameToAddr(std::string_view ip, in6_addr* addr);
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv
#endif // WPIUTIL_WPI_UV_UTIL_H_
diff --git a/wpiutil/src/main/native/unix/Demangle.cpp b/wpiutil/src/main/native/unix/Demangle.cpp
index 3453084..69404ed 100644
--- a/wpiutil/src/main/native/unix/Demangle.cpp
+++ b/wpiutil/src/main/native/unix/Demangle.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2020 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. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/Demangle.h"
@@ -15,14 +12,12 @@
namespace wpi {
-std::string Demangle(const Twine& mangledSymbol) {
- SmallString<128> buf;
+std::string Demangle(std::string_view mangledSymbol) {
+ SmallString<128> buf{mangledSymbol};
size_t length;
int32_t status;
- char* symbol =
- abi::__cxa_demangle(mangledSymbol.toNullTerminatedStringRef(buf).data(),
- nullptr, &length, &status);
+ char* symbol = abi::__cxa_demangle(buf.c_str(), nullptr, &length, &status);
if (status == 0) {
std::string rv{symbol};
std::free(symbol);
@@ -30,7 +25,7 @@
}
// If everything else failed, just return the mangled symbol
- return mangledSymbol.str();
+ return std::string{mangledSymbol};
}
} // namespace wpi
diff --git a/wpiutil/src/main/native/unix/StackTrace.cpp b/wpiutil/src/main/native/unix/StackTrace.cpp
index 004eb39..598c9da 100644
--- a/wpiutil/src/main/native/unix/StackTrace.cpp
+++ b/wpiutil/src/main/native/unix/StackTrace.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2020 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. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/StackTrace.h"
@@ -11,7 +8,7 @@
#include "wpi/Demangle.h"
#include "wpi/SmallString.h"
-#include "wpi/StringRef.h"
+#include "wpi/StringExtras.h"
#include "wpi/raw_ostream.h"
namespace wpi {
@@ -27,16 +24,18 @@
// Only print recursive functions once in a row.
if (i == 0 || stackTrace[i] != stackTrace[i - 1]) {
// extract just function name from "pathToExe(functionName+offset)"
- StringRef sym{mangledSymbols[i]};
- sym = sym.split('(').second;
- sym = sym.split('+').first;
- trace << "\tat " << Demangle(sym) << "\n";
+ std::string_view sym = split(mangledSymbols[i], '(').second;
+ std::string_view offset;
+ std::tie(sym, offset) = split(sym, '+');
+ std::string_view addr;
+ std::tie(offset, addr) = split(offset, ')');
+ trace << "\tat " << Demangle(sym) << " + " << offset << addr << "\n";
}
}
std::free(mangledSymbols);
- return trace.str();
+ return std::string{trace.str()};
}
} // namespace wpi
diff --git a/wpiutil/src/main/native/windows/Demangle.cpp b/wpiutil/src/main/native/windows/Demangle.cpp
index a1560d6..1b0a8a7 100644
--- a/wpiutil/src/main/native/windows/Demangle.cpp
+++ b/wpiutil/src/main/native/windows/Demangle.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2020 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. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/Demangle.h"
@@ -18,15 +15,15 @@
namespace wpi {
-std::string Demangle(const Twine& mangledSymbol) {
+std::string Demangle(std::string_view mangledSymbol) {
static wpi::mutex m;
std::scoped_lock lock(m);
- SmallString<128> buf;
+ SmallString<128> buf{mangledSymbol};
char buffer[256];
- DWORD sz =
- UnDecorateSymbolName(mangledSymbol.toNullTerminatedStringRef(buf).data(),
- buffer, sizeof(buffer), UNDNAME_COMPLETE);
- if (sz == 0) return mangledSymbol.str();
+ DWORD sz = UnDecorateSymbolName(buf.c_str(), buffer, sizeof(buffer),
+ UNDNAME_COMPLETE);
+ if (sz == 0)
+ return std::string{mangledSymbol};
return std::string(buffer, sz);
}
diff --git a/wpiutil/src/main/native/windows/StackTrace.cpp b/wpiutil/src/main/native/windows/StackTrace.cpp
index 86bbdff..3e3ccfa 100644
--- a/wpiutil/src/main/native/windows/StackTrace.cpp
+++ b/wpiutil/src/main/native/windows/StackTrace.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-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. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/StackTrace.h"
diff --git a/wpiutil/src/netconsoleServer/native/cpp/main.cpp b/wpiutil/src/netconsoleServer/native/cpp/main.cpp
index ed45eeb..c43a670 100644
--- a/wpiutil/src/netconsoleServer/native/cpp/main.cpp
+++ b/wpiutil/src/netconsoleServer/native/cpp/main.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
#ifdef __APPLE__
#include <util.h>
@@ -11,9 +8,12 @@
#include <pty.h>
#endif
+#include <cstdio>
+
+#include "fmt/format.h"
#include "wpi/MathExtras.h"
#include "wpi/SmallVector.h"
-#include "wpi/raw_ostream.h"
+#include "wpi/StringExtras.h"
#include "wpi/raw_uv_ostream.h"
#include "wpi/timestamp.h"
#include "wpi/uv/Loop.h"
@@ -33,9 +33,9 @@
wpi::SmallVectorImpl<uv::Buffer>& bufs, bool tcp,
uint16_t tcpSeq) {
// scan for last newline
- wpi::StringRef str(buf.base, len);
+ std::string_view str(buf.base, len);
size_t idx = str.rfind('\n');
- if (idx == wpi::StringRef::npos) {
+ if (idx == std::string_view::npos) {
// no newline yet, just keep appending to remainder
rem += str;
return false;
@@ -43,7 +43,7 @@
// build output
wpi::raw_uv_ostream out(bufs, 4096);
- wpi::StringRef toCopy = str.slice(0, idx + 1);
+ 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);
@@ -57,12 +57,12 @@
static_cast<uint8_t>(ts & 0xff),
static_cast<uint8_t>((tcpSeq >> 8) & 0xff),
static_cast<uint8_t>(tcpSeq & 0xff)};
- out << wpi::ArrayRef<uint8_t>(header);
+ out << wpi::span<const uint8_t>(header);
}
out << rem << toCopy;
// reset remainder
- rem = str.slice(idx + 1, wpi::StringRef::npos);
+ rem = wpi::slice(str, idx + 1, std::string_view::npos);
return true;
}
@@ -81,11 +81,15 @@
uv::Buffer& buf, size_t len) {
// build buffers
wpi::SmallVector<uv::Buffer, 4> bufs;
- if (!NewlineBuffer(*rem, buf, len, bufs, false, 0)) return;
+ if (!NewlineBuffer(*rem, buf, len, bufs, false, 0)) {
+ return;
+ }
// send output
outPtr->Send(addr, bufs, [](auto bufs2, uv::Error) {
- for (auto buf : bufs2) buf.Deallocate();
+ for (auto buf : bufs2) {
+ buf.Deallocate();
+ }
});
},
out);
@@ -101,12 +105,15 @@
uv::Buffer& buf, size_t len) {
// build buffers
wpi::SmallVector<uv::Buffer, 4> bufs;
- if (!NewlineBuffer(data->rem, buf, len, bufs, true, data->seq++))
+ if (!NewlineBuffer(data->rem, buf, len, bufs, true, data->seq++)) {
return;
+ }
// send output
outPtr->Write(bufs, [](auto bufs2, uv::Error) {
- for (auto buf : bufs2) buf.Deallocate();
+ for (auto buf : bufs2) {
+ buf.Deallocate();
+ }
});
},
out);
@@ -116,8 +123,10 @@
in.data.connect([out](uv::Buffer& buf, size_t len) {
uv::Buffer buf2 = buf.Dup();
buf2.len = len;
- out->Write(buf2, [](auto bufs, uv::Error) {
- for (auto buf : bufs) buf.Deallocate();
+ out->Write({buf2}, [](auto bufs, uv::Error) {
+ for (auto buf : bufs) {
+ buf.Deallocate();
+ }
});
});
}
@@ -130,24 +139,26 @@
bool err = false;
while (programArgc < argc && argv[programArgc][0] == '-') {
- if (wpi::StringRef(argv[programArgc]) == "-u") {
+ if (std::string_view(argv[programArgc]) == "-u") {
useUdp = true;
- } else if (wpi::StringRef(argv[programArgc]) == "-b") {
+ } else if (std::string_view(argv[programArgc]) == "-b") {
useUdp = true;
broadcastUdp = true;
} else {
- wpi::errs() << "unrecognized command line option " << argv[programArgc]
- << '\n';
+ fmt::print(stderr, "unrecognized command line option {}\n",
+ argv[programArgc]);
err = true;
}
++programArgc;
}
if (err || (argc - programArgc) < 1) {
- wpi::errs()
- << argv[0] << " [-ub] program [arguments ...]\n"
- << " -u send udp to localhost port 6666 instead of using tcp\n"
- << " -b broadcast udp to port 6666 instead of using tcp\n";
+ std::fputs(argv[0], stderr);
+ std::fputs(
+ " [-ub] program [arguments ...]\n"
+ " -u send udp to localhost port 6666 instead of using tcp\n"
+ " -b broadcast udp to port 6666 instead of using tcp\n",
+ stderr);
return EXIT_FAILURE;
}
@@ -155,7 +166,7 @@
auto loop = uv::Loop::Create();
loop->error.connect(
- [](uv::Error err) { wpi::errs() << "uv ERROR: " << err.str() << '\n'; });
+ [](uv::Error err) { fmt::print(stderr, "uv ERROR: {}\n", err.str()); });
// create pipes to communicate with child
auto stdinPipe = uv::Pipe::Create(loop);
@@ -168,12 +179,20 @@
auto stderrTty = uv::Tty::Create(loop, 2, false);
// pass through our console to child's (bidirectional)
- if (stdinTty) CopyStream(*stdinTty, stdinPipe);
- if (stdoutTty) CopyStream(*stdoutPipe, stdoutTty);
- if (stderrTty) CopyStream(*stderrPipe, stderrTty);
+ if (stdinTty) {
+ CopyStream(*stdinTty, stdinPipe);
+ }
+ if (stdoutTty) {
+ CopyStream(*stdoutPipe, stdoutTty);
+ }
+ if (stderrTty) {
+ CopyStream(*stderrPipe, stderrTty);
+ }
// when our stdin closes, also close child stdin
- if (stdinTty) stdinTty->end.connect([stdinPipe] { stdinPipe->Close(); });
+ if (stdinTty) {
+ stdinTty->end.connect([stdinPipe] { stdinPipe->Close(); });
+ }
if (useUdp) {
auto udp = uv::Udp::Create(loop);
@@ -189,7 +208,9 @@
// when we get a connection, accept it
tcp->connection.connect([srv = tcp.get(), stdoutPipe, stderrPipe] {
auto tcp = srv->Accept();
- if (!tcp) return;
+ if (!tcp) {
+ return;
+ }
// close on error
tcp->error.connect([s = tcp.get()](wpi::uv::Error err) { s->Close(); });
@@ -227,17 +248,21 @@
uv::Process::StdioCreatePipe(2, *stderrPipe, UV_WRITABLE_PIPE));
// pass our args as the child args (argv[1] becomes child argv[0], etc)
- for (int i = programArgc; i < argc; ++i) options.emplace_back(argv[i]);
+ for (int i = programArgc; i < argc; ++i) {
+ options.emplace_back(argv[i]);
+ }
auto proc = uv::Process::SpawnArray(loop, argv[programArgc], options);
if (!proc) {
- wpi::errs() << "could not start subprocess\n";
+ std::fputs("could not start subprocess\n", stderr);
return EXIT_FAILURE;
}
proc->exited.connect([](int64_t status, int) { std::exit(status); });
// start reading
- if (stdinTty) stdinTty->StartRead();
+ if (stdinTty) {
+ stdinTty->StartRead();
+ }
stdoutPipe->StartRead();
stderrPipe->StartRead();
diff --git a/wpiutil/src/netconsoleTee/native/cpp/main.cpp b/wpiutil/src/netconsoleTee/native/cpp/main.cpp
index 82cc82a..8abb458 100644
--- a/wpiutil/src/netconsoleTee/native/cpp/main.cpp
+++ b/wpiutil/src/netconsoleTee/native/cpp/main.cpp
@@ -1,13 +1,13 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2020 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. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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 "fmt/format.h"
#include "wpi/MathExtras.h"
#include "wpi/SmallVector.h"
-#include "wpi/raw_ostream.h"
+#include "wpi/StringExtras.h"
#include "wpi/raw_uv_ostream.h"
#include "wpi/timestamp.h"
#include "wpi/uv/Loop.h"
@@ -24,9 +24,9 @@
wpi::SmallVectorImpl<uv::Buffer>& bufs, bool tcp,
uint16_t tcpSeq) {
// scan for last newline
- wpi::StringRef str(buf.base, len);
+ std::string_view str(buf.base, len);
size_t idx = str.rfind('\n');
- if (idx == wpi::StringRef::npos) {
+ if (idx == std::string_view::npos) {
// no newline yet, just keep appending to remainder
rem += str;
return false;
@@ -34,7 +34,7 @@
// build output
wpi::raw_uv_ostream out(bufs, 4096);
- wpi::StringRef toCopy = str.slice(0, idx + 1);
+ 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);
@@ -48,12 +48,12 @@
static_cast<uint8_t>(ts & 0xff),
static_cast<uint8_t>((tcpSeq >> 8) & 0xff),
static_cast<uint8_t>(tcpSeq & 0xff)};
- out << wpi::ArrayRef<uint8_t>(header);
+ out << wpi::span<const uint8_t>(header);
}
out << rem << toCopy;
// reset remainder
- rem = str.slice(idx + 1, wpi::StringRef::npos);
+ rem = wpi::slice(str, idx + 1, std::string_view::npos);
return true;
}
@@ -72,11 +72,15 @@
uv::Buffer& buf, size_t len) {
// build buffers
wpi::SmallVector<uv::Buffer, 4> bufs;
- if (!NewlineBuffer(*rem, buf, len, bufs, false, 0)) return;
+ if (!NewlineBuffer(*rem, buf, len, bufs, false, 0)) {
+ return;
+ }
// send output
outPtr->Send(addr, bufs, [](auto bufs2, uv::Error) {
- for (auto buf : bufs2) buf.Deallocate();
+ for (auto buf : bufs2) {
+ buf.Deallocate();
+ }
});
},
out);
@@ -92,12 +96,15 @@
uv::Buffer& buf, size_t len) {
// build buffers
wpi::SmallVector<uv::Buffer, 4> bufs;
- if (!NewlineBuffer(data->rem, buf, len, bufs, true, data->seq++))
+ if (!NewlineBuffer(data->rem, buf, len, bufs, true, data->seq++)) {
return;
+ }
// send output
outPtr->Write(bufs, [](auto bufs2, uv::Error) {
- for (auto buf : bufs2) buf.Deallocate();
+ for (auto buf : bufs2) {
+ buf.Deallocate();
+ }
});
},
out);
@@ -107,8 +114,10 @@
in.data.connect([out](uv::Buffer& buf, size_t len) {
uv::Buffer buf2 = buf.Dup();
buf2.len = len;
- out->Write(buf2, [](auto bufs, uv::Error) {
- for (auto buf : bufs) buf.Deallocate();
+ out->Write({buf2}, [](auto bufs, uv::Error) {
+ for (auto buf : bufs) {
+ buf.Deallocate();
+ }
});
});
}
@@ -122,47 +131,56 @@
int port = -1;
while (arg < argc && argv[arg][0] == '-') {
- if (wpi::StringRef(argv[arg]) == "-u") {
+ if (std::string_view(argv[arg]) == "-u") {
useUdp = true;
- } else if (wpi::StringRef(argv[arg]) == "-b") {
+ } else if (std::string_view(argv[arg]) == "-b") {
useUdp = true;
broadcastUdp = true;
- } else if (wpi::StringRef(argv[arg]) == "-p") {
+ } else if (std::string_view(argv[arg]) == "-p") {
++arg;
+ std::optional<int> portValue;
if (arg >= argc || argv[arg][0] == '-' ||
- wpi::StringRef(argv[arg]).getAsInteger(10, port)) {
- wpi::errs() << "-p must be followed by port number\n";
+ !(portValue = wpi::parse_integer<int>(argv[arg], 10))) {
+ std::fputs("-p must be followed by port number\n", stderr);
err = true;
+ } else if (portValue) {
+ port = portValue.value();
}
} else {
- wpi::errs() << "unrecognized command line option " << argv[arg] << '\n';
+ fmt::print(stderr, "unrecognized command line option {}\n", argv[arg]);
err = true;
}
++arg;
}
if (err) {
- wpi::errs()
- << argv[0] << " [-ub] [-p PORT]\n"
- << " -u send udp to localhost port 6666 instead of using tcp\n"
- << " -b broadcast udp to port 6666 instead of using tcp\n"
- << " -p PORT use port PORT instead of 6666 (udp) or 1740 (tcp)\n";
+ std::fputs(argv[0], stderr);
+ std::fputs(
+ " [-ub] [-p PORT]\n"
+ " -u send udp to localhost port 6666 instead of using tcp\n"
+ " -b broadcast udp to port 6666 instead of using tcp\n"
+ " -p PORT use port PORT instead of 6666 (udp) or 1740 (tcp)\n",
+ stderr);
return EXIT_FAILURE;
}
auto loop = uv::Loop::Create();
loop->error.connect(
- [](uv::Error err) { wpi::errs() << "uv ERROR: " << err.str() << '\n'; });
+ [](uv::Error err) { fmt::print(stderr, "uv ERROR: {}\n", err.str()); });
// create ttys
auto stdinTty = uv::Tty::Create(loop, 0, true);
auto stdoutTty = uv::Tty::Create(loop, 1, false);
// don't bother continuing if we don't have a stdin
- if (!stdinTty) return EXIT_SUCCESS;
+ if (!stdinTty) {
+ return EXIT_SUCCESS;
+ }
// pass through our input to output
- if (stdoutTty) CopyStream(*stdinTty, stdoutTty);
+ if (stdoutTty) {
+ CopyStream(*stdinTty, stdoutTty);
+ }
// when our stdin closes, exit
stdinTty->end.connect([] { std::exit(EXIT_SUCCESS); });
@@ -180,7 +198,9 @@
// when we get a connection, accept it
tcp->connection.connect([srv = tcp.get(), stdinTty] {
auto tcp = srv->Accept();
- if (!tcp) return;
+ if (!tcp) {
+ return;
+ }
// close on error
tcp->error.connect([s = tcp.get()](wpi::uv::Error err) { s->Close(); });
@@ -194,7 +214,9 @@
}
// start reading
- if (stdinTty) stdinTty->StartRead();
+ if (stdinTty) {
+ stdinTty->StartRead();
+ }
// run the loop!
loop->Run();
diff --git a/wpiutil/src/test/java/edu/wpi/first/wpiutil/CircularBufferTest.java b/wpiutil/src/test/java/edu/wpi/first/util/CircularBufferTest.java
similarity index 83%
rename from wpiutil/src/test/java/edu/wpi/first/wpiutil/CircularBufferTest.java
rename to wpiutil/src/test/java/edu/wpi/first/util/CircularBufferTest.java
index f995098..7972754 100644
--- a/wpiutil/src/test/java/edu/wpi/first/wpiutil/CircularBufferTest.java
+++ b/wpiutil/src/test/java/edu/wpi/first/util/CircularBufferTest.java
@@ -1,23 +1,23 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2015-2019 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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.wpiutil;
-
-import org.junit.jupiter.api.Test;
+package edu.wpi.first.util;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import org.junit.jupiter.api.Test;
+
class CircularBufferTest {
- private final double[] m_values = {751.848, 766.366, 342.657, 234.252, 716.126,
- 132.344, 445.697, 22.727, 421.125, 799.913};
- private final double[] m_addFirstOut = {799.913, 421.125, 22.727, 445.697, 132.344,
- 716.126, 234.252, 342.657};
- private final double[] m_addLastOut = {342.657, 234.252, 716.126, 132.344, 445.697,
- 22.727, 421.125, 799.913};
+ private final double[] m_values = {
+ 751.848, 766.366, 342.657, 234.252, 716.126, 132.344, 445.697, 22.727, 421.125, 799.913
+ };
+ private final double[] m_addFirstOut = {
+ 799.913, 421.125, 22.727, 445.697, 132.344, 716.126, 234.252, 342.657
+ };
+ private final double[] m_addLastOut = {
+ 342.657, 234.252, 716.126, 132.344, 445.697, 22.727, 421.125, 799.913
+ };
@Test
void addFirstTest() {
@@ -105,7 +105,6 @@
}
@Test
- @SuppressWarnings("PMD.ExcessiveMethodLength")
void resizeTest() {
CircularBuffer queue = new CircularBuffer(5);
diff --git a/wpiutil/src/test/java/edu/wpi/first/util/WPIUtilJNITest.java b/wpiutil/src/test/java/edu/wpi/first/util/WPIUtilJNITest.java
new file mode 100644
index 0000000..7d5b229
--- /dev/null
+++ b/wpiutil/src/test/java/edu/wpi/first/util/WPIUtilJNITest.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.util;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
+import org.junit.jupiter.api.Test;
+
+public class WPIUtilJNITest {
+ @Test
+ public void testEnableMockTime() {
+ assertDoesNotThrow(WPIUtilJNI::enableMockTime);
+ }
+
+ @Test
+ public void testSetMockTime() {
+ assertDoesNotThrow(() -> WPIUtilJNI.setMockTime(0L));
+ }
+
+ @Test
+ public void testNow() {
+ assertDoesNotThrow(WPIUtilJNI::now);
+ }
+}
diff --git a/wpiutil/src/test/java/edu/wpi/first/wpiutil/WPIUtilJNITest.java b/wpiutil/src/test/java/edu/wpi/first/wpiutil/WPIUtilJNITest.java
deleted file mode 100644
index e92efe4..0000000
--- a/wpiutil/src/test/java/edu/wpi/first/wpiutil/WPIUtilJNITest.java
+++ /dev/null
@@ -1,19 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2020 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. */
-/*----------------------------------------------------------------------------*/
-
-package edu.wpi.first.wpiutil;
-
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-
-public class WPIUtilJNITest {
- @Test
- public void testNow() {
- assertDoesNotThrow(WPIUtilJNI::now);
- }
-}
diff --git a/wpiutil/src/test/native/ManagedStaticTest.cpp b/wpiutil/src/test/native/ManagedStaticTest.cpp
index 81a4b9c..ab54499 100644
--- a/wpiutil/src/test/native/ManagedStaticTest.cpp
+++ b/wpiutil/src/test/native/ManagedStaticTest.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2019 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/ManagedStatic.h" // NOLINT(build/include_order)
@@ -23,6 +20,7 @@
{
refCount = 0;
wpi::ManagedStatic<StaticTestClass> managedStatic;
+ (void)managedStatic;
ASSERT_EQ(refCount, 0);
}
ASSERT_EQ(refCount, 0);
@@ -46,7 +44,7 @@
{
refCount = 0;
StaticTestClass* test = new StaticTestClass{};
- ASSERT_EQ(refCount, 1);
+ ASSERT_EQ(refCount, 1); // NOLINT
wpi::ManagedStatic<StaticTestClass> managedStatic(
test, [](void* val) { delete static_cast<StaticTestClass*>(val); });
ASSERT_EQ(refCount, 1);
diff --git a/wpiutil/src/test/native/cpp/Base64Test.cpp b/wpiutil/src/test/native/cpp/Base64Test.cpp
index caa35aa..542858d 100644
--- a/wpiutil/src/test/native/cpp/Base64Test.cpp
+++ b/wpiutil/src/test/native/cpp/Base64Test.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2015-2019 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/Base64.h"
@@ -26,11 +23,12 @@
class Base64Test : public ::testing::TestWithParam<Base64TestParam> {
protected:
- StringRef GetPlain() {
- if (GetParam().plain_len < 0)
- return StringRef(GetParam().plain);
- else
- return StringRef(GetParam().plain, GetParam().plain_len);
+ std::string_view GetPlain() {
+ if (GetParam().plain_len < 0) {
+ return GetParam().plain;
+ } else {
+ return std::string_view(GetParam().plain, GetParam().plain_len);
+ }
}
};
@@ -53,7 +51,7 @@
TEST_P(Base64Test, DecodeStdString) {
std::string s;
- StringRef encoded = GetParam().encoded;
+ std::string_view encoded = GetParam().encoded;
EXPECT_EQ(encoded.size(), Base64Decode(encoded, &s));
ASSERT_EQ(GetPlain(), s);
@@ -64,9 +62,9 @@
TEST_P(Base64Test, DecodeSmallString) {
SmallString<128> buf;
- StringRef encoded = GetParam().encoded;
+ std::string_view encoded = GetParam().encoded;
size_t len;
- StringRef plain = Base64Decode(encoded, &len, buf);
+ std::string_view plain = Base64Decode(encoded, &len, buf);
EXPECT_EQ(encoded.size(), len);
ASSERT_EQ(GetPlain(), plain);
@@ -85,7 +83,8 @@
"mQgc28gb24uLi4K"},
};
-INSTANTIATE_TEST_SUITE_P(Base64Sample, Base64Test, ::testing::ValuesIn(sample));
+INSTANTIATE_TEST_SUITE_P(Base64SampleTests, Base64Test,
+ ::testing::ValuesIn(sample));
static Base64TestParam standard[] = {
{0, "", ""},
@@ -98,7 +97,7 @@
{2, "\xff\xef", "/+8="},
};
-INSTANTIATE_TEST_SUITE_P(Base64Standard, Base64Test,
+INSTANTIATE_TEST_SUITE_P(Base64StandardTests, Base64Test,
::testing::ValuesIn(standard));
} // namespace wpi
diff --git a/wpiutil/src/test/native/cpp/CircularBufferTest.cpp b/wpiutil/src/test/native/cpp/CircularBufferTest.cpp
index 7fe9e03..ad2285e 100644
--- a/wpiutil/src/test/native/cpp/CircularBufferTest.cpp
+++ b/wpiutil/src/test/native/cpp/CircularBufferTest.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2015-2019 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/circular_buffer.h" // NOLINT(build/include_order)
@@ -21,31 +18,55 @@
static const std::array<double, 8> pushBackOut = {
{342.657, 234.252, 716.126, 132.344, 445.697, 22.727, 421.125, 799.913}};
-TEST(CircularBufferTest, PushFrontTest) {
+TEST(CircularBufferTest, PushFront) {
wpi::circular_buffer<double> queue(8);
for (auto& value : values) {
queue.push_front(value);
}
- for (size_t i = 0; i < pushFrontOut.size(); i++) {
+ for (size_t i = 0; i < pushFrontOut.size(); ++i) {
EXPECT_EQ(pushFrontOut[i], queue[i]);
}
}
-TEST(CircularBufferTest, PushBackTest) {
+TEST(CircularBufferTest, PushBack) {
wpi::circular_buffer<double> queue(8);
for (auto& value : values) {
queue.push_back(value);
}
- for (size_t i = 0; i < pushBackOut.size(); i++) {
+ for (size_t i = 0; i < pushBackOut.size(); ++i) {
EXPECT_EQ(pushBackOut[i], queue[i]);
}
}
-TEST(CircularBufferTest, PushPopTest) {
+TEST(CircularBufferTest, EmplaceFront) {
+ wpi::circular_buffer<double> queue(8);
+
+ for (auto& value : values) {
+ queue.emplace_front(value);
+ }
+
+ for (size_t i = 0; i < pushFrontOut.size(); ++i) {
+ EXPECT_EQ(pushFrontOut[i], queue[i]);
+ }
+}
+
+TEST(CircularBufferTest, EmplaceBack) {
+ wpi::circular_buffer<double> queue(8);
+
+ for (auto& value : values) {
+ queue.emplace_back(value);
+ }
+
+ for (size_t i = 0; i < pushBackOut.size(); ++i) {
+ EXPECT_EQ(pushBackOut[i], queue[i]);
+ }
+}
+
+TEST(CircularBufferTest, PushPop) {
wpi::circular_buffer<double> queue(3);
// Insert three elements into the buffer
@@ -88,21 +109,19 @@
EXPECT_EQ(4.0, queue[0]);
}
-TEST(CircularBufferTest, ResetTest) {
+TEST(CircularBufferTest, Reset) {
wpi::circular_buffer<double> queue(5);
- for (size_t i = 1; i < 6; i++) {
+ for (size_t i = 1; i < 6; ++i) {
queue.push_back(i);
}
queue.reset();
- for (size_t i = 0; i < 5; i++) {
- EXPECT_EQ(0.0, queue[i]);
- }
+ EXPECT_EQ(queue.size(), size_t{0});
}
-TEST(CircularBufferTest, ResizeTest) {
+TEST(CircularBufferTest, Resize) {
wpi::circular_buffer<double> queue(5);
/* Buffer contains {1, 2, 3, _, _}
@@ -207,3 +226,29 @@
EXPECT_EQ(2.0, queue[2]);
EXPECT_EQ(3.0, queue[3]);
}
+
+TEST(CircularBufferTest, Iterator) {
+ wpi::circular_buffer<double> queue(3);
+
+ queue.push_back(1.0);
+ queue.push_back(2.0);
+ queue.push_back(3.0);
+ queue.push_back(4.0); // Overwrite 1 with 4
+
+ // The buffer now contains 2, 3 and 4
+ const std::array<double, 3> values = {2.0, 3.0, 4.0};
+
+ // iterator
+ int i = 0;
+ for (auto& elem : queue) {
+ EXPECT_EQ(values[i], elem);
+ ++i;
+ }
+
+ // const_iterator
+ i = 0;
+ for (const auto& elem : queue) {
+ EXPECT_EQ(values[i], elem);
+ ++i;
+ }
+}
diff --git a/wpiutil/src/test/native/cpp/HttpParserTest.cpp b/wpiutil/src/test/native/cpp/HttpParserTest.cpp
index 8de2bc6..281abd0 100644
--- a/wpiutil/src/test/native/cpp/HttpParserTest.cpp
+++ b/wpiutil/src/test/native/cpp/HttpParserTest.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/HttpParser.h" // NOLINT(build/include_order)
@@ -14,7 +11,7 @@
TEST(HttpParserTest, UrlMethodHeadersComplete) {
HttpParser p{HttpParser::kRequest};
int callbacks = 0;
- p.url.connect([&](StringRef path) {
+ p.url.connect([&](std::string_view path) {
ASSERT_EQ(path, "/foo/bar");
ASSERT_EQ(p.GetUrl(), "/foo/bar");
++callbacks;
@@ -32,7 +29,7 @@
TEST(HttpParserTest, UrlMethodHeader) {
HttpParser p{HttpParser::kRequest};
int callbacks = 0;
- p.url.connect([&](StringRef path) {
+ p.url.connect([&](std::string_view path) {
ASSERT_EQ(path, "/foo/bar");
ASSERT_EQ(p.GetUrl(), "/foo/bar");
++callbacks;
@@ -52,7 +49,7 @@
TEST(HttpParserTest, StatusHeadersComplete) {
HttpParser p{HttpParser::kResponse};
int callbacks = 0;
- p.status.connect([&](StringRef status) {
+ p.status.connect([&](std::string_view status) {
ASSERT_EQ(status, "OK");
ASSERT_EQ(p.GetStatusCode(), 200u);
++callbacks;
@@ -69,7 +66,7 @@
TEST(HttpParserTest, StatusHeader) {
HttpParser p{HttpParser::kResponse};
int callbacks = 0;
- p.status.connect([&](StringRef status) {
+ p.status.connect([&](std::string_view status) {
ASSERT_EQ(status, "OK");
ASSERT_EQ(p.GetStatusCode(), 200u);
++callbacks;
@@ -86,7 +83,7 @@
TEST(HttpParserTest, HeaderFieldComplete) {
HttpParser p{HttpParser::kRequest};
int callbacks = 0;
- p.header.connect([&](StringRef name, StringRef value) {
+ p.header.connect([&](std::string_view name, std::string_view value) {
ASSERT_EQ(name, "Foo");
ASSERT_EQ(value, "Bar");
++callbacks;
@@ -109,7 +106,7 @@
TEST(HttpParserTest, HeaderFieldNext) {
HttpParser p{HttpParser::kRequest};
int callbacks = 0;
- p.header.connect([&](StringRef name, StringRef value) {
+ p.header.connect([&](std::string_view name, std::string_view value) {
ASSERT_EQ(name, "Foo");
ASSERT_EQ(value, "Bar");
++callbacks;
diff --git a/wpiutil/src/test/native/cpp/HttpUtilTest.cpp b/wpiutil/src/test/native/cpp/HttpUtilTest.cpp
index a83214d..524086a 100644
--- a/wpiutil/src/test/native/cpp/HttpUtilTest.cpp
+++ b/wpiutil/src/test/native/cpp/HttpUtilTest.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/HttpUtil.h" // NOLINT(build/include_order)
diff --git a/wpiutil/src/test/native/cpp/HttpWebSocketServerConnectionTest.cpp b/wpiutil/src/test/native/cpp/HttpWebSocketServerConnectionTest.cpp
index 8022cae..18a21be 100644
--- a/wpiutil/src/test/native/cpp/HttpWebSocketServerConnectionTest.cpp
+++ b/wpiutil/src/test/native/cpp/HttpWebSocketServerConnectionTest.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2020 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. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/HttpWebSocketServerConnection.h" // NOLINT(build/include_order)
@@ -15,7 +12,7 @@
: public HttpWebSocketServerConnection<HttpWebSocketServerConnectionTest> {
public:
HttpWebSocketServerConnectionTest(std::shared_ptr<uv::Stream> stream,
- ArrayRef<StringRef> protocols)
+ span<const std::string_view> protocols)
: HttpWebSocketServerConnection{stream, protocols} {}
void ProcessRequest() override { ++gotRequest; }
diff --git a/wpiutil/src/test/native/cpp/StaticCircularBufferTest.cpp b/wpiutil/src/test/native/cpp/StaticCircularBufferTest.cpp
index 72da38b..f95f9cb 100644
--- a/wpiutil/src/test/native/cpp/StaticCircularBufferTest.cpp
+++ b/wpiutil/src/test/native/cpp/StaticCircularBufferTest.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2020 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. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/static_circular_buffer.h" // NOLINT(build/include_order)
@@ -21,7 +18,7 @@
static const std::array<double, 8> pushBackOut = {
{342.657, 234.252, 716.126, 132.344, 445.697, 22.727, 421.125, 799.913}};
-TEST(StaticCircularBufferTest, PushFrontTest) {
+TEST(StaticCircularBufferTest, PushFront) {
wpi::static_circular_buffer<double, 8> queue;
for (auto& value : values) {
@@ -33,7 +30,7 @@
}
}
-TEST(StaticCircularBufferTest, PushBackTest) {
+TEST(StaticCircularBufferTest, PushBack) {
wpi::static_circular_buffer<double, 8> queue;
for (auto& value : values) {
@@ -45,7 +42,7 @@
}
}
-TEST(StaticCircularBufferTest, EmplaceFrontTest) {
+TEST(StaticCircularBufferTest, EmplaceFront) {
wpi::static_circular_buffer<double, 8> queue;
for (auto& value : values) {
@@ -57,7 +54,7 @@
}
}
-TEST(StaticCircularBufferTest, EmplaceBackTest) {
+TEST(StaticCircularBufferTest, EmplaceBack) {
wpi::static_circular_buffer<double, 8> queue;
for (auto& value : values) {
@@ -69,7 +66,7 @@
}
}
-TEST(StaticCircularBufferTest, PushPopTest) {
+TEST(StaticCircularBufferTest, PushPop) {
wpi::static_circular_buffer<double, 3> queue;
// Insert three elements into the buffer
@@ -112,7 +109,7 @@
EXPECT_EQ(4.0, queue[0]);
}
-TEST(StaticCircularBufferTest, ResetTest) {
+TEST(StaticCircularBufferTest, Reset) {
wpi::static_circular_buffer<double, 5> queue;
for (size_t i = 1; i < 6; ++i) {
@@ -124,7 +121,7 @@
EXPECT_EQ(queue.size(), size_t{0});
}
-TEST(StaticCircularBufferTest, IteratorTest) {
+TEST(StaticCircularBufferTest, Iterator) {
wpi::static_circular_buffer<double, 3> queue;
queue.push_back(1.0);
diff --git a/wpiutil/src/test/native/cpp/SynchronizationTest.cpp b/wpiutil/src/test/native/cpp/SynchronizationTest.cpp
new file mode 100644
index 0000000..a349c54
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/SynchronizationTest.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 "wpi/Synchronization.h" // NOLINT(build/include_order)
+
+#include <thread>
+
+#include "gtest/gtest.h"
+
+TEST(EventTest, AutoReset) {
+ auto event = wpi::CreateEvent(false, false);
+ std::thread thr([&] { wpi::SetEvent(event); });
+ wpi::WaitForObject(event);
+ thr.join();
+ bool timedOut;
+ wpi::WaitForObject(event, 0, &timedOut);
+ ASSERT_EQ(timedOut, true);
+}
+
+TEST(EventTest, ManualReset) {
+ auto event = wpi::CreateEvent(true, false);
+ int done = 0;
+ std::thread thr([&] {
+ wpi::SetEvent(event);
+ ++done;
+ });
+ wpi::WaitForObject(event);
+ thr.join();
+ ASSERT_EQ(done, 1);
+ bool timedOut;
+ wpi::WaitForObject(event, 0, &timedOut);
+ ASSERT_EQ(timedOut, false);
+}
+
+TEST(EventTest, InitialSet) {
+ auto event = wpi::CreateEvent(false, true);
+ bool timedOut;
+ wpi::WaitForObject(event, 0, &timedOut);
+ ASSERT_EQ(timedOut, false);
+}
+
+TEST(EventTest, WaitMultiple) {
+ auto event1 = wpi::CreateEvent(false, false);
+ auto event2 = wpi::CreateEvent(false, false);
+ std::thread thr([&] { wpi::SetEvent(event2); });
+ WPI_Handle signaled[2];
+ auto result1 = wpi::WaitForObjects({event1, event2}, signaled);
+ thr.join();
+ ASSERT_EQ(result1.size(), 1u);
+ ASSERT_EQ(result1[0], event2);
+ bool timedOut;
+ auto result2 = wpi::WaitForObjects({event1, event2}, signaled, 0, &timedOut);
+ ASSERT_EQ(timedOut, true);
+ ASSERT_EQ(result2.size(), 0u);
+}
diff --git a/wpiutil/src/test/native/cpp/UidVectorTest.cpp b/wpiutil/src/test/native/cpp/UidVectorTest.cpp
index e11c7b2..f9e1624 100644
--- a/wpiutil/src/test/native/cpp/UidVectorTest.cpp
+++ b/wpiutil/src/test/native/cpp/UidVectorTest.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/UidVector.h" // NOLINT(build/include_order)
@@ -39,7 +36,9 @@
v.emplace_back(2);
v.emplace_back(1);
std::vector<int> out;
- for (auto&& val : v) out.push_back(val);
+ for (auto&& val : v) {
+ out.push_back(val);
+ }
ASSERT_EQ(out.size(), 2u);
EXPECT_EQ(out[0], 2);
EXPECT_EQ(out[1], 1);
diff --git a/wpiutil/src/test/native/cpp/WebSocketClientTest.cpp b/wpiutil/src/test/native/cpp/WebSocketClientTest.cpp
index 2db9b54..9d85e66 100644
--- a/wpiutil/src/test/native/cpp/WebSocketClientTest.cpp
+++ b/wpiutil/src/test/native/cpp/WebSocketClientTest.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/WebSocket.h" // NOLINT(build/include_order)
@@ -11,6 +8,7 @@
#include "wpi/Base64.h"
#include "wpi/HttpParser.h"
#include "wpi/SmallString.h"
+#include "wpi/StringExtras.h"
#include "wpi/raw_uv_ostream.h"
#include "wpi/sha1.h"
@@ -20,9 +18,11 @@
public:
WebSocketClientTest() {
// Bare bones server
- req.header.connect([this](StringRef name, StringRef value) {
+ req.header.connect([this](std::string_view name, std::string_view value) {
// save key (required for valid response)
- if (name.equals_lower("sec-websocket-key")) clientKey = value;
+ if (equals_lower(name, "sec-websocket-key")) {
+ clientKey = value;
+ }
});
req.headersComplete.connect([this](bool) {
// send response
@@ -36,37 +36,48 @@
SHA1 hash;
hash.Update(clientKey);
hash.Update("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
- if (mockBadAccept) hash.Update("1");
+ if (mockBadAccept) {
+ hash.Update("1");
+ }
SmallString<64> hashBuf;
SmallString<64> acceptBuf;
os << "Sec-WebSocket-Accept: "
<< Base64Encode(hash.RawFinal(hashBuf), acceptBuf) << "\r\n";
- if (!mockProtocol.empty())
+ if (!mockProtocol.empty()) {
os << "Sec-WebSocket-Protocol: " << mockProtocol << "\r\n";
+ }
os << "\r\n";
conn->Write(bufs, [](auto bufs, uv::Error) {
- for (auto& buf : bufs) buf.Deallocate();
+ for (auto& buf : bufs) {
+ buf.Deallocate();
+ }
});
serverHeadersDone = true;
- if (connected) connected();
+ if (connected) {
+ connected();
+ }
});
serverPipe->Listen([this] {
conn = serverPipe->Accept();
conn->StartRead();
conn->data.connect([this](uv::Buffer& buf, size_t size) {
- StringRef data{buf.base, size};
+ std::string_view data{buf.base, size};
if (!serverHeadersDone) {
data = req.Execute(data);
- if (req.HasError()) Finish();
+ if (req.HasError()) {
+ Finish();
+ }
ASSERT_EQ(req.GetError(), HPE_OK) << http_errno_name(req.GetError());
- if (data.empty()) return;
+ if (data.empty()) {
+ return;
+ }
}
- wireData.insert(wireData.end(), data.bytes_begin(), data.bytes_end());
+ wireData.insert(wireData.end(), data.begin(), data.end());
});
conn->end.connect([this] { Finish(); });
});
@@ -87,12 +98,13 @@
clientPipe->Connect(pipeName, [&] {
auto ws = WebSocket::CreateClient(*clientPipe, "/test", pipeName);
- ws->closed.connect([&](uint16_t code, StringRef reason) {
+ ws->closed.connect([&](uint16_t code, std::string_view reason) {
Finish();
- if (code != 1005 && code != 1006)
+ if (code != 1005 && code != 1006) {
FAIL() << "Code: " << code << " Reason: " << reason;
+ }
});
- ws->open.connect([&](StringRef protocol) {
+ ws->open.connect([&](std::string_view protocol) {
++gotOpen;
Finish();
ASSERT_TRUE(protocol.empty());
@@ -101,7 +113,9 @@
loop->Run();
- if (HasFatalFailure()) return;
+ if (HasFatalFailure()) {
+ return;
+ }
ASSERT_EQ(gotOpen, 1);
}
@@ -112,12 +126,12 @@
clientPipe->Connect(pipeName, [&] {
auto ws = WebSocket::CreateClient(*clientPipe, "/test", pipeName);
- ws->closed.connect([&](uint16_t code, StringRef msg) {
+ ws->closed.connect([&](uint16_t code, std::string_view msg) {
Finish();
++gotClosed;
ASSERT_EQ(code, 1002) << "Message: " << msg;
});
- ws->open.connect([&](StringRef protocol) {
+ ws->open.connect([&](std::string_view protocol) {
Finish();
FAIL() << "Got open";
});
@@ -125,7 +139,9 @@
loop->Run();
- if (HasFatalFailure()) return;
+ if (HasFatalFailure()) {
+ return;
+ }
ASSERT_EQ(gotClosed, 1);
}
@@ -137,12 +153,13 @@
clientPipe->Connect(pipeName, [&] {
auto ws = WebSocket::CreateClient(*clientPipe, "/test", pipeName,
{"myProtocol", "myProtocol2"});
- ws->closed.connect([&](uint16_t code, StringRef msg) {
+ ws->closed.connect([&](uint16_t code, std::string_view msg) {
Finish();
- if (code != 1005 && code != 1006)
+ if (code != 1005 && code != 1006) {
FAIL() << "Code: " << code << "Message: " << msg;
+ }
});
- ws->open.connect([&](StringRef protocol) {
+ ws->open.connect([&](std::string_view protocol) {
++gotOpen;
Finish();
ASSERT_EQ(protocol, "myProtocol");
@@ -151,7 +168,9 @@
loop->Run();
- if (HasFatalFailure()) return;
+ if (HasFatalFailure()) {
+ return;
+ }
ASSERT_EQ(gotOpen, 1);
}
@@ -162,12 +181,12 @@
clientPipe->Connect(pipeName, [&] {
auto ws = WebSocket::CreateClient(*clientPipe, "/test", pipeName);
- ws->closed.connect([&](uint16_t code, StringRef msg) {
+ ws->closed.connect([&](uint16_t code, std::string_view msg) {
Finish();
++gotClosed;
ASSERT_EQ(code, 1003) << "Message: " << msg;
});
- ws->open.connect([&](StringRef protocol) {
+ ws->open.connect([&](std::string_view protocol) {
Finish();
FAIL() << "Got open";
});
@@ -175,7 +194,9 @@
loop->Run();
- if (HasFatalFailure()) return;
+ if (HasFatalFailure()) {
+ return;
+ }
ASSERT_EQ(gotClosed, 1);
}
@@ -184,13 +205,13 @@
clientPipe->Connect(pipeName, [&] {
auto ws = WebSocket::CreateClient(*clientPipe, "/test", pipeName,
- StringRef{"myProtocol"});
- ws->closed.connect([&](uint16_t code, StringRef msg) {
+ {{"myProtocol"}});
+ ws->closed.connect([&](uint16_t code, std::string_view msg) {
Finish();
++gotClosed;
ASSERT_EQ(code, 1002) << "Message: " << msg;
});
- ws->open.connect([&](StringRef protocol) {
+ ws->open.connect([&](std::string_view protocol) {
Finish();
FAIL() << "Got open";
});
@@ -198,7 +219,9 @@
loop->Run();
- if (HasFatalFailure()) return;
+ if (HasFatalFailure()) {
+ return;
+ }
ASSERT_EQ(gotClosed, 1);
}
@@ -213,7 +236,9 @@
WebSocketClientDataTest() {
clientPipe->Connect(pipeName, [&] {
ws = WebSocket::CreateClient(*clientPipe, "/test", pipeName);
- if (setupWebSocket) setupWebSocket();
+ if (setupWebSocket) {
+ setupWebSocket();
+ }
});
}
@@ -228,8 +253,8 @@
int gotCallback = 0;
std::vector<uint8_t> data(GetParam(), 0x03u);
setupWebSocket = [&] {
- ws->open.connect([&](StringRef) {
- ws->SendBinary(uv::Buffer(data), [&](auto bufs, uv::Error) {
+ ws->open.connect([&](std::string_view) {
+ ws->SendBinary({{data}}, [&](auto bufs, uv::Error) {
++gotCallback;
ws->Terminate();
ASSERT_FALSE(bufs.empty());
@@ -250,7 +275,7 @@
int gotCallback = 0;
std::vector<uint8_t> data(GetParam(), 0x03u);
setupWebSocket = [&] {
- ws->binary.connect([&](ArrayRef<uint8_t> inData, bool fin) {
+ ws->binary.connect([&](auto inData, bool fin) {
++gotCallback;
ws->Terminate();
ASSERT_TRUE(fin);
@@ -259,9 +284,7 @@
});
};
auto message = BuildMessage(0x02, true, false, data);
- connected = [&] {
- conn->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
- };
+ connected = [&] { conn->Write({{message}}, [&](auto bufs, uv::Error) {}); };
loop->Run();
@@ -276,19 +299,17 @@
int gotCallback = 0;
std::vector<uint8_t> data(GetParam(), ' ');
setupWebSocket = [&] {
- ws->text.connect([&](StringRef, bool) {
+ ws->text.connect([&](std::string_view, bool) {
ws->Terminate();
FAIL() << "Should not have gotten masked message";
});
- ws->closed.connect([&](uint16_t code, StringRef reason) {
+ ws->closed.connect([&](uint16_t code, std::string_view reason) {
++gotCallback;
ASSERT_EQ(code, 1002) << "reason: " << reason;
});
};
auto message = BuildMessage(0x01, true, true, data);
- connected = [&] {
- conn->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
- };
+ connected = [&] { conn->Write({{message}}, [&](auto bufs, uv::Error) {}); };
loop->Run();
diff --git a/wpiutil/src/test/native/cpp/WebSocketIntegrationTest.cpp b/wpiutil/src/test/native/cpp/WebSocketIntegrationTest.cpp
index 9a66a2e..3de75d9 100644
--- a/wpiutil/src/test/native/cpp/WebSocketIntegrationTest.cpp
+++ b/wpiutil/src/test/native/cpp/WebSocketIntegrationTest.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/WebSocketServer.h" // NOLINT(build/include_order)
@@ -22,7 +19,7 @@
serverPipe->Listen([&]() {
auto conn = serverPipe->Accept();
auto server = WebSocketServer::Create(*conn);
- server->connected.connect([&](StringRef url, WebSocket&) {
+ server->connected.connect([&](std::string_view url, WebSocket&) {
++gotServerOpen;
ASSERT_EQ(url, "/test");
});
@@ -30,12 +27,13 @@
clientPipe->Connect(pipeName, [&] {
auto ws = WebSocket::CreateClient(*clientPipe, "/test", pipeName);
- ws->closed.connect([&](uint16_t code, StringRef reason) {
+ ws->closed.connect([&](uint16_t code, std::string_view reason) {
Finish();
- if (code != 1005 && code != 1006)
+ if (code != 1005 && code != 1006) {
FAIL() << "Code: " << code << " Reason: " << reason;
+ }
});
- ws->open.connect([&, s = ws.get()](StringRef) {
+ ws->open.connect([&, s = ws.get()](std::string_view) {
++gotClientOpen;
s->Close();
});
@@ -54,7 +52,7 @@
serverPipe->Listen([&]() {
auto conn = serverPipe->Accept();
auto server = WebSocketServer::Create(*conn, {"proto1", "proto2"});
- server->connected.connect([&](StringRef, WebSocket& ws) {
+ server->connected.connect([&](std::string_view, WebSocket& ws) {
++gotServerOpen;
ASSERT_EQ(ws.GetProtocol(), "proto1");
});
@@ -63,12 +61,13 @@
clientPipe->Connect(pipeName, [&] {
auto ws =
WebSocket::CreateClient(*clientPipe, "/test", pipeName, {"proto1"});
- ws->closed.connect([&](uint16_t code, StringRef reason) {
+ ws->closed.connect([&](uint16_t code, std::string_view reason) {
Finish();
- if (code != 1005 && code != 1006)
+ if (code != 1005 && code != 1006) {
FAIL() << "Code: " << code << " Reason: " << reason;
+ }
});
- ws->open.connect([&, s = ws.get()](StringRef protocol) {
+ ws->open.connect([&, s = ws.get()](std::string_view protocol) {
++gotClientOpen;
s->Close();
ASSERT_EQ(protocol, "proto1");
@@ -87,20 +86,21 @@
serverPipe->Listen([&]() {
auto conn = serverPipe->Accept();
auto server = WebSocketServer::Create(*conn);
- server->connected.connect([&](StringRef, WebSocket& ws) {
- ws.SendBinary(uv::Buffer{"\x03\x04", 2}, [&](auto, uv::Error) {});
+ server->connected.connect([&](std::string_view, WebSocket& ws) {
+ ws.SendBinary({uv::Buffer{"\x03\x04", 2}}, [&](auto, uv::Error) {});
ws.Close();
});
});
clientPipe->Connect(pipeName, [&] {
auto ws = WebSocket::CreateClient(*clientPipe, "/test", pipeName);
- ws->closed.connect([&](uint16_t code, StringRef reason) {
+ ws->closed.connect([&](uint16_t code, std::string_view reason) {
Finish();
- if (code != 1005 && code != 1006)
+ if (code != 1005 && code != 1006) {
FAIL() << "Code: " << code << " Reason: " << reason;
+ }
});
- ws->binary.connect([&](ArrayRef<uint8_t> data, bool) {
+ ws->binary.connect([&](auto data, bool) {
++gotData;
std::vector<uint8_t> recvData{data.begin(), data.end()};
std::vector<uint8_t> expectData{0x03, 0x04};
@@ -119,8 +119,8 @@
serverPipe->Listen([&]() {
auto conn = serverPipe->Accept();
auto server = WebSocketServer::Create(*conn);
- server->connected.connect([&](StringRef, WebSocket& ws) {
- ws.text.connect([&](StringRef data, bool) {
+ server->connected.connect([&](std::string_view, WebSocket& ws) {
+ ws.text.connect([&](std::string_view data, bool) {
++gotData;
ASSERT_EQ(data, "hello");
});
@@ -129,13 +129,14 @@
clientPipe->Connect(pipeName, [&] {
auto ws = WebSocket::CreateClient(*clientPipe, "/test", pipeName);
- ws->closed.connect([&](uint16_t code, StringRef reason) {
+ ws->closed.connect([&](uint16_t code, std::string_view reason) {
Finish();
- if (code != 1005 && code != 1006)
+ if (code != 1005 && code != 1006) {
FAIL() << "Code: " << code << " Reason: " << reason;
+ }
});
- ws->open.connect([&, s = ws.get()](StringRef) {
- s->SendText(uv::Buffer{"hello"}, [&](auto, uv::Error) {});
+ ws->open.connect([&, s = ws.get()](std::string_view) {
+ s->SendText({{"hello"}}, [&](auto, uv::Error) {});
s->Close();
});
});
diff --git a/wpiutil/src/test/native/cpp/WebSocketServerTest.cpp b/wpiutil/src/test/native/cpp/WebSocketServerTest.cpp
index d11fdda..83561bf 100644
--- a/wpiutil/src/test/native/cpp/WebSocketServerTest.cpp
+++ b/wpiutil/src/test/native/cpp/WebSocketServerTest.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/WebSocket.h" // NOLINT(build/include_order)
@@ -24,28 +21,36 @@
serverPipe->Listen([this]() {
auto conn = serverPipe->Accept();
ws = WebSocket::CreateServer(*conn, "foo", "13");
- if (setupWebSocket) setupWebSocket();
+ if (setupWebSocket) {
+ setupWebSocket();
+ }
});
clientPipe->Connect(pipeName, [this]() {
clientPipe->StartRead();
clientPipe->data.connect([this](uv::Buffer& buf, size_t size) {
- StringRef data{buf.base, size};
+ std::string_view data{buf.base, size};
if (!headersDone) {
data = resp.Execute(data);
- if (resp.HasError()) Finish();
+ if (resp.HasError()) {
+ Finish();
+ }
ASSERT_EQ(resp.GetError(), HPE_OK)
<< http_errno_name(resp.GetError());
- if (data.empty()) return;
+ if (data.empty()) {
+ return;
+ }
}
- wireData.insert(wireData.end(), data.bytes_begin(), data.bytes_end());
- if (handleData) handleData(data);
+ wireData.insert(wireData.end(), data.begin(), data.end());
+ if (handleData) {
+ handleData(data);
+ }
});
clientPipe->end.connect([this]() { Finish(); });
});
}
std::function<void()> setupWebSocket;
- std::function<void(StringRef)> handleData;
+ std::function<void(std::string_view)> handleData;
std::vector<uint8_t> wireData;
std::shared_ptr<WebSocket> ws;
HttpParser resp{HttpParser::kResponse};
@@ -59,8 +64,8 @@
TEST_F(WebSocketServerTest, Terminate) {
int gotClosed = 0;
setupWebSocket = [&] {
- ws->open.connect([&](StringRef) { ws->Terminate(); });
- ws->closed.connect([&](uint16_t code, StringRef reason) {
+ ws->open.connect([&](std::string_view) { ws->Terminate(); });
+ ws->closed.connect([&](uint16_t code, std::string_view reason) {
++gotClosed;
ASSERT_EQ(code, 1006) << "reason: " << reason;
});
@@ -75,8 +80,8 @@
TEST_F(WebSocketServerTest, TerminateCode) {
int gotClosed = 0;
setupWebSocket = [&] {
- ws->open.connect([&](StringRef) { ws->Terminate(1000); });
- ws->closed.connect([&](uint16_t code, StringRef reason) {
+ ws->open.connect([&](std::string_view) { ws->Terminate(1000); });
+ ws->closed.connect([&](uint16_t code, std::string_view reason) {
++gotClosed;
ASSERT_EQ(code, 1000) << "reason: " << reason;
});
@@ -91,8 +96,8 @@
TEST_F(WebSocketServerTest, TerminateReason) {
int gotClosed = 0;
setupWebSocket = [&] {
- ws->open.connect([&](StringRef) { ws->Terminate(1000, "reason"); });
- ws->closed.connect([&](uint16_t code, StringRef reason) {
+ ws->open.connect([&](std::string_view) { ws->Terminate(1000, "reason"); });
+ ws->closed.connect([&](uint16_t code, std::string_view reason) {
++gotClosed;
ASSERT_EQ(code, 1000);
ASSERT_EQ(reason, "reason");
@@ -112,16 +117,16 @@
TEST_F(WebSocketServerTest, CloseBasic) {
int gotClosed = 0;
setupWebSocket = [&] {
- ws->open.connect([&](StringRef) { ws->Close(); });
- ws->closed.connect([&](uint16_t code, StringRef reason) {
+ ws->open.connect([&](std::string_view) { ws->Close(); });
+ ws->closed.connect([&](uint16_t code, std::string_view reason) {
++gotClosed;
ASSERT_EQ(code, 1005) << "reason: " << reason;
});
};
// need to respond with close for server to finish shutdown
auto message = BuildMessage(0x08, true, true, {});
- handleData = [&](StringRef) {
- clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
+ handleData = [&](std::string_view) {
+ clientPipe->Write({{message}}, [&](auto bufs, uv::Error) {});
};
loop->Run();
@@ -134,8 +139,8 @@
TEST_F(WebSocketServerTest, CloseCode) {
int gotClosed = 0;
setupWebSocket = [&] {
- ws->open.connect([&](StringRef) { ws->Close(1000); });
- ws->closed.connect([&](uint16_t code, StringRef reason) {
+ ws->open.connect([&](std::string_view) { ws->Close(1000); });
+ ws->closed.connect([&](uint16_t code, std::string_view reason) {
++gotClosed;
ASSERT_EQ(code, 1000) << "reason: " << reason;
});
@@ -143,8 +148,8 @@
// need to respond with close for server to finish shutdown
const uint8_t contents[] = {0x03u, 0xe8u};
auto message = BuildMessage(0x08, true, true, contents);
- handleData = [&](StringRef) {
- clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
+ handleData = [&](std::string_view) {
+ clientPipe->Write({{message}}, [&](auto bufs, uv::Error) {});
};
loop->Run();
@@ -157,8 +162,8 @@
TEST_F(WebSocketServerTest, CloseReason) {
int gotClosed = 0;
setupWebSocket = [&] {
- ws->open.connect([&](StringRef) { ws->Close(1000, "hangup"); });
- ws->closed.connect([&](uint16_t code, StringRef reason) {
+ ws->open.connect([&](std::string_view) { ws->Close(1000, "hangup"); });
+ ws->closed.connect([&](uint16_t code, std::string_view reason) {
++gotClosed;
ASSERT_EQ(code, 1000);
ASSERT_EQ(reason, "hangup");
@@ -167,8 +172,8 @@
// need to respond with close for server to finish shutdown
const uint8_t contents[] = {0x03u, 0xe8u, 'h', 'a', 'n', 'g', 'u', 'p'};
auto message = BuildMessage(0x08, true, true, contents);
- handleData = [&](StringRef) {
- clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
+ handleData = [&](std::string_view) {
+ clientPipe->Write({{message}}, [&](auto bufs, uv::Error) {});
};
loop->Run();
@@ -185,14 +190,14 @@
TEST_F(WebSocketServerTest, ReceiveCloseBasic) {
int gotClosed = 0;
setupWebSocket = [&] {
- ws->closed.connect([&](uint16_t code, StringRef reason) {
+ ws->closed.connect([&](uint16_t code, std::string_view reason) {
++gotClosed;
ASSERT_EQ(code, 1005) << "reason: " << reason;
});
};
auto message = BuildMessage(0x08, true, true, {});
resp.headersComplete.connect([&](bool) {
- clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
+ clientPipe->Write({{message}}, [&](auto bufs, uv::Error) {});
});
loop->Run();
@@ -206,7 +211,7 @@
TEST_F(WebSocketServerTest, ReceiveCloseCode) {
int gotClosed = 0;
setupWebSocket = [&] {
- ws->closed.connect([&](uint16_t code, StringRef reason) {
+ ws->closed.connect([&](uint16_t code, std::string_view reason) {
++gotClosed;
ASSERT_EQ(code, 1000) << "reason: " << reason;
});
@@ -214,7 +219,7 @@
const uint8_t contents[] = {0x03u, 0xe8u};
auto message = BuildMessage(0x08, true, true, contents);
resp.headersComplete.connect([&](bool) {
- clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
+ clientPipe->Write({{message}}, [&](auto bufs, uv::Error) {});
});
loop->Run();
@@ -228,7 +233,7 @@
TEST_F(WebSocketServerTest, ReceiveCloseReason) {
int gotClosed = 0;
setupWebSocket = [&] {
- ws->closed.connect([&](uint16_t code, StringRef reason) {
+ ws->closed.connect([&](uint16_t code, std::string_view reason) {
++gotClosed;
ASSERT_EQ(code, 1000);
ASSERT_EQ(reason, "hangup");
@@ -237,7 +242,7 @@
const uint8_t contents[] = {0x03u, 0xe8u, 'h', 'a', 'n', 'g', 'u', 'p'};
auto message = BuildMessage(0x08, true, true, contents);
resp.headersComplete.connect([&](bool) {
- clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
+ clientPipe->Write({{message}}, [&](auto bufs, uv::Error) {});
});
loop->Run();
@@ -266,14 +271,14 @@
int gotCallback = 0;
std::vector<uint8_t> data(4, 0x03);
setupWebSocket = [&] {
- ws->closed.connect([&](uint16_t code, StringRef reason) {
+ ws->closed.connect([&](uint16_t code, std::string_view reason) {
++gotCallback;
ASSERT_EQ(code, 1002) << "reason: " << reason;
});
};
auto message = BuildMessage(GetParam(), true, true, data);
resp.headersComplete.connect([&](bool) {
- clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
+ clientPipe->Write({{message}}, [&](auto bufs, uv::Error) {});
});
loop->Run();
@@ -297,14 +302,14 @@
int gotCallback = 0;
std::vector<uint8_t> data(4, 0x03);
setupWebSocket = [&] {
- ws->closed.connect([&](uint16_t code, StringRef reason) {
+ ws->closed.connect([&](uint16_t code, std::string_view reason) {
++gotCallback;
ASSERT_EQ(code, 1002) << "reason: " << reason;
});
};
auto message = BuildMessage(GetParam(), false, true, data);
resp.headersComplete.connect([&](bool) {
- clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
+ clientPipe->Write({{message}}, [&](auto bufs, uv::Error) {});
});
loop->Run();
@@ -324,14 +329,14 @@
int gotCallback = 0;
std::vector<uint8_t> data(4, 0x03);
setupWebSocket = [&] {
- ws->closed.connect([&](uint16_t code, StringRef reason) {
+ ws->closed.connect([&](uint16_t code, std::string_view reason) {
++gotCallback;
ASSERT_EQ(code, 1002) << "reason: " << reason;
});
};
auto message = BuildMessage(0x00, false, true, data);
resp.headersComplete.connect([&](bool) {
- clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
+ clientPipe->Write({{message}}, [&](auto bufs, uv::Error) {});
});
loop->Run();
@@ -344,7 +349,7 @@
int gotCallback = 0;
std::vector<uint8_t> data(4, 0x03);
setupWebSocket = [&] {
- ws->closed.connect([&](uint16_t code, StringRef reason) {
+ ws->closed.connect([&](uint16_t code, std::string_view reason) {
++gotCallback;
ASSERT_EQ(code, 1002) << "reason: " << reason;
});
@@ -352,8 +357,7 @@
auto message = BuildMessage(0x01, true, true, {}); // FIN=1
auto message2 = BuildMessage(0x00, false, true, data);
resp.headersComplete.connect([&](bool) {
- clientPipe->Write({uv::Buffer(message), uv::Buffer(message2)},
- [&](auto bufs, uv::Error) {});
+ clientPipe->Write({{message}, {message2}}, [&](auto bufs, uv::Error) {});
});
loop->Run();
@@ -365,7 +369,7 @@
TEST_F(WebSocketServerTest, ReceiveFragmentInvalidIncomplete) {
int gotCallback = 0;
setupWebSocket = [&] {
- ws->closed.connect([&](uint16_t code, StringRef reason) {
+ ws->closed.connect([&](uint16_t code, std::string_view reason) {
++gotCallback;
ASSERT_EQ(code, 1002) << "reason: " << reason;
});
@@ -374,9 +378,8 @@
auto message2 = BuildMessage(0x00, false, true, {});
auto message3 = BuildMessage(0x01, true, true, {});
resp.headersComplete.connect([&](bool) {
- clientPipe->Write(
- {uv::Buffer(message), uv::Buffer(message2), uv::Buffer(message3)},
- [&](auto bufs, uv::Error) {});
+ clientPipe->Write({{message}, {message2}, {message3}},
+ [&](auto bufs, uv::Error) {});
});
loop->Run();
@@ -396,7 +399,7 @@
combData.insert(combData.end(), data3.begin(), data3.end());
setupWebSocket = [&] {
- ws->binary.connect([&](ArrayRef<uint8_t> inData, bool fin) {
+ ws->binary.connect([&](auto inData, bool fin) {
++gotCallback;
ws->Terminate();
ASSERT_TRUE(fin);
@@ -409,9 +412,8 @@
auto message2 = BuildMessage(0x00, false, true, data2);
auto message3 = BuildMessage(0x00, true, true, data3);
resp.headersComplete.connect([&](bool) {
- clientPipe->Write(
- {uv::Buffer(message), uv::Buffer(message2), uv::Buffer(message3)},
- [&](auto bufs, uv::Error) {});
+ clientPipe->Write({{message}, {message2}, {message3}},
+ [&](auto bufs, uv::Error) {});
});
loop->Run();
@@ -432,7 +434,7 @@
setupWebSocket = [&] {
ws->SetCombineFragments(false);
- ws->binary.connect([&](ArrayRef<uint8_t> inData, bool fin) {
+ ws->binary.connect([&](auto inData, bool fin) {
std::vector<uint8_t> recvData{inData.begin(), inData.end()};
switch (++gotCallback) {
case 1:
@@ -459,9 +461,8 @@
auto message2 = BuildMessage(0x00, false, true, data2);
auto message3 = BuildMessage(0x00, true, true, data3);
resp.headersComplete.connect([&](bool) {
- clientPipe->Write(
- {uv::Buffer(message), uv::Buffer(message2), uv::Buffer(message3)},
- [&](auto bufs, uv::Error) {});
+ clientPipe->Write({{message}, {message2}, {message3}},
+ [&](auto bufs, uv::Error) {});
});
loop->Run();
@@ -483,14 +484,14 @@
ws->Terminate();
FAIL() << "Should not have gotten unmasked message";
});
- ws->closed.connect([&](uint16_t code, StringRef reason) {
+ ws->closed.connect([&](uint16_t code, std::string_view reason) {
++gotCallback;
ASSERT_EQ(code, 1009) << "reason: " << reason;
});
};
auto message = BuildMessage(0x01, true, true, data);
resp.headersComplete.connect([&](bool) {
- clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
+ clientPipe->Write({{message}}, [&](auto bufs, uv::Error) {});
});
loop->Run();
@@ -508,7 +509,7 @@
ws->Terminate();
FAIL() << "Should not have gotten unmasked message";
});
- ws->closed.connect([&](uint16_t code, StringRef reason) {
+ ws->closed.connect([&](uint16_t code, std::string_view reason) {
++gotCallback;
ASSERT_EQ(code, 1009) << "reason: " << reason;
});
@@ -516,8 +517,7 @@
auto message = BuildMessage(0x01, false, true, data);
auto message2 = BuildMessage(0x00, true, true, data);
resp.headersComplete.connect([&](bool) {
- clientPipe->Write({uv::Buffer(message), uv::Buffer(message2)},
- [&](auto bufs, uv::Error) {});
+ clientPipe->Write({{message}, {message2}}, [&](auto bufs, uv::Error) {});
});
loop->Run();
@@ -539,8 +539,8 @@
int gotCallback = 0;
std::vector<uint8_t> data(GetParam(), ' ');
setupWebSocket = [&] {
- ws->open.connect([&](StringRef) {
- ws->SendText(uv::Buffer(data), [&](auto bufs, uv::Error) {
+ ws->open.connect([&](std::string_view) {
+ ws->SendText({{data}}, [&](auto bufs, uv::Error) {
++gotCallback;
ws->Terminate();
ASSERT_FALSE(bufs.empty());
@@ -560,8 +560,8 @@
int gotCallback = 0;
std::vector<uint8_t> data(GetParam(), 0x03u);
setupWebSocket = [&] {
- ws->open.connect([&](StringRef) {
- ws->SendBinary(uv::Buffer(data), [&](auto bufs, uv::Error) {
+ ws->open.connect([&](std::string_view) {
+ ws->SendBinary({{data}}, [&](auto bufs, uv::Error) {
++gotCallback;
ws->Terminate();
ASSERT_FALSE(bufs.empty());
@@ -581,8 +581,8 @@
int gotCallback = 0;
std::vector<uint8_t> data(GetParam(), 0x03u);
setupWebSocket = [&] {
- ws->open.connect([&](StringRef) {
- ws->SendPing(uv::Buffer(data), [&](auto bufs, uv::Error) {
+ ws->open.connect([&](std::string_view) {
+ ws->SendPing({{data}}, [&](auto bufs, uv::Error) {
++gotCallback;
ws->Terminate();
ASSERT_FALSE(bufs.empty());
@@ -602,8 +602,8 @@
int gotCallback = 0;
std::vector<uint8_t> data(GetParam(), 0x03u);
setupWebSocket = [&] {
- ws->open.connect([&](StringRef) {
- ws->SendPong(uv::Buffer(data), [&](auto bufs, uv::Error) {
+ ws->open.connect([&](std::string_view) {
+ ws->SendPong({{data}}, [&](auto bufs, uv::Error) {
++gotCallback;
ws->Terminate();
ASSERT_FALSE(bufs.empty());
@@ -623,18 +623,18 @@
int gotCallback = 0;
std::vector<uint8_t> data(GetParam(), ' ');
setupWebSocket = [&] {
- ws->text.connect([&](StringRef inData, bool fin) {
+ ws->text.connect([&](std::string_view inData, bool fin) {
++gotCallback;
ws->Terminate();
ASSERT_TRUE(fin);
std::vector<uint8_t> recvData;
- recvData.insert(recvData.end(), inData.bytes_begin(), inData.bytes_end());
+ recvData.insert(recvData.end(), inData.begin(), inData.end());
ASSERT_EQ(data, recvData);
});
};
auto message = BuildMessage(0x01, true, true, data);
resp.headersComplete.connect([&](bool) {
- clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
+ clientPipe->Write({{message}}, [&](auto bufs, uv::Error) {});
});
loop->Run();
@@ -646,7 +646,7 @@
int gotCallback = 0;
std::vector<uint8_t> data(GetParam(), 0x03u);
setupWebSocket = [&] {
- ws->binary.connect([&](ArrayRef<uint8_t> inData, bool fin) {
+ ws->binary.connect([&](auto inData, bool fin) {
++gotCallback;
ws->Terminate();
ASSERT_TRUE(fin);
@@ -656,7 +656,7 @@
};
auto message = BuildMessage(0x02, true, true, data);
resp.headersComplete.connect([&](bool) {
- clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
+ clientPipe->Write({{message}}, [&](auto bufs, uv::Error) {});
});
loop->Run();
@@ -668,7 +668,7 @@
int gotCallback = 0;
std::vector<uint8_t> data(GetParam(), 0x03u);
setupWebSocket = [&] {
- ws->ping.connect([&](ArrayRef<uint8_t> inData) {
+ ws->ping.connect([&](auto inData) {
++gotCallback;
ws->Terminate();
std::vector<uint8_t> recvData{inData.begin(), inData.end()};
@@ -677,7 +677,7 @@
};
auto message = BuildMessage(0x09, true, true, data);
resp.headersComplete.connect([&](bool) {
- clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
+ clientPipe->Write({{message}}, [&](auto bufs, uv::Error) {});
});
loop->Run();
@@ -689,7 +689,7 @@
int gotCallback = 0;
std::vector<uint8_t> data(GetParam(), 0x03u);
setupWebSocket = [&] {
- ws->pong.connect([&](ArrayRef<uint8_t> inData) {
+ ws->pong.connect([&](auto inData) {
++gotCallback;
ws->Terminate();
std::vector<uint8_t> recvData{inData.begin(), inData.end()};
@@ -698,7 +698,7 @@
};
auto message = BuildMessage(0x0a, true, true, data);
resp.headersComplete.connect([&](bool) {
- clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
+ clientPipe->Write({{message}}, [&](auto bufs, uv::Error) {});
});
loop->Run();
@@ -714,18 +714,18 @@
int gotCallback = 0;
std::vector<uint8_t> data(GetParam(), ' ');
setupWebSocket = [&] {
- ws->text.connect([&](StringRef, bool) {
+ ws->text.connect([&](std::string_view, bool) {
ws->Terminate();
FAIL() << "Should not have gotten unmasked message";
});
- ws->closed.connect([&](uint16_t code, StringRef reason) {
+ ws->closed.connect([&](uint16_t code, std::string_view reason) {
++gotCallback;
ASSERT_EQ(code, 1002) << "reason: " << reason;
});
};
auto message = BuildMessage(0x01, true, false, data);
resp.headersComplete.connect([&](bool) {
- clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
+ clientPipe->Write({{message}}, [&](auto bufs, uv::Error) {});
});
loop->Run();
diff --git a/wpiutil/src/test/native/cpp/WebSocketTest.cpp b/wpiutil/src/test/native/cpp/WebSocketTest.cpp
index c27bac0..b5d5e4e 100644
--- a/wpiutil/src/test/native/cpp/WebSocketTest.cpp
+++ b/wpiutil/src/test/native/cpp/WebSocketTest.cpp
@@ -1,15 +1,13 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/WebSocket.h" // NOLINT(build/include_order)
#include "WebSocketTest.h"
#include "wpi/HttpParser.h"
+#include "wpi/StringExtras.h"
namespace wpi {
@@ -38,15 +36,19 @@
data.push_back(len & 0xff);
} else {
data.push_back(127u | (masking ? 0x80u : 0x00u));
- for (int i = 56; i >= 0; i -= 8) data.push_back((len >> i) & 0xff);
+ for (int i = 56; i >= 0; i -= 8) {
+ data.push_back((len >> i) & 0xff);
+ }
}
- if (masking) data.insert(data.end(), &testMask[0], &testMask[4]);
+ if (masking) {
+ data.insert(data.end(), &testMask[0], &testMask[4]);
+ }
return data;
}
std::vector<uint8_t> WebSocketTest::BuildMessage(uint8_t opcode, bool fin,
bool masking,
- ArrayRef<uint8_t> data) {
+ span<const uint8_t> data) {
auto finalData = BuildHeader(opcode, fin, masking, data.size());
size_t headerSize = finalData.size();
finalData.insert(finalData.end(), data.begin(), data.end());
@@ -56,7 +58,9 @@
int n = 0;
for (size_t i = headerSize, end = finalData.size(); i < end; ++i) {
finalData[i] ^= mask[n++];
- if (n >= 4) n = 0;
+ if (n >= 4) {
+ n = 0;
+ }
}
}
return finalData;
@@ -64,17 +68,22 @@
// If the message is masked, changes the mask to match the mask set by
// BuildHeader() by unmasking and remasking.
-void WebSocketTest::AdjustMasking(MutableArrayRef<uint8_t> message) {
- if (message.size() < 2) return;
- if ((message[1] & 0x80) == 0) return; // not masked
+void WebSocketTest::AdjustMasking(span<uint8_t> message) {
+ if (message.size() < 2) {
+ return;
+ }
+ if ((message[1] & 0x80) == 0) {
+ return; // not masked
+ }
size_t maskPos;
uint8_t len = message[1] & 0x7f;
- if (len == 126)
+ if (len == 126) {
maskPos = 4;
- else if (len == 127)
+ } else if (len == 127) {
maskPos = 10;
- else
+ } else {
maskPos = 2;
+ }
uint8_t mask[4] = {message[maskPos], message[maskPos + 1],
message[maskPos + 2], message[maskPos + 3]};
message[maskPos] = testMask[0];
@@ -82,9 +91,11 @@
message[maskPos + 2] = testMask[2];
message[maskPos + 3] = testMask[3];
int n = 0;
- for (auto& ch : message.slice(maskPos + 4)) {
+ for (auto& ch : message.subspan(maskPos + 4)) {
ch ^= mask[n] ^ testMask[n];
- if (++n >= 4) n = 0;
+ if (++n >= 4) {
+ n = 0;
+ }
}
}
@@ -96,24 +107,24 @@
int gotVersion = 0;
HttpParser req{HttpParser::kRequest};
- req.url.connect([](StringRef url) { ASSERT_EQ(url, "/test"); });
- req.header.connect([&](StringRef name, StringRef value) {
- if (name.equals_lower("host")) {
+ req.url.connect([](std::string_view url) { ASSERT_EQ(url, "/test"); });
+ req.header.connect([&](std::string_view name, std::string_view value) {
+ if (equals_lower(name, "host")) {
ASSERT_EQ(value, pipeName);
++gotHost;
- } else if (name.equals_lower("upgrade")) {
+ } else if (equals_lower(name, "upgrade")) {
ASSERT_EQ(value, "websocket");
++gotUpgrade;
- } else if (name.equals_lower("connection")) {
+ } else if (equals_lower(name, "connection")) {
ASSERT_EQ(value, "Upgrade");
++gotConnection;
- } else if (name.equals_lower("sec-websocket-key")) {
+ } else if (equals_lower(name, "sec-websocket-key")) {
++gotKey;
- } else if (name.equals_lower("sec-websocket-version")) {
+ } else if (equals_lower(name, "sec-websocket-version")) {
ASSERT_EQ(value, "13");
++gotVersion;
} else {
- FAIL() << "unexpected header " << name.str();
+ FAIL() << "unexpected header " << name;
}
});
req.headersComplete.connect([&](bool) { Finish(); });
@@ -122,8 +133,10 @@
auto conn = serverPipe->Accept();
conn->StartRead();
conn->data.connect([&](uv::Buffer& buf, size_t size) {
- req.Execute(StringRef{buf.base, size});
- if (req.HasError()) Finish();
+ req.Execute(std::string_view{buf.base, size});
+ if (req.HasError()) {
+ Finish();
+ }
ASSERT_EQ(req.GetError(), HPE_OK) << http_errno_name(req.GetError());
});
});
@@ -133,7 +146,9 @@
loop->Run();
- if (HasFatalFailure()) return;
+ if (HasFatalFailure()) {
+ return;
+ }
ASSERT_EQ(gotHost, 1);
ASSERT_EQ(gotUpgrade, 1);
ASSERT_EQ(gotConnection, 1);
@@ -145,11 +160,11 @@
int gotExtra1 = 0;
int gotExtra2 = 0;
HttpParser req{HttpParser::kRequest};
- req.header.connect([&](StringRef name, StringRef value) {
- if (name.equals("Extra1")) {
+ req.header.connect([&](std::string_view name, std::string_view value) {
+ if (equals(name, "Extra1")) {
ASSERT_EQ(value, "Data1");
++gotExtra1;
- } else if (name.equals("Extra2")) {
+ } else if (equals(name, "Extra2")) {
ASSERT_EQ(value, "Data2");
++gotExtra2;
}
@@ -160,24 +175,28 @@
auto conn = serverPipe->Accept();
conn->StartRead();
conn->data.connect([&](uv::Buffer& buf, size_t size) {
- req.Execute(StringRef{buf.base, size});
- if (req.HasError()) Finish();
+ req.Execute(std::string_view{buf.base, size});
+ if (req.HasError()) {
+ Finish();
+ }
ASSERT_EQ(req.GetError(), HPE_OK) << http_errno_name(req.GetError());
});
});
clientPipe->Connect(pipeName, [&]() {
WebSocket::ClientOptions options;
- SmallVector<std::pair<StringRef, StringRef>, 4> extraHeaders;
+ SmallVector<std::pair<std::string_view, std::string_view>, 4> extraHeaders;
extraHeaders.emplace_back("Extra1", "Data1");
extraHeaders.emplace_back("Extra2", "Data2");
options.extraHeaders = extraHeaders;
- auto ws = WebSocket::CreateClient(*clientPipe, "/test", pipeName,
- ArrayRef<StringRef>{}, options);
+ auto ws =
+ WebSocket::CreateClient(*clientPipe, "/test", pipeName, {}, options);
});
loop->Run();
- if (HasFatalFailure()) return;
+ if (HasFatalFailure()) {
+ return;
+ }
ASSERT_EQ(gotExtra1, 1);
ASSERT_EQ(gotExtra2, 1);
}
@@ -188,9 +207,9 @@
clientPipe->Connect(pipeName, [&]() {
WebSocket::ClientOptions options;
options.handshakeTimeout = uv::Timer::Time{100};
- auto ws = WebSocket::CreateClient(*clientPipe, "/test", pipeName,
- ArrayRef<StringRef>{}, options);
- ws->closed.connect([&](uint16_t code, StringRef) {
+ auto ws =
+ WebSocket::CreateClient(*clientPipe, "/test", pipeName, {}, options);
+ ws->closed.connect([&](uint16_t code, std::string_view) {
Finish();
++gotClosed;
ASSERT_EQ(code, 1006);
@@ -199,7 +218,9 @@
loop->Run();
- if (HasFatalFailure()) return;
+ if (HasFatalFailure()) {
+ return;
+ }
ASSERT_EQ(gotClosed, 1);
}
@@ -211,22 +232,22 @@
int gotOpen = 0;
HttpParser resp{HttpParser::kResponse};
- resp.status.connect([&](StringRef status) {
+ resp.status.connect([&](std::string_view status) {
++gotStatus;
ASSERT_EQ(resp.GetStatusCode(), 101u) << "status: " << status;
});
- resp.header.connect([&](StringRef name, StringRef value) {
- if (name.equals_lower("upgrade")) {
+ resp.header.connect([&](std::string_view name, std::string_view value) {
+ if (equals_lower(name, "upgrade")) {
ASSERT_EQ(value, "websocket");
++gotUpgrade;
- } else if (name.equals_lower("connection")) {
+ } else if (equals_lower(name, "connection")) {
ASSERT_EQ(value, "Upgrade");
++gotConnection;
- } else if (name.equals_lower("sec-websocket-accept")) {
+ } else if (equals_lower(name, "sec-websocket-accept")) {
ASSERT_EQ(value, "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=");
++gotAccept;
} else {
- FAIL() << "unexpected header " << name.str();
+ FAIL() << "unexpected header " << name;
}
});
resp.headersComplete.connect([&](bool) { Finish(); });
@@ -234,7 +255,7 @@
serverPipe->Listen([&]() {
auto conn = serverPipe->Accept();
auto ws = WebSocket::CreateServer(*conn, "dGhlIHNhbXBsZSBub25jZQ==", "13");
- ws->open.connect([&](StringRef protocol) {
+ ws->open.connect([&](std::string_view protocol) {
++gotOpen;
ASSERT_TRUE(protocol.empty());
});
@@ -242,15 +263,19 @@
clientPipe->Connect(pipeName, [&] {
clientPipe->StartRead();
clientPipe->data.connect([&](uv::Buffer& buf, size_t size) {
- resp.Execute(StringRef{buf.base, size});
- if (resp.HasError()) Finish();
+ resp.Execute(std::string_view{buf.base, size});
+ if (resp.HasError()) {
+ Finish();
+ }
ASSERT_EQ(resp.GetError(), HPE_OK) << http_errno_name(resp.GetError());
});
});
loop->Run();
- if (HasFatalFailure()) return;
+ if (HasFatalFailure()) {
+ return;
+ }
ASSERT_EQ(gotStatus, 1);
ASSERT_EQ(gotUpgrade, 1);
ASSERT_EQ(gotConnection, 1);
@@ -263,8 +288,8 @@
int gotOpen = 0;
HttpParser resp{HttpParser::kResponse};
- resp.header.connect([&](StringRef name, StringRef value) {
- if (name.equals_lower("sec-websocket-protocol")) {
+ resp.header.connect([&](std::string_view name, std::string_view value) {
+ if (equals_lower(name, "sec-websocket-protocol")) {
++gotProtocol;
ASSERT_EQ(value, "myProtocol");
}
@@ -274,7 +299,7 @@
serverPipe->Listen([&]() {
auto conn = serverPipe->Accept();
auto ws = WebSocket::CreateServer(*conn, "foo", "13", "myProtocol");
- ws->open.connect([&](StringRef protocol) {
+ ws->open.connect([&](std::string_view protocol) {
++gotOpen;
ASSERT_EQ(protocol, "myProtocol");
});
@@ -282,15 +307,19 @@
clientPipe->Connect(pipeName, [&] {
clientPipe->StartRead();
clientPipe->data.connect([&](uv::Buffer& buf, size_t size) {
- resp.Execute(StringRef{buf.base, size});
- if (resp.HasError()) Finish();
+ resp.Execute(std::string_view{buf.base, size});
+ if (resp.HasError()) {
+ Finish();
+ }
ASSERT_EQ(resp.GetError(), HPE_OK) << http_errno_name(resp.GetError());
});
});
loop->Run();
- if (HasFatalFailure()) return;
+ if (HasFatalFailure()) {
+ return;
+ }
ASSERT_EQ(gotProtocol, 1);
ASSERT_EQ(gotOpen, 1);
}
@@ -301,19 +330,19 @@
int gotUpgrade = 0;
HttpParser resp{HttpParser::kResponse};
- resp.status.connect([&](StringRef status) {
+ resp.status.connect([&](std::string_view status) {
++gotStatus;
ASSERT_EQ(resp.GetStatusCode(), 426u) << "status: " << status;
});
- resp.header.connect([&](StringRef name, StringRef value) {
- if (name.equals_lower("sec-websocket-version")) {
+ resp.header.connect([&](std::string_view name, std::string_view value) {
+ if (equals_lower(name, "sec-websocket-version")) {
++gotVersion;
ASSERT_EQ(value, "13");
- } else if (name.equals_lower("upgrade")) {
+ } else if (equals_lower(name, "upgrade")) {
++gotUpgrade;
ASSERT_EQ(value, "WebSocket");
} else {
- FAIL() << "unexpected header " << name.str();
+ FAIL() << "unexpected header " << name;
}
});
resp.headersComplete.connect([&](bool) { Finish(); });
@@ -321,7 +350,7 @@
serverPipe->Listen([&] {
auto conn = serverPipe->Accept();
auto ws = WebSocket::CreateServer(*conn, "foo", "14");
- ws->open.connect([&](StringRef) {
+ ws->open.connect([&](std::string_view) {
Finish();
FAIL();
});
@@ -329,15 +358,19 @@
clientPipe->Connect(pipeName, [&] {
clientPipe->StartRead();
clientPipe->data.connect([&](uv::Buffer& buf, size_t size) {
- resp.Execute(StringRef{buf.base, size});
- if (resp.HasError()) Finish();
+ resp.Execute(std::string_view{buf.base, size});
+ if (resp.HasError()) {
+ Finish();
+ }
ASSERT_EQ(resp.GetError(), HPE_OK) << http_errno_name(resp.GetError());
});
});
loop->Run();
- if (HasFatalFailure()) return;
+ if (HasFatalFailure()) {
+ return;
+ }
ASSERT_EQ(gotStatus, 1);
ASSERT_EQ(gotVersion, 1);
ASSERT_EQ(gotUpgrade, 1);
diff --git a/wpiutil/src/test/native/cpp/WebSocketTest.h b/wpiutil/src/test/native/cpp/WebSocketTest.h
index 8b40440..fda4473 100644
--- a/wpiutil/src/test/native/cpp/WebSocketTest.h
+++ b/wpiutil/src/test/native/cpp/WebSocketTest.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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
@@ -12,7 +9,7 @@
#include <vector>
#include "gtest/gtest.h"
-#include "wpi/ArrayRef.h"
+#include "wpi/span.h"
#include "wpi/uv/Loop.h"
#include "wpi/uv/Pipe.h"
#include "wpi/uv/Timer.h"
@@ -51,7 +48,7 @@
failTimer->Unreference();
}
- ~WebSocketTest() { Finish(); }
+ ~WebSocketTest() override { Finish(); }
void Finish() {
loop->Walk([](uv::Handle& it) { it.Close(); });
@@ -61,8 +58,8 @@
bool masking, uint64_t len);
static std::vector<uint8_t> BuildMessage(uint8_t opcode, bool fin,
bool masking,
- ArrayRef<uint8_t> data);
- static void AdjustMasking(MutableArrayRef<uint8_t> message);
+ span<const uint8_t> data);
+ static void AdjustMasking(span<uint8_t> message);
static const uint8_t testMask[4];
std::shared_ptr<uv::Loop> loop;
diff --git a/wpiutil/src/test/native/cpp/WorkerThreadTest.cpp b/wpiutil/src/test/native/cpp/WorkerThreadTest.cpp
index 610029c..9bc9a17 100644
--- a/wpiutil/src/test/native/cpp/WorkerThreadTest.cpp
+++ b/wpiutil/src/test/native/cpp/WorkerThreadTest.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/WorkerThread.h" // NOLINT(build/include_order)
@@ -15,14 +12,14 @@
namespace wpi {
-TEST(WorkerThread, Future) {
+TEST(WorkerThreadTest, Future) {
WorkerThread<int(bool)> worker;
future<int> f =
worker.QueueWork([](bool v) -> int { return v ? 1 : 2; }, true);
ASSERT_EQ(f.get(), 1);
}
-TEST(WorkerThread, FutureVoid) {
+TEST(WorkerThreadTest, FutureVoid) {
int callbacks = 0;
WorkerThread<void(int)> worker;
future<void> f = worker.QueueWork(
@@ -35,7 +32,7 @@
ASSERT_EQ(callbacks, 1);
}
-TEST(WorkerThread, Loop) {
+TEST(WorkerThreadTest, Loop) {
int callbacks = 0;
WorkerThread<int(bool)> worker;
auto loop = uv::Loop::Create();
@@ -53,7 +50,7 @@
ASSERT_EQ(callbacks, 1);
}
-TEST(WorkerThread, LoopVoid) {
+TEST(WorkerThreadTest, LoopVoid) {
int callbacks = 0;
WorkerThread<void(bool)> worker;
auto loop = uv::Loop::Create();
diff --git a/wpiutil/src/test/native/cpp/future_test.cpp b/wpiutil/src/test/native/cpp/future_test.cpp
index b9b79a8..8817f2d 100644
--- a/wpiutil/src/test/native/cpp/future_test.cpp
+++ b/wpiutil/src/test/native/cpp/future_test.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/future.h" // NOLINT(build/include_order)
@@ -13,7 +10,7 @@
namespace wpi {
-TEST(Future, Then) {
+TEST(FutureTest, Then) {
promise<bool> inPromise;
future<int> outFuture =
inPromise.get_future().then([](bool v) { return v ? 5 : 6; });
@@ -22,7 +19,7 @@
ASSERT_EQ(outFuture.get(), 5);
}
-TEST(Future, ThenSame) {
+TEST(FutureTest, ThenSame) {
promise<bool> inPromise;
future<bool> outFuture =
inPromise.get_future().then([](bool v) { return !v; });
@@ -31,7 +28,7 @@
ASSERT_EQ(outFuture.get(), false);
}
-TEST(Future, ThenFromVoid) {
+TEST(FutureTest, ThenFromVoid) {
promise<void> inPromise;
future<int> outFuture = inPromise.get_future().then([] { return 5; });
@@ -39,7 +36,7 @@
ASSERT_EQ(outFuture.get(), 5);
}
-TEST(Future, ThenToVoid) {
+TEST(FutureTest, ThenToVoid) {
promise<bool> inPromise;
future<void> outFuture = inPromise.get_future().then([](bool v) {});
@@ -47,7 +44,7 @@
ASSERT_TRUE(outFuture.is_ready());
}
-TEST(Future, ThenVoidVoid) {
+TEST(FutureTest, ThenVoidVoid) {
promise<void> inPromise;
future<void> outFuture = inPromise.get_future().then([] {});
@@ -55,7 +52,7 @@
ASSERT_TRUE(outFuture.is_ready());
}
-TEST(Future, Implicit) {
+TEST(FutureTest, Implicit) {
promise<bool> inPromise;
future<int> outFuture = inPromise.get_future();
@@ -63,7 +60,7 @@
ASSERT_EQ(outFuture.get(), 1);
}
-TEST(Future, MoveSame) {
+TEST(FutureTest, MoveSame) {
promise<bool> inPromise;
future<bool> outFuture1 = inPromise.get_future();
future<bool> outFuture(std::move(outFuture1));
@@ -72,7 +69,7 @@
ASSERT_EQ(outFuture.get(), true);
}
-TEST(Future, MoveVoid) {
+TEST(FutureTest, MoveVoid) {
promise<void> inPromise;
future<void> outFuture1 = inPromise.get_future();
future<void> outFuture(std::move(outFuture1));
diff --git a/wpiutil/src/test/native/cpp/hostname.cpp b/wpiutil/src/test/native/cpp/hostname.cpp
index d5ec535..29cceb1 100644
--- a/wpiutil/src/test/native/cpp/hostname.cpp
+++ b/wpiutil/src/test/native/cpp/hostname.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* 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. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/hostname.h"
@@ -12,7 +9,9 @@
#include "wpi/SmallVector.h"
namespace wpi {
-TEST(HostNameTest, HostNameNotEmpty) { ASSERT_NE(GetHostname(), ""); }
+TEST(HostNameTest, HostNameNotEmpty) {
+ ASSERT_NE(GetHostname(), "");
+}
TEST(HostNameTest, HostNameNotEmptySmallVector) {
SmallVector<char, 256> name;
ASSERT_NE(GetHostname(name), "");
diff --git a/wpiutil/src/test/native/cpp/json/unit-readme.cpp b/wpiutil/src/test/native/cpp/json/unit-readme.cpp
index ecb7f79..14d2b1b 100644
--- a/wpiutil/src/test/native/cpp/json/unit-readme.cpp
+++ b/wpiutil/src/test/native/cpp/json/unit-readme.cpp
@@ -200,7 +200,7 @@
{
std::vector<int> c_vector {1, 2, 3, 4};
json j_vec(c_vector);
- json j_vec2(wpi::makeArrayRef(c_vector));
+ json j_vec2(wpi::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};
diff --git a/wpiutil/src/test/native/cpp/json/unit-unicode.cpp b/wpiutil/src/test/native/cpp/json/unit-unicode.cpp
index e0179b6..6755fe2 100644
--- a/wpiutil/src/test/native/cpp/json/unit-unicode.cpp
+++ b/wpiutil/src/test/native/cpp/json/unit-unicode.cpp
@@ -37,7 +37,7 @@
#include "unit-json.h"
using wpi::json;
-#include "wpi/Format.h"
+#include "fmt/format.h"
#include "wpi/StringExtras.h"
#include "wpi/raw_ostream.h"
@@ -919,11 +919,7 @@
// reverse solidus, followed by the lowercase letter u, followed
// by four hexadecimal digits that encode the character's code
// point
- std::string s;
- wpi::raw_string_ostream ss(s);
- ss << "\\u" << wpi::format_hex_no_prefix(cp, 4);
- ss.flush();
- return s;
+ return fmt::format("\\u{:04x}", cp);
}
// correct sequences
diff --git a/wpiutil/src/test/native/cpp/leb128Test.cpp b/wpiutil/src/test/native/cpp/leb128Test.cpp
index a3a7ed0..cc537ae 100644
--- a/wpiutil/src/test/native/cpp/leb128Test.cpp
+++ b/wpiutil/src/test/native/cpp/leb128Test.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2015-2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
//===- llvm/unittest/Support/LEB128Test.cpp - LEB128 function tests -------===//
//
@@ -17,23 +14,23 @@
#include <stdint.h>
#include <string>
+#include <string_view>
#include "gtest/gtest.h"
#include "wpi/SmallString.h"
-#include "wpi/StringRef.h"
#include "wpi/leb128.h"
#include "wpi/raw_istream.h"
namespace wpi {
TEST(LEB128Test, WriteUleb128) {
-#define EXPECT_ULEB128_EQ(EXPECTED, VALUE, PAD) \
- do { \
- StringRef expected(EXPECTED, sizeof(EXPECTED) - 1); \
- SmallString<32> buf; \
- size_t size = WriteUleb128(buf, VALUE); \
- EXPECT_EQ(size, buf.size()); \
- EXPECT_EQ(expected, buf.str()); \
+#define EXPECT_ULEB128_EQ(EXPECTED, VALUE, PAD) \
+ do { \
+ std::string_view expected(EXPECTED, sizeof(EXPECTED) - 1); \
+ SmallString<32> buf; \
+ size_t size = WriteUleb128(buf, VALUE); \
+ EXPECT_EQ(size, buf.size()); \
+ EXPECT_EQ(expected, buf.str()); \
} while (0)
// Write ULEB128
@@ -48,6 +45,8 @@
EXPECT_ULEB128_EQ("\xff\x01", 0xff, 0);
EXPECT_ULEB128_EQ("\x80\x02", 0x100, 0);
EXPECT_ULEB128_EQ("\x81\x02", 0x101, 0);
+ EXPECT_ULEB128_EQ("\x80\x41", 0x2080, 0);
+ EXPECT_ULEB128_EQ("\x80\xc1\x80\x80\x10", 0x100002080, 0);
#undef EXPECT_ULEB128_EQ
}
@@ -73,7 +72,8 @@
EXPECT_READ_ULEB128_EQ(0xffu, "\xff\x01");
EXPECT_READ_ULEB128_EQ(0x100u, "\x80\x02");
EXPECT_READ_ULEB128_EQ(0x101u, "\x81\x02");
- EXPECT_READ_ULEB128_EQ(8320u, "\x80\xc1\x80\x80\x10");
+ EXPECT_READ_ULEB128_EQ(0x2080u, "\x80\x41");
+ EXPECT_READ_ULEB128_EQ(0x100002080u, "\x80\xc1\x80\x80\x10");
#undef EXPECT_READ_ULEB128_EQ
}
diff --git a/wpiutil/src/test/native/cpp/main.cpp b/wpiutil/src/test/native/cpp/main.cpp
index 1e5ecf0..09072ee 100644
--- a/wpiutil/src/test/native/cpp/main.cpp
+++ b/wpiutil/src/test/native/cpp/main.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2015-2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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"
diff --git a/wpiutil/src/test/native/cpp/priority_mutex_test.cpp b/wpiutil/src/test/native/cpp/priority_mutex_test.cpp
index 448a757..832e8f3 100644
--- a/wpiutil/src/test/native/cpp/priority_mutex_test.cpp
+++ b/wpiutil/src/test/native/cpp/priority_mutex_test.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-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. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/priority_mutex.h> // NOLINT(build/include_order)
@@ -58,7 +55,7 @@
void SetThreadRealtimePriorityOrDie(int32_t priority) {
struct sched_param param;
- // Set realtime priority for this thread
+ // Set real-time priority for this thread
param.sched_priority = priority + sched_get_priority_min(SCHED_RR);
ASSERT_EQ(pthread_setschedparam(pthread_self(), SCHED_RR, ¶m), 0)
<< ": Failed to set scheduler priority.";
@@ -225,7 +222,7 @@
// TODO: Fix roborio permissions to run as root.
// Priority inversion test.
-TEST(MutexTest, DISABLED_PriorityInversionTest) {
+TEST(MutexTest, DISABLED_PriorityInversion) {
InversionTestRunner<priority_mutex> runner;
std::thread runner_thread(std::ref(runner));
runner_thread.join();
@@ -233,7 +230,7 @@
}
// Verify that the non-priority inversion mutex doesn't pass the test.
-TEST(MutexTest, DISABLED_StdMutexPriorityInversionTest) {
+TEST(MutexTest, DISABLED_StdMutexPriorityInversion) {
InversionTestRunner<std::mutex> runner;
std::thread runner_thread(std::ref(runner));
runner_thread.join();
@@ -250,7 +247,7 @@
}
// Priority inversion test.
-TEST(MutexTest, DISABLED_ReentrantPriorityInversionTest) {
+TEST(MutexTest, DISABLED_ReentrantPriorityInversion) {
InversionTestRunner<priority_recursive_mutex> runner;
std::thread runner_thread(std::ref(runner));
runner_thread.join();
diff --git a/wpiutil/src/test/native/cpp/raw_uv_stream_test.cpp b/wpiutil/src/test/native/cpp/raw_uv_stream_test.cpp
index d1a0f6c..985fb8b 100644
--- a/wpiutil/src/test/native/cpp/raw_uv_stream_test.cpp
+++ b/wpiutil/src/test/native/cpp/raw_uv_stream_test.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/raw_uv_ostream.h" // NOLINT(build/include_order)
@@ -22,6 +19,10 @@
ASSERT_EQ(bufs[0].base[1], '2');
ASSERT_EQ(bufs[0].base[2], '3');
ASSERT_EQ(bufs[0].base[3], '4');
+
+ for (auto& buf : bufs) {
+ buf.Deallocate();
+ }
}
TEST(RawUvStreamTest, BoundaryWrite) {
@@ -34,6 +35,10 @@
ASSERT_EQ(bufs.size(), 1u);
os << "56";
ASSERT_EQ(bufs.size(), 2u);
+
+ for (auto& buf : bufs) {
+ buf.Deallocate();
+ }
}
TEST(RawUvStreamTest, LargeWrite) {
@@ -43,6 +48,10 @@
ASSERT_EQ(bufs.size(), 2u);
ASSERT_EQ(bufs[1].len, 2u);
ASSERT_EQ(bufs[1].base[0], '5');
+
+ for (auto& buf : bufs) {
+ buf.Deallocate();
+ }
}
TEST(RawUvStreamTest, PrevDataWrite) {
@@ -53,6 +62,10 @@
ASSERT_EQ(bufs.size(), 2u);
ASSERT_EQ(bufs[0].len, 1024u);
ASSERT_EQ(bufs[1].len, 4u);
+
+ for (auto& buf : bufs) {
+ buf.Deallocate();
+ }
}
} // namespace wpi
diff --git a/wpiutil/src/test/native/cpp/sha1Test.cpp b/wpiutil/src/test/native/cpp/sha1Test.cpp
index 08d85fe..0257431 100644
--- a/wpiutil/src/test/native/cpp/sha1Test.cpp
+++ b/wpiutil/src/test/native/cpp/sha1Test.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* 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. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
/*
test_sha1.cpp - test program of
diff --git a/wpiutil/src/test/native/cpp/sigslot/function-traits.cpp b/wpiutil/src/test/native/cpp/sigslot/function-traits.cpp
index 240e688..f31e296 100644
--- a/wpiutil/src/test/native/cpp/sigslot/function-traits.cpp
+++ b/wpiutil/src/test/native/cpp/sigslot/function-traits.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
/*
@@ -119,7 +116,7 @@
namespace wpi {
-TEST(Signal, FunctionTraits) {
+TEST(SignalTest, FunctionTraits) {
auto l1 = [](int, char, float) {};
auto l2 = [&](int, char, float) mutable {};
auto l3 = [&](auto...) mutable {};
diff --git a/wpiutil/src/test/native/cpp/sigslot/recursive.cpp b/wpiutil/src/test/native/cpp/sigslot/recursive.cpp
index 85ae9a9..82613f2 100644
--- a/wpiutil/src/test/native/cpp/sigslot/recursive.cpp
+++ b/wpiutil/src/test/native/cpp/sigslot/recursive.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
/*
@@ -42,7 +39,7 @@
template <typename T>
struct object {
- object(T i) : v{i} {} // NOLINT(runtime/explicit)
+ object(T i) : v{i} {} // NOLINT
void inc_val(const T& i) {
if (i != v) {
@@ -66,7 +63,7 @@
namespace wpi {
-TEST(Signal, Recursive) {
+TEST(SignalTest, Recursive) {
object<int> i1(-1);
object<int> i2(10);
@@ -78,7 +75,7 @@
ASSERT_EQ(i1.v, i2.v);
}
-TEST(Signal, SelfRecursive) {
+TEST(SignalTest, SelfRecursive) {
int i = 0;
wpi::sig::Signal_r<int> s;
diff --git a/wpiutil/src/test/native/cpp/sigslot/signal-extended.cpp b/wpiutil/src/test/native/cpp/sigslot/signal-extended.cpp
index 1ebbc8e..a0dbdfc 100644
--- a/wpiutil/src/test/native/cpp/sigslot/signal-extended.cpp
+++ b/wpiutil/src/test/native/cpp/sigslot/signal-extended.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
/*
@@ -71,7 +68,7 @@
namespace wpi {
-TEST(SignalExtended, FreeConnection) {
+TEST(SignalExtendedTest, FreeConnection) {
sum = 0;
Signal<int> sig;
sig.connect_extended(f);
@@ -82,7 +79,7 @@
ASSERT_EQ(sum, 1);
}
-TEST(SignalExtended, StaticConnection) {
+TEST(SignalExtendedTest, StaticConnection) {
sum = 0;
Signal<int> sig;
sig.connect_extended(&s::sf);
@@ -93,7 +90,7 @@
ASSERT_EQ(sum, 1);
}
-TEST(SignalExtended, PmfConnection) {
+TEST(SignalExtendedTest, PmfConnection) {
sum = 0;
Signal<int> sig;
s p;
@@ -105,7 +102,7 @@
ASSERT_EQ(sum, 1);
}
-TEST(SignalExtended, FunctionObjectConnection) {
+TEST(SignalExtendedTest, FunctionObjectConnection) {
sum = 0;
Signal<int> sig;
sig.connect_extended(o{});
@@ -116,7 +113,7 @@
ASSERT_EQ(sum, 1);
}
-TEST(SignalExtended, LambdaConnection) {
+TEST(SignalExtendedTest, LambdaConnection) {
sum = 0;
Signal<int> sig;
diff --git a/wpiutil/src/test/native/cpp/sigslot/signal-threaded.cpp b/wpiutil/src/test/native/cpp/sigslot/signal-threaded.cpp
index c4f7cdb..00001a8 100644
--- a/wpiutil/src/test/native/cpp/sigslot/signal-threaded.cpp
+++ b/wpiutil/src/test/native/cpp/sigslot/signal-threaded.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
/*
@@ -48,16 +45,22 @@
std::atomic<int> sum{0};
-void f(int i) { sum += i; }
+void f(int i) {
+ sum += i;
+}
void emit_many(Signal_mt<int>& sig) {
- for (int i = 0; i < 10000; ++i) sig(1);
+ for (int i = 0; i < 10000; ++i) {
+ sig(1);
+ }
}
void connect_emit(Signal_mt<int>& sig) {
for (int i = 0; i < 100; ++i) {
auto s = sig.connect_scoped(f);
- for (int j = 0; j < 100; ++j) sig(1);
+ for (int j = 0; j < 100; ++j) {
+ sig(1);
+ }
}
}
@@ -65,27 +68,35 @@
namespace wpi {
-TEST(Signal, ThreadedMix) {
+TEST(SignalTest, ThreadedMix) {
sum = 0;
Signal_mt<int> sig;
std::array<std::thread, 10> threads;
- for (auto& t : threads) t = std::thread(connect_emit, std::ref(sig));
+ for (auto& t : threads) {
+ t = std::thread(connect_emit, std::ref(sig));
+ }
- for (auto& t : threads) t.join();
+ for (auto& t : threads) {
+ t.join();
+ }
}
-TEST(Signal, ThreadedEmission) {
+TEST(SignalTest, ThreadedEmission) {
sum = 0;
Signal_mt<int> sig;
sig.connect(f);
std::array<std::thread, 10> threads;
- for (auto& t : threads) t = std::thread(emit_many, std::ref(sig));
+ for (auto& t : threads) {
+ t = std::thread(emit_many, std::ref(sig));
+ }
- for (auto& t : threads) t.join();
+ for (auto& t : threads) {
+ t.join();
+ }
ASSERT_EQ(sum, 100000);
}
diff --git a/wpiutil/src/test/native/cpp/sigslot/signal-tracking.cpp b/wpiutil/src/test/native/cpp/sigslot/signal-tracking.cpp
index 89ffd36..d05ee1a 100644
--- a/wpiutil/src/test/native/cpp/sigslot/signal-tracking.cpp
+++ b/wpiutil/src/test/native/cpp/sigslot/signal-tracking.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
/*
@@ -48,7 +45,9 @@
int sum = 0;
-void f1(int i) { sum += i; }
+void f1(int i) {
+ sum += i;
+}
struct o1 {
void operator()(int i) { sum += 2 * i; }
};
@@ -73,7 +72,7 @@
namespace wpi {
-TEST(Signal, TrackShared) {
+TEST(SignalTest, TrackShared) {
sum = 0;
Signal<int> sig;
@@ -96,7 +95,7 @@
ASSERT_EQ(sum, 5);
}
-TEST(Signal, TrackOther) {
+TEST(SignalTest, TrackOther) {
sum = 0;
Signal<int> sig;
@@ -119,7 +118,7 @@
ASSERT_EQ(sum, 5);
}
-TEST(Signal, TrackOverloadedFunctionObject) {
+TEST(SignalTest, TrackOverloadedFunctionObject) {
sum = 0;
Signal<int> sig;
Signal<double> sig1;
@@ -144,7 +143,7 @@
ASSERT_EQ(sum, 5);
}
-TEST(Signal, TrackGenericLambda) {
+TEST(SignalTest, TrackGenericLambda) {
std::stringstream s;
auto f = [&](auto a, auto... args) {
diff --git a/wpiutil/src/test/native/cpp/sigslot/signal.cpp b/wpiutil/src/test/native/cpp/sigslot/signal.cpp
index a4f9208..cc2ff80 100644
--- a/wpiutil/src/test/native/cpp/sigslot/signal.cpp
+++ b/wpiutil/src/test/native/cpp/sigslot/signal.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
/*
@@ -48,8 +45,12 @@
int sum = 0;
-void f1(int i) { sum += i; }
-void f2(int i) /*noexcept*/ { sum += 2 * i; }
+void f1(int i) {
+ sum += i;
+}
+void f2(int i) /*noexcept*/ {
+ sum += 2 * i;
+}
struct s {
static void s1(int i) { sum += i; }
@@ -99,7 +100,7 @@
namespace wpi {
-TEST(Signal, FreeConnection) {
+TEST(SignalTest, FreeConnection) {
sum = 0;
Signal<int> sig;
@@ -112,7 +113,7 @@
ASSERT_EQ(sum, 4);
}
-TEST(Signal, StaticConnection) {
+TEST(SignalTest, StaticConnection) {
sum = 0;
Signal<int> sig;
@@ -125,7 +126,7 @@
ASSERT_EQ(sum, 4);
}
-TEST(Signal, PmfConnection) {
+TEST(SignalTest, PmfConnection) {
sum = 0;
Signal<int> sig;
s p;
@@ -143,7 +144,7 @@
ASSERT_EQ(sum, 8);
}
-TEST(Signal, ConstPmfConnection) {
+TEST(SignalTest, ConstPmfConnection) {
sum = 0;
Signal<int> sig;
const s p;
@@ -157,7 +158,7 @@
ASSERT_EQ(sum, 4);
}
-TEST(Signal, FunctionObjectConnection) {
+TEST(SignalTest, FunctionObjectConnection) {
sum = 0;
Signal<int> sig;
@@ -174,7 +175,7 @@
ASSERT_EQ(sum, 8);
}
-TEST(Signal, OverloadedFunctionObjectConnection) {
+TEST(SignalTest, OverloadedFunctionObjectConnection) {
sum = 0;
Signal<int> sig;
Signal<double> sig1;
@@ -188,7 +189,7 @@
ASSERT_EQ(sum, 5);
}
-TEST(Signal, LambdaConnection) {
+TEST(SignalTest, LambdaConnection) {
sum = 0;
Signal<int> sig;
@@ -201,7 +202,7 @@
ASSERT_EQ(sum, 4);
}
-TEST(Signal, GenericLambdaConnection) {
+TEST(SignalTest, GenericLambdaConnection) {
std::stringstream s;
auto f = [&](auto a, auto... args) {
@@ -228,7 +229,7 @@
ASSERT_EQ(s.str(), "1foo4.1");
}
-TEST(Signal, LvalueEmission) {
+TEST(SignalTest, LvalueEmission) {
sum = 0;
Signal<int> sig;
@@ -242,7 +243,7 @@
ASSERT_EQ(sum, 4);
}
-TEST(Signal, Mutation) {
+TEST(SignalTest, Mutation) {
int res = 0;
Signal<int&> sig;
@@ -255,7 +256,7 @@
ASSERT_EQ(res, 4);
}
-TEST(Signal, CompatibleArgs) {
+TEST(SignalTest, CompatibleArgs) {
long ll = 0; // NOLINT(runtime/int)
std::string ss;
short ii = 0; // NOLINT(runtime/int)
@@ -275,7 +276,7 @@
ASSERT_EQ(ii, 1);
}
-TEST(Signal, Disconnection) {
+TEST(SignalTest, Disconnection) {
// test removing only connected
{
sum = 0;
@@ -327,7 +328,7 @@
}
}
-TEST(Signal, ScopedConnection) {
+TEST(SignalTest, ScopedConnection) {
sum = 0;
Signal<int> sig;
@@ -360,7 +361,7 @@
ASSERT_EQ(sum, 4);
}
-TEST(Signal, ConnectionBlocking) {
+TEST(SignalTest, ConnectionBlocking) {
sum = 0;
Signal<int> sig;
@@ -378,7 +379,7 @@
ASSERT_EQ(sum, 8);
}
-TEST(Signal, ConnectionBlocker) {
+TEST(SignalTest, ConnectionBlocker) {
sum = 0;
Signal<int> sig;
@@ -397,7 +398,7 @@
ASSERT_EQ(sum, 8);
}
-TEST(Signal, SignalBlocking) {
+TEST(SignalTest, SignalBlocking) {
sum = 0;
Signal<int> sig;
@@ -415,7 +416,7 @@
ASSERT_EQ(sum, 6);
}
-TEST(Signal, AllDisconnection) {
+TEST(SignalTest, AllDisconnection) {
sum = 0;
Signal<int> sig;
@@ -429,7 +430,7 @@
ASSERT_EQ(sum, 3);
}
-TEST(Signal, ConnectionCopyingMoving) {
+TEST(SignalTest, ConnectionCopyingMoving) {
sum = 0;
Signal<int> sig;
@@ -458,7 +459,7 @@
ASSERT_EQ(sum, 9);
}
-TEST(Signal, ScopedConnectionMoving) {
+TEST(SignalTest, ScopedConnectionMoving) {
sum = 0;
Signal<int> sig;
@@ -484,7 +485,7 @@
ASSERT_EQ(sum, 10);
}
-TEST(Signal, SignalMoving) {
+TEST(SignalTest, SignalMoving) {
sum = 0;
Signal<int> sig;
@@ -506,7 +507,7 @@
template <typename T>
struct object {
object();
- object(T i) : v{i} {} // NOLINT(runtime/explicit)
+ object(T i) : v{i} {} // NOLINT
const T& val() const { return v; }
T& val() { return v; }
@@ -524,7 +525,7 @@
Signal<T> s;
};
-TEST(Signal, Loop) {
+TEST(SignalTest, Loop) {
object<int> i1(0);
object<int> i2(3);
diff --git a/wpiutil/src/test/native/cpp/span/test_conversions.cpp b/wpiutil/src/test/native/cpp/span/test_conversions.cpp
new file mode 100644
index 0000000..fa111a7
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/span/test_conversions.cpp
@@ -0,0 +1,46 @@
+#if __has_include(<span>)
+#include <span>
+#endif
+#include "wpi/span.h"
+
+#include "gtest/gtest.h"
+
+void func1(wpi::span<const int> x) {}
+#ifdef __cpp_lib_span
+void func2(std::span<const int> x) {}
+#endif
+
+void func4(wpi::span<int> x) {}
+#ifdef __cpp_lib_span
+void func5(std::span<int> x) {}
+#endif
+
+TEST(Span, ConvertStdSpan) {
+ func1(wpi::span<const int>{});
+ func1(wpi::span<int>{});
+#ifdef __cpp_lib_span
+ func1(std::span<const int>{});
+ func1(std::span<int>{});
+#endif
+
+#ifdef __cpp_lib_span
+ func2(wpi::span<const int>{});
+ func2(wpi::span<int>{});
+ func2(std::span<const int>{});
+ func2(std::span<int>{});
+#endif
+
+ //func4(wpi::span<const int>{});
+ func4(wpi::span<int>{});
+#ifdef __cpp_lib_span
+ //func4(std::span<const int>{});
+ func4(std::span<int>{});
+#endif
+
+#ifdef __cpp_lib_span
+ //func5(wpi::span<const int>{});
+ func5(wpi::span<int>{});
+ //func5(std::span<const int>{});
+ func5(std::span<int>{});
+#endif
+}
diff --git a/wpiutil/src/test/native/cpp/span/test_deduction_guides.cpp b/wpiutil/src/test/native/cpp/span/test_deduction_guides.cpp
new file mode 100644
index 0000000..9851b5e
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/span/test_deduction_guides.cpp
@@ -0,0 +1,98 @@
+
+#include "wpi/span.h"
+
+#include "gtest/gtest.h"
+
+#include <vector>
+
+using wpi::span;
+
+namespace {
+
+template <typename R1, typename R2>
+constexpr bool equal(R1&& r1, R2&& r2)
+{
+ auto first1 = std::begin(r1);
+ const auto last1 = std::end(r1);
+ auto first2 = std::begin(r2);
+ const auto last2 = std::end(r2);
+
+ while (first1 != last1 && first2 != last2) {
+ if (*first1 != *first2) {
+ return false;
+ }
+ ++first1;
+ ++first2;
+ }
+
+ return true;
+}
+
+constexpr bool test_raw_array()
+{
+ int arr[] = {1, 2, 3};
+ auto s = span{arr};
+ static_assert(std::is_same_v<decltype(s), span<int, 3>>);
+
+ return equal(arr, s);
+}
+
+constexpr bool test_const_raw_array()
+{
+ constexpr int arr[] = {1, 2, 3};
+ auto s = span{arr};
+ static_assert(std::is_same_v<decltype(s), span<const int, 3>>);
+
+ return equal(arr, s);
+}
+
+constexpr bool test_std_array()
+{
+ auto arr = std::array<int, 3>{1, 2, 3};
+ auto s = span{arr};
+ static_assert(std::is_same_v<decltype(s), span<int, 3>>);
+
+ return equal(arr, s);
+}
+
+constexpr bool test_const_std_array()
+{
+ const auto arr = std::array<int, 3>{1, 2, 3};
+ auto s = span{arr};
+ static_assert(std::is_same_v<decltype(s), span<const int, 3>>);
+
+ return equal(arr, s);
+}
+
+bool test_vec()
+{
+ auto arr = std::vector{1, 2, 3};
+ auto s = span{arr};
+ static_assert(std::is_same_v<decltype(s), span<int>>);
+
+ return equal(arr, s);
+}
+
+bool test_const_vec()
+{
+ const auto arr = std::vector{1, 2, 3};
+ auto s = span{arr};
+ static_assert(std::is_same_v<decltype(s), span<const int>>);
+
+ return equal(arr, s);
+}
+
+}
+
+TEST(Deduction, FromRawArrays)
+{
+ static_assert(test_raw_array());
+ static_assert(test_const_raw_array());
+ static_assert(test_std_array());
+ static_assert(test_const_std_array());
+
+ ASSERT_TRUE(test_std_array());
+ ASSERT_TRUE(test_const_std_array());
+ ASSERT_TRUE(test_vec());
+ ASSERT_TRUE(test_const_vec());
+}
diff --git a/wpiutil/src/test/native/cpp/span/test_span.cpp b/wpiutil/src/test/native/cpp/span/test_span.cpp
new file mode 100644
index 0000000..7cecfd0
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/span/test_span.cpp
@@ -0,0 +1,663 @@
+
+#include "wpi/span.h"
+
+#include <algorithm>
+#include <cassert>
+#include <deque>
+#include <initializer_list>
+#include <vector>
+
+#include "gtest/gtest.h"
+
+using wpi::span;
+
+struct base {};
+struct derived : base {};
+
+TEST(Span, DefaultConstruction)
+{
+ static_assert(std::is_nothrow_default_constructible<span<int>>::value, "");
+ static_assert(std::is_nothrow_default_constructible<span<int, 0>>::value,
+ "");
+ static_assert(!std::is_default_constructible<span<int, 42>>::value, "");
+
+ //SECTION("dynamic size")
+ {
+ constexpr span<int> s{};
+ static_assert(s.size() == 0, "");
+ static_assert(s.data() == nullptr, "");
+ // This causes an ICE on MSVC
+#ifndef _MSC_VER
+ static_assert(s.begin() == s.end(), "");
+#else
+ ASSERT_EQ(s.begin(), s.end());
+#endif
+ }
+
+ //SECTION("fixed size")
+ {
+ constexpr span<int, 0> s{};
+ static_assert(s.size() == 0, "");
+ static_assert(s.data() == nullptr, "");
+#ifndef _MSC_VER
+ static_assert(s.begin() == s.end(), "");
+#else
+ ASSERT_EQ(s.begin(), s.end());
+#endif
+ }
+}
+
+TEST(Span, PointerLengthConstruction)
+{
+ static_assert(std::is_constructible<span<int>, int*, int>::value, "");
+ static_assert(std::is_constructible<span<const int>, int*, int>::value, "");
+ static_assert(
+ std::is_constructible<span<const int>, const int*, int>::value, "");
+
+ static_assert(std::is_constructible<span<int, 42>, int*, int>::value, "");
+ static_assert(std::is_constructible<span<const int, 42>, int*, int>::value,
+ "");
+ static_assert(
+ std::is_constructible<span<const int, 42>, const int*, int>::value, "");
+
+ //SECTION("dynamic size")
+ {
+ int arr[] = {1, 2, 3};
+ span<int> s(arr, 3);
+
+ ASSERT_EQ(s.size(), 3u);
+ ASSERT_EQ(s.data(), arr);
+ ASSERT_EQ(s.begin(), std::begin(arr));
+ ASSERT_EQ(s.end(), std::end(arr));
+ }
+
+ //SECTION("fixed size")
+ {
+ int arr[] = {1, 2, 3};
+ span<int, 3> s(arr, 3);
+
+ ASSERT_EQ(s.size(), 3u);
+ ASSERT_EQ(s.data(), arr);
+ ASSERT_EQ(s.begin(), std::begin(arr));
+ ASSERT_EQ(s.end(), std::end(arr));
+ }
+}
+
+TEST(Span, PointerPointerConstruction)
+{
+ static_assert(std::is_constructible<span<int>, int*, int*>::value, "");
+ static_assert(!std::is_constructible<span<int>, float*, float*>::value, "");
+ static_assert(std::is_constructible<span<int, 42>, int*, int*>::value, "");
+ static_assert(!std::is_constructible<span<int, 42>, float*, float*>::value,
+ "");
+
+ //SECTION("dynamic size")
+ {
+ int arr[] = {1, 2, 3};
+ span<int> s{arr, arr + 3};
+ ASSERT_EQ(s.size(), 3u);
+ ASSERT_EQ(s.data(), arr);
+ ASSERT_EQ(s.begin(), std::begin(arr));
+ ASSERT_EQ(s.end(), std::end(arr));
+ }
+
+ //SECTION("fixed size")
+ {
+ int arr[] = {1, 2, 3};
+ span<int, 3> s{arr, arr + 3};
+ ASSERT_EQ(s.size(), 3u);
+ ASSERT_EQ(s.data(), arr);
+ ASSERT_EQ(s.begin(), std::begin(arr));
+ ASSERT_EQ(s.end(), std::end(arr));
+ }
+}
+
+TEST(Span, CArrayConstruction)
+{
+ using int_array_t = int[3];
+ using float_array_t = float[3];
+
+ static_assert(std::is_nothrow_constructible<span<int>, int_array_t&>::value,
+ "");
+ static_assert(!std::is_constructible<span<int>, int_array_t const&>::value,
+ "");
+ static_assert(!std::is_constructible<span<int>, float_array_t>::value, "");
+
+ static_assert(
+ std::is_nothrow_constructible<span<const int>, int_array_t&>::value,
+ "");
+ static_assert(std::is_nothrow_constructible<span<const int>,
+ int_array_t const&>::value,
+ "");
+ static_assert(!std::is_constructible<span<const int>, float_array_t>::value,
+ "");
+
+ static_assert(
+ std::is_nothrow_constructible<span<int, 3>, int_array_t&>::value, "");
+ static_assert(
+ !std::is_constructible<span<int, 3>, int_array_t const&>::value, "");
+ static_assert(!std::is_constructible<span<int, 3>, float_array_t&>::value,
+ "");
+
+ static_assert(
+ std::is_nothrow_constructible<span<const int, 3>, int_array_t&>::value,
+ "");
+ static_assert(std::is_nothrow_constructible<span<const int, 3>,
+ int_array_t const&>::value,
+ "");
+ static_assert(
+ !std::is_constructible<span<const int, 3>, float_array_t>::value, "");
+
+ static_assert(!std::is_constructible<span<int, 42>, int_array_t&>::value,
+ "");
+ static_assert(
+ !std::is_constructible<span<int, 42>, int_array_t const&>::value, "");
+ static_assert(!std::is_constructible<span<int, 42>, float_array_t&>::value,
+ "");
+
+ static_assert(
+ !std::is_constructible<span<const int, 42>, int_array_t&>::value, "");
+ static_assert(
+ !std::is_constructible<span<const int, 42>, int_array_t const&>::value,
+ "");
+ static_assert(
+ !std::is_constructible<span<const int, 42>, float_array_t&>::value, "");
+
+ //SECTION("non-const, dynamic size")
+ {
+ int arr[] = {1, 2, 3};
+ span<int> s{arr};
+ ASSERT_EQ(s.size(), 3u);
+ ASSERT_EQ(s.data(), arr);
+ ASSERT_EQ(s.begin(), std::begin(arr));
+ ASSERT_EQ(s.end(), std::end(arr));
+ }
+
+ //SECTION("const, dynamic size")
+ {
+ int arr[] = {1, 2, 3};
+ span<int const> s{arr};
+ ASSERT_EQ(s.size(), 3u);
+ ASSERT_EQ(s.data(), arr);
+ ASSERT_EQ(s.begin(), std::begin(arr));
+ ASSERT_EQ(s.end(), std::end(arr));
+ }
+
+ //SECTION("non-const, static size")
+ {
+ int arr[] = {1, 2, 3};
+ span<int, 3> s{arr};
+ ASSERT_EQ(s.size(), 3u);
+ ASSERT_EQ(s.data(), arr);
+ ASSERT_EQ(s.begin(), std::begin(arr));
+ ASSERT_EQ(s.end(), std::end(arr));
+ }
+
+ //SECTION("const, dynamic size")
+ {
+ int arr[] = {1, 2, 3};
+ span<int const, 3> s{arr};
+ ASSERT_EQ(s.size(), 3u);
+ ASSERT_EQ(s.data(), arr);
+ ASSERT_EQ(s.begin(), std::begin(arr));
+ ASSERT_EQ(s.end(), std::end(arr));
+ }
+}
+
+TEST(Span, StdArrayConstruction)
+{
+ using int_array_t = std::array<int, 3>;
+ using float_array_t = std::array<float, 3>;
+ using zero_array_t = std::array<int, 0>;
+
+ static_assert(std::is_nothrow_constructible<span<int>, int_array_t&>::value,
+ "");
+ static_assert(!std::is_constructible<span<int>, int_array_t const&>::value,
+ "");
+ static_assert(!std::is_constructible<span<int>, float_array_t>::value, "");
+
+ static_assert(
+ std::is_nothrow_constructible<span<const int>, int_array_t&>::value,
+ "");
+ static_assert(std::is_nothrow_constructible<span<const int>,
+ int_array_t const&>::value,
+ "");
+ static_assert(
+ !std::is_constructible<span<const int>, float_array_t const&>::value,
+ "");
+
+ static_assert(
+ std::is_nothrow_constructible<span<int, 3>, int_array_t&>::value, "");
+ static_assert(
+ !std::is_constructible<span<int, 3>, int_array_t const&>::value, "");
+ static_assert(!std::is_constructible<span<int, 3>, float_array_t>::value,
+ "");
+
+ static_assert(
+ std::is_nothrow_constructible<span<const int, 3>, int_array_t&>::value,
+ "");
+ static_assert(std::is_nothrow_constructible<span<const int, 3>,
+ int_array_t const&>::value,
+ "");
+ static_assert(
+ !std::is_constructible<span<const int, 3>, float_array_t const&>::value,
+ "");
+
+ static_assert(!std::is_constructible<span<int, 42>, int_array_t&>::value,
+ "");
+ static_assert(
+ !std::is_constructible<span<int, 42>, int_array_t const&>::value, "");
+ static_assert(
+ !std::is_constructible<span<int, 42>, float_array_t const&>::value, "");
+
+ static_assert(
+ !std::is_constructible<span<const int, 42>, int_array_t&>::value, "");
+ static_assert(
+ !std::is_constructible<span<const int, 42>, int_array_t const&>::value,
+ "");
+ static_assert(
+ !std::is_constructible<span<const int, 42>, float_array_t&>::value, "");
+
+ static_assert(std::is_constructible<span<int>, zero_array_t&>::value, "");
+ static_assert(!std::is_constructible<span<int>, const zero_array_t&>::value,
+ "");
+ static_assert(std::is_constructible<span<const int>, zero_array_t&>::value,
+ "");
+ static_assert(
+ std::is_constructible<span<const int>, const zero_array_t&>::value, "");
+
+ static_assert(std::is_constructible<span<int, 0>, zero_array_t&>::value,
+ "");
+ static_assert(
+ !std::is_constructible<span<int, 0>, const zero_array_t&>::value, "");
+ static_assert(
+ std::is_constructible<span<const int, 0>, zero_array_t&>::value, "");
+ static_assert(
+ std::is_constructible<span<const int, 0>, const zero_array_t&>::value,
+ "");
+
+ //SECTION("non-const, dynamic size")
+ {
+ int_array_t arr = {1, 2, 3};
+ span<int> s{arr};
+ ASSERT_EQ(s.size(), 3u);
+ ASSERT_EQ(s.data(), arr.data());
+ ASSERT_EQ(s.begin(), arr.data());
+ ASSERT_EQ(s.end(), arr.data() + 3);
+ }
+
+ //SECTION("const, dynamic size")
+ {
+ int_array_t arr = {1, 2, 3};
+ span<int const> s{arr};
+ ASSERT_EQ(s.size(), 3u);
+ ASSERT_EQ(s.data(), arr.data());
+ ASSERT_EQ(s.begin(), arr.data());
+ ASSERT_EQ(s.end(), arr.data() + 3);
+ }
+
+ //SECTION("non-const, static size")
+ {
+ int_array_t arr = {1, 2, 3};
+ span<int, 3> s{arr};
+ ASSERT_EQ(s.size(), 3u);
+ ASSERT_EQ(s.data(), arr.data());
+ ASSERT_EQ(s.begin(), arr.data());
+ ASSERT_EQ(s.end(), arr.data() + 3);
+ }
+
+ //SECTION("const, dynamic size")
+ {
+ int_array_t arr = {1, 2, 3};
+ span<int const, 3> s{arr};
+ ASSERT_EQ(s.size(), 3u);
+ ASSERT_EQ(s.data(), arr.data());
+ ASSERT_EQ(s.begin(), arr.data());
+ ASSERT_EQ(s.end(), arr.data() + 3);
+ }
+}
+
+TEST(Span, ConstructionFromOtherContainers)
+{
+ using vec_t = std::vector<int>;
+ using deque_t = std::deque<int>;
+
+ static_assert(std::is_constructible<span<int>, vec_t&>::value, "");
+ static_assert(!std::is_constructible<span<int>, const vec_t&>::value, "");
+ static_assert(!std::is_constructible<span<int>, const deque_t&>::value, "");
+
+ static_assert(std::is_constructible<span<const int>, vec_t&>::value, "");
+ static_assert(std::is_constructible<span<const int>, const vec_t&>::value,
+ "");
+ static_assert(
+ !std::is_constructible<span<const int>, const deque_t&>::value, "");
+
+ static_assert(!std::is_constructible<span<int, 3>, vec_t&>::value, "");
+ static_assert(!std::is_constructible<span<int, 3>, const vec_t&>::value,
+ "");
+ static_assert(!std::is_constructible<span<int, 3>, const deque_t&>::value,
+ "");
+
+ static_assert(!std::is_constructible<span<const int, 3>, vec_t&>::value, "");
+ static_assert(
+ !std::is_constructible<span<const int, 3>, const vec_t&>::value, "");
+ static_assert(
+ !std::is_constructible<span<const int, 3>, const deque_t&>::value, "");
+
+ // vector<bool> is not contiguous and cannot be converted to span<bool>
+ // Regression test for https://github.com/tcbrindle/span/issues/24
+ static_assert(
+ !std::is_constructible<span<bool>, std::vector<bool>&>::value, "");
+ static_assert(!std::is_constructible<span<const bool>,
+ const std::vector<bool>&>::value, "");
+
+ //SECTION("non-const, dynamic size")
+ {
+ vec_t arr = {1, 2, 3};
+ span<int> s{arr};
+ ASSERT_EQ(s.size(), 3u);
+ ASSERT_EQ(s.data(), arr.data());
+ ASSERT_EQ(s.begin(), arr.data());
+ ASSERT_EQ(s.end(), arr.data() + 3);
+ }
+
+ //SECTION("const, dynamic size")
+ {
+ vec_t arr = {1, 2, 3};
+ span<int const> s{arr};
+ ASSERT_EQ(s.size(), 3u);
+ ASSERT_EQ(s.data(), arr.data());
+ ASSERT_EQ(s.begin(), arr.data());
+ ASSERT_EQ(s.end(), arr.data() + 3);
+ }
+
+ //SECTION("non-const, static size")
+ {
+ std::array<int, 3> arr = {1, 2, 3};
+ span<int, 3> s{arr};
+ ASSERT_EQ(s.size(), 3u);
+ ASSERT_EQ(s.data(), arr.data());
+ ASSERT_EQ(s.begin(), arr.data());
+ ASSERT_EQ(s.end(), arr.data() + 3);
+ }
+
+ //SECTION("const, dynamic size")
+ {
+ std::array<int, 3> arr = {1, 2, 3};
+ span<int const, 3> s{arr};
+ ASSERT_EQ(s.size(), 3u);
+ ASSERT_EQ(s.data(), arr.data());
+ ASSERT_EQ(s.begin(), arr.data());
+ ASSERT_EQ(s.end(), arr.data() + 3);
+ }
+}
+
+TEST(Span, ConstructionFromSpansOfDifferentSize)
+{
+ using zero_span = span<int, 0>;
+ using zero_const_span = span<const int, 0>;
+ using big_span = span<int, 1000000>;
+ using big_const_span = span<const int, 1000000>;
+ using dynamic_span = span<int>;
+ using dynamic_const_span = span<const int>;
+
+ static_assert(std::is_trivially_copyable<zero_span>::value, "");
+ static_assert(std::is_trivially_move_constructible<zero_span>::value, "");
+ static_assert(!std::is_constructible<zero_span, zero_const_span>::value,
+ "");
+ static_assert(!std::is_constructible<zero_span, big_span>::value, "");
+ static_assert(!std::is_constructible<zero_span, big_const_span>::value, "");
+ static_assert(!std::is_constructible<zero_span, dynamic_span>::value, "");
+ static_assert(!std::is_constructible<zero_span, dynamic_const_span>::value,
+ "");
+
+ static_assert(
+ std::is_nothrow_constructible<zero_const_span, zero_span>::value, "");
+ static_assert(std::is_trivially_copyable<zero_const_span>::value, "");
+ static_assert(std::is_trivially_move_constructible<zero_const_span>::value,
+ "");
+ static_assert(!std::is_constructible<zero_const_span, big_span>::value, "");
+ static_assert(
+ !std::is_constructible<zero_const_span, big_const_span>::value, "");
+ static_assert(!std::is_constructible<zero_const_span, dynamic_span>::value,
+ "");
+ static_assert(
+ !std::is_constructible<zero_const_span, dynamic_const_span>::value, "");
+
+ static_assert(!std::is_constructible<big_span, zero_span>::value, "");
+ static_assert(!std::is_constructible<big_span, zero_const_span>::value, "");
+ static_assert(std::is_trivially_copyable<big_span>::value, "");
+ static_assert(std::is_trivially_move_constructible<big_span>::value, "");
+ static_assert(!std::is_constructible<big_span, big_const_span>::value, "");
+ static_assert(!std::is_constructible<big_span, dynamic_span>::value, "");
+ static_assert(!std::is_constructible<big_span, dynamic_const_span>::value,
+ "");
+
+ static_assert(!std::is_constructible<big_const_span, zero_span>::value, "");
+ static_assert(
+ !std::is_constructible<big_const_span, zero_const_span>::value, "");
+ static_assert(std::is_trivially_copyable<big_const_span>::value, "");
+ static_assert(std::is_trivially_move_constructible<big_const_span>::value,
+ "");
+ static_assert(
+ std::is_nothrow_constructible<big_const_span, big_span>::value, "");
+ static_assert(!std::is_constructible<big_const_span, dynamic_span>::value,
+ "");
+ static_assert(
+ !std::is_constructible<big_const_span, dynamic_const_span>::value, "");
+
+ static_assert(std::is_nothrow_constructible<dynamic_span, zero_span>::value,
+ "");
+ static_assert(!std::is_constructible<dynamic_span, zero_const_span>::value,
+ "");
+ static_assert(std::is_nothrow_constructible<dynamic_span, big_span>::value,
+ "");
+ static_assert(!std::is_constructible<dynamic_span, big_const_span>::value,
+ "");
+ static_assert(std::is_trivially_copyable<dynamic_span>::value, "");
+ static_assert(std::is_trivially_move_constructible<dynamic_span>::value,
+ "");
+ static_assert(
+ !std::is_constructible<dynamic_span, dynamic_const_span>::value, "");
+
+ static_assert(
+ std::is_nothrow_constructible<dynamic_const_span, zero_span>::value,
+ "");
+ static_assert(std::is_nothrow_constructible<dynamic_const_span,
+ zero_const_span>::value,
+ "");
+ static_assert(
+ std::is_nothrow_constructible<dynamic_const_span, big_span>::value, "");
+ static_assert(std::is_nothrow_constructible<dynamic_const_span,
+ big_const_span>::value,
+ "");
+ static_assert(
+ std::is_nothrow_constructible<dynamic_const_span, dynamic_span>::value,
+ "");
+ static_assert(std::is_trivially_copyable<dynamic_const_span>::value, "");
+ static_assert(
+ std::is_trivially_move_constructible<dynamic_const_span>::value, "");
+
+ constexpr zero_const_span s0{};
+ constexpr dynamic_const_span d{s0};
+
+ static_assert(d.size() == 0, "");
+ static_assert(d.data() == nullptr, "");
+#ifndef _MSC_VER
+ static_assert(d.begin() == d.end(), "");
+#else
+ ASSERT_EQ(d.begin(), d.end());
+#endif
+}
+
+TEST(Span, MemberSubviewOperations)
+{
+ //SECTION("first<N>")
+ {
+ int arr[] = {1, 2, 3, 4, 5};
+ span<int, 5> s{arr};
+ auto f = s.first<3>();
+
+ static_assert(std::is_same<decltype(f), span<int, 3>>::value, "");
+ ASSERT_EQ(f.size(), 3u);
+ ASSERT_EQ(f.data(), arr);
+ ASSERT_EQ(f.begin(), arr);
+ ASSERT_EQ(f.end(), arr + 3);
+ }
+
+ //SECTION("last<N>")
+ {
+ int arr[] = {1, 2, 3, 4, 5};
+ span<int, 5> s{arr};
+ auto l = s.last<3>();
+
+ static_assert(std::is_same<decltype(l), span<int, 3>>::value, "");
+ ASSERT_EQ(l.size(), 3u);
+ ASSERT_EQ(l.data(), arr + 2);
+ ASSERT_EQ(l.begin(), arr + 2);
+ ASSERT_EQ(l.end(), std::end(arr));
+ }
+
+ //SECTION("subspan<N>")
+ {
+ int arr[] = {1, 2, 3, 4, 5};
+ span<int, 5> s{arr};
+ auto ss = s.subspan<1, 2>();
+
+ static_assert(std::is_same<decltype(ss), span<int, 2>>::value, "");
+ ASSERT_EQ(ss.size(), 2u);
+ ASSERT_EQ(ss.data(), arr + 1);
+ ASSERT_EQ(ss.begin(), arr + 1);
+ ASSERT_EQ(ss.end(), arr + 1 + 2);
+ }
+
+ //SECTION("first(n)")
+ {
+ int arr[] = {1, 2, 3, 4, 5};
+ span<int, 5> s{arr};
+ auto f = s.first(3);
+
+ static_assert(std::is_same<decltype(f), span<int>>::value, "");
+ ASSERT_EQ(f.size(), 3u);
+ ASSERT_EQ(f.data(), arr);
+ ASSERT_EQ(f.begin(), arr);
+ ASSERT_EQ(f.end(), arr + 3);
+ }
+
+ //SECTION("last(n)")
+ {
+ int arr[] = {1, 2, 3, 4, 5};
+ span<int, 5> s{arr};
+ auto l = s.last(3);
+
+ static_assert(std::is_same<decltype(l), span<int>>::value, "");
+ ASSERT_EQ(l.size(), 3u);
+ ASSERT_EQ(l.data(), arr + 2);
+ ASSERT_EQ(l.begin(), arr + 2);
+ ASSERT_EQ(l.end(), std::end(arr));
+ }
+
+ //SECTION("subspan(n)")
+ {
+ int arr[] = {1, 2, 3, 4, 5};
+ span<int, 5> s{arr};
+ auto ss = s.subspan(1, 2);
+
+ static_assert(std::is_same<decltype(ss), span<int>>::value, "");
+ ASSERT_EQ(ss.size(), 2u);
+ ASSERT_EQ(ss.data(), arr + 1);
+ ASSERT_EQ(ss.begin(), arr + 1);
+ ASSERT_EQ(ss.end(), arr + 1 + 2);
+ }
+
+ // TODO: Test all the dynamic subspan possibilities
+}
+
+TEST(Span, Observers)
+{
+ // We already use this everywhere, but whatever
+ constexpr span<int, 0> empty{};
+ static_assert(empty.size() == 0, "");
+ static_assert(empty.empty(), "");
+
+ constexpr int arr[] = {1, 2, 3};
+ static_assert(span<const int>{arr}.size() == 3, "");
+ static_assert(!span<const int>{arr}.empty(), "");
+}
+
+TEST(Span, ElementAccess)
+{
+ constexpr int arr[] = {1, 2, 3};
+ span<const int> s{arr};
+
+ ASSERT_EQ(s[0], arr[0]);
+ ASSERT_EQ(s[1], arr[1]);
+ ASSERT_EQ(s[2], arr[2]);
+}
+
+TEST(Span, IteratorSupport)
+{
+ {
+ std::vector<int> vec;
+ span<int> s{vec};
+ std::sort(s.begin(), s.end());
+ ASSERT_TRUE(std::is_sorted(vec.cbegin(), vec.cend()));
+ }
+
+ {
+ const std::vector<int> vec{1, 2, 3};
+ span<const int> s{vec};
+ ASSERT_TRUE(std::equal(s.rbegin(), s.rend(), vec.crbegin()));
+ }
+}
+
+TEST(Span, MakeSpan)
+{
+ {
+ int arr[3] = {1, 2, 3};
+ auto s = wpi::span(arr);
+ static_assert(std::is_same<decltype(s), span<int, 3>>::value, "");
+ ASSERT_EQ(s.data(), arr);
+ ASSERT_EQ(s.size(), 3u);
+ }
+
+ {
+ const int arr[3] = {1, 2, 3};
+ auto s = wpi::span(arr);
+ static_assert(std::is_same<decltype(s), span<const int, 3>>::value, "");
+ ASSERT_EQ(s.data(), arr);
+ ASSERT_EQ(s.size(), 3u);
+ }
+
+ {
+ std::array<int, 3> arr = {1, 2, 3};
+ auto s = wpi::span(arr);
+ static_assert(std::is_same<decltype(s), span<int, 3>>::value, "");
+ ASSERT_EQ(s.data(), arr.data());
+ ASSERT_EQ(s.size(), arr.size());
+ }
+
+ {
+ const std::array<int, 3> arr = {1, 2, 3};
+ auto s = wpi::span(arr);
+ static_assert(std::is_same<decltype(s), span<const int, 3>>::value, "");
+ ASSERT_EQ(s.data(), arr.data());
+ ASSERT_EQ(s.size(), 3u);
+ }
+
+ {
+ std::vector<int> arr = {1, 2, 3};
+ auto s = wpi::span(arr);
+ static_assert(std::is_same<decltype(s), span<int>>::value, "");
+ ASSERT_EQ(s.data(), arr.data());
+ ASSERT_EQ(s.size(), arr.size());
+ }
+
+ {
+ const std::vector<int> arr = {1, 2, 3};
+ auto s = wpi::span(arr);
+ static_assert(std::is_same<decltype(s), span<const int>>::value, "");
+ ASSERT_EQ(s.data(), arr.data());
+ ASSERT_EQ(s.size(), arr.size());
+ }
+}
diff --git a/wpiutil/src/test/native/cpp/span/test_structured_bindings.cpp b/wpiutil/src/test/native/cpp/span/test_structured_bindings.cpp
new file mode 100644
index 0000000..5ce2969
--- /dev/null
+++ b/wpiutil/src/test/native/cpp/span/test_structured_bindings.cpp
@@ -0,0 +1,30 @@
+
+#include "wpi/span.h"
+
+#include "gtest/gtest.h"
+
+using static_span_t = wpi::span<int, 3>;
+using dynamic_span_t = wpi::span<int>;
+
+static_assert(std::tuple_size_v<static_span_t> == static_span_t::extent);
+static_assert(!wpi::detail::is_complete<std::tuple_size<dynamic_span_t>>::value);
+
+TEST(StructuredBindings, Test)
+{
+ // C++, why you no let me do constexpr structured bindings?
+
+ int arr[] = {1, 2, 3};
+
+ auto& [a1, a2, a3] = arr;
+ auto&& [s1, s2, s3] = wpi::span(arr);
+
+ ASSERT_EQ(a1, s1);
+ ASSERT_EQ(a2, s2);
+ ASSERT_EQ(a3, s3);
+
+ a1 = 99;
+ ASSERT_EQ(s1, 99);
+
+ s2 = 100;
+ ASSERT_EQ(a2, 100);
+}
diff --git a/wpiutil/src/test/native/cpp/spinlock_bench.cpp b/wpiutil/src/test/native/cpp/spinlock_bench.cpp
index b36e558..9534837 100644
--- a/wpiutil/src/test/native/cpp/spinlock_bench.cpp
+++ b/wpiutil/src/test/native/cpp/spinlock_bench.cpp
@@ -1,17 +1,14 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/spinlock.h" // NOLINT(build/include_order)
#include <chrono>
-#include <iostream>
#include <mutex>
#include <thread>
+#include "fmt/core.h"
#include "gtest/gtest.h"
#include "wpi/mutex.h"
@@ -53,9 +50,8 @@
++value;
}
auto stop = high_resolution_clock::now();
- std::cout << "std::mutex sizeof: " << sizeof(std_mutex)
- << " time: " << duration_cast<microseconds>(stop - start).count()
- << " value: " << value << "\n";
+ fmt::print("std::mutex sizeof: {} time: {} value: {}\n", sizeof(std_mutex),
+ duration_cast<microseconds>(stop - start).count(), value);
});
thrb.join();
@@ -68,9 +64,9 @@
++value;
}
auto stop = high_resolution_clock::now();
- std::cout << "std::recursive_mutex sizeof: " << sizeof(std_recursive_mutex)
- << " time: " << duration_cast<microseconds>(stop - start).count()
- << " value: " << value << "\n";
+ fmt::print("std::recursive_mutex sizeof: {} time: {} value: {}\n",
+ sizeof(std_recursive_mutex),
+ duration_cast<microseconds>(stop - start).count(), value);
});
thrb2.join();
@@ -83,9 +79,8 @@
++value;
}
auto stop = high_resolution_clock::now();
- std::cout << "wpi::mutex sizeof: " << sizeof(wpi_mutex)
- << " time: " << duration_cast<microseconds>(stop - start).count()
- << " value: " << value << "\n";
+ fmt::print("wpi::mutex sizeof: {} time: {} value: {}\n", sizeof(wpi_mutex),
+ duration_cast<microseconds>(stop - start).count(), value);
});
thr2.join();
@@ -98,9 +93,9 @@
++value;
}
auto stop = high_resolution_clock::now();
- std::cout << "wpi::recursive_mutex sizeof: " << sizeof(wpi_recursive_mutex)
- << " time: " << duration_cast<microseconds>(stop - start).count()
- << " value: " << value << "\n";
+ fmt::print("wpi::recursive_mutex sizeof: {} time: {} value: {}\n",
+ sizeof(wpi_recursive_mutex),
+ duration_cast<microseconds>(stop - start).count(), value);
});
thr2b.join();
@@ -113,9 +108,8 @@
++value;
}
auto stop = high_resolution_clock::now();
- std::cout << "spinlock sizeof: " << sizeof(spinlock)
- << " time: " << duration_cast<microseconds>(stop - start).count()
- << " value: " << value << "\n";
+ fmt::print("spinlock sizeof: {} time: {} value: {}\n", sizeof(spinlock),
+ duration_cast<microseconds>(stop - start).count(), value);
});
thr3.join();
@@ -128,9 +122,9 @@
++value;
}
auto stop = high_resolution_clock::now();
- std::cout << "recursive_spinlock1 sizeof: " << sizeof(recursive_spinlock1)
- << " time: " << duration_cast<microseconds>(stop - start).count()
- << " value: " << value << "\n";
+ fmt::print("recursive_spinlock1 sizeof: {} time: {} value: {}\n",
+ sizeof(recursive_spinlock1),
+ duration_cast<microseconds>(stop - start).count(), value);
});
thr4.join();
@@ -143,9 +137,9 @@
++value;
}
auto stop = high_resolution_clock::now();
- std::cout << "recursive_spinlock2 sizeof: " << sizeof(recursive_spinlock2)
- << " time: " << duration_cast<microseconds>(stop - start).count()
- << " value: " << value << "\n";
+ fmt::print("recursive_spinlock2 sizeof: {} time: {} value: {}\n",
+ sizeof(recursive_spinlock2),
+ duration_cast<microseconds>(stop - start).count(), value);
});
thr4b.join();
@@ -158,9 +152,9 @@
++value;
}
auto stop = high_resolution_clock::now();
- std::cout << "recursive_spinlock sizeof: " << sizeof(recursive_spinlock)
- << " time: " << duration_cast<microseconds>(stop - start).count()
- << " value: " << value << "\n";
+ fmt::print("recursive_spinlock sizeof: {} time: {} value: {}\n",
+ sizeof(recursive_spinlock),
+ duration_cast<microseconds>(stop - start).count(), value);
});
thr4c.join();
}
diff --git a/wpiutil/src/test/native/cpp/uv/UvAsyncFunctionTest.cpp b/wpiutil/src/test/native/cpp/uv/UvAsyncFunctionTest.cpp
index 132adea..8407935 100644
--- a/wpiutil/src/test/native/cpp/uv/UvAsyncFunctionTest.cpp
+++ b/wpiutil/src/test/native/cpp/uv/UvAsyncFunctionTest.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/uv/AsyncFunction.h" // NOLINT(build/include_order)
@@ -14,10 +11,9 @@
#include "wpi/uv/Loop.h"
#include "wpi/uv/Prepare.h"
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
-TEST(UvAsyncFunction, Test) {
+TEST(UvAsyncFunctionTest, Basic) {
int prepare_cb_called = 0;
int async_cb_called[2] = {0, 0};
int close_cb_called = 0;
@@ -32,7 +28,9 @@
prepare->error.connect([](Error) { FAIL(); });
prepare->prepare.connect([&] {
- if (prepare_cb_called++) return;
+ if (prepare_cb_called++) {
+ return;
+ }
theThread = std::thread([&] {
auto call0 = async->Call(0);
auto call1 = async->Call(1);
@@ -59,10 +57,12 @@
ASSERT_EQ(async_cb_called[1], 1);
ASSERT_EQ(close_cb_called, 1);
- if (theThread.joinable()) theThread.join();
+ if (theThread.joinable()) {
+ theThread.join();
+ }
}
-TEST(UvAsyncFunction, Ref) {
+TEST(UvAsyncFunctionTest, Ref) {
int prepare_cb_called = 0;
int val = 0;
@@ -73,7 +73,9 @@
auto prepare = Prepare::Create(loop);
prepare->prepare.connect([&] {
- if (prepare_cb_called++) return;
+ if (prepare_cb_called++) {
+ return;
+ }
theThread = std::thread([&] { ASSERT_EQ(async->Call(1, val).get(), 2); });
});
prepare->Start();
@@ -89,10 +91,12 @@
ASSERT_EQ(val, 1);
- if (theThread.joinable()) theThread.join();
+ if (theThread.joinable()) {
+ theThread.join();
+ }
}
-TEST(UvAsyncFunction, Movable) {
+TEST(UvAsyncFunctionTest, Movable) {
int prepare_cb_called = 0;
std::thread theThread;
@@ -103,7 +107,9 @@
auto prepare = Prepare::Create(loop);
prepare->prepare.connect([&] {
- if (prepare_cb_called++) return;
+ if (prepare_cb_called++) {
+ return;
+ }
theThread = std::thread([&] {
auto val = std::make_unique<int>(1);
auto val2 = async->Call(std::move(val)).get();
@@ -122,10 +128,12 @@
loop->Run();
- if (theThread.joinable()) theThread.join();
+ if (theThread.joinable()) {
+ theThread.join();
+ }
}
-TEST(UvAsyncFunction, CallIgnoreResult) {
+TEST(UvAsyncFunctionTest, CallIgnoreResult) {
int prepare_cb_called = 0;
std::thread theThread;
@@ -136,7 +144,9 @@
auto prepare = Prepare::Create(loop);
prepare->prepare.connect([&] {
- if (prepare_cb_called++) return;
+ if (prepare_cb_called++) {
+ return;
+ }
theThread = std::thread([&] { async->Call(std::make_unique<int>(1)); });
});
prepare->Start();
@@ -150,10 +160,12 @@
loop->Run();
- if (theThread.joinable()) theThread.join();
+ if (theThread.joinable()) {
+ theThread.join();
+ }
}
-TEST(UvAsyncFunction, VoidCall) {
+TEST(UvAsyncFunctionTest, VoidCall) {
int prepare_cb_called = 0;
std::thread theThread;
@@ -163,7 +175,9 @@
auto prepare = Prepare::Create(loop);
prepare->prepare.connect([&] {
- if (prepare_cb_called++) return;
+ if (prepare_cb_called++) {
+ return;
+ }
theThread = std::thread([&] { async->Call(); });
});
prepare->Start();
@@ -176,10 +190,12 @@
loop->Run();
- if (theThread.joinable()) theThread.join();
+ if (theThread.joinable()) {
+ theThread.join();
+ }
}
-TEST(UvAsyncFunction, WaitFor) {
+TEST(UvAsyncFunctionTest, WaitFor) {
int prepare_cb_called = 0;
std::thread theThread;
@@ -189,7 +205,9 @@
auto prepare = Prepare::Create(loop);
prepare->prepare.connect([&] {
- if (prepare_cb_called++) return;
+ if (prepare_cb_called++) {
+ return;
+ }
theThread = std::thread([&] {
ASSERT_FALSE(async->Call().wait_for(std::chrono::milliseconds(10)));
});
@@ -205,10 +223,12 @@
loop->Run();
- if (theThread.joinable()) theThread.join();
+ if (theThread.joinable()) {
+ theThread.join();
+ }
}
-TEST(UvAsyncFunction, VoidWaitFor) {
+TEST(UvAsyncFunctionTest, VoidWaitFor) {
int prepare_cb_called = 0;
std::thread theThread;
@@ -218,7 +238,9 @@
auto prepare = Prepare::Create(loop);
prepare->prepare.connect([&] {
- if (prepare_cb_called++) return;
+ if (prepare_cb_called++) {
+ return;
+ }
theThread = std::thread([&] {
ASSERT_FALSE(async->Call().wait_for(std::chrono::milliseconds(10)));
});
@@ -234,8 +256,9 @@
loop->Run();
- if (theThread.joinable()) theThread.join();
+ if (theThread.joinable()) {
+ theThread.join();
+ }
}
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv
diff --git a/wpiutil/src/test/native/cpp/uv/UvAsyncTest.cpp b/wpiutil/src/test/native/cpp/uv/UvAsyncTest.cpp
index e18f972..5561f1e 100644
--- a/wpiutil/src/test/native/cpp/uv/UvAsyncTest.cpp
+++ b/wpiutil/src/test/native/cpp/uv/UvAsyncTest.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
*
@@ -37,10 +34,9 @@
#include "wpi/uv/Loop.h"
#include "wpi/uv/Prepare.h"
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
-TEST(UvAsync, Test) {
+TEST(UvAsyncTest, CallbackOnly) {
std::atomic_int async_cb_called{0};
int prepare_cb_called = 0;
int close_cb_called = 0;
@@ -59,7 +55,9 @@
prepare->error.connect([](Error) { FAIL(); });
prepare->closed.connect([&] { close_cb_called++; });
prepare->prepare.connect([&] {
- if (prepare_cb_called++) return;
+ if (prepare_cb_called++) {
+ return;
+ }
theThread = std::thread([&] {
for (;;) {
mutex.lock();
@@ -98,10 +96,12 @@
ASSERT_EQ(async_cb_called, 3);
ASSERT_EQ(close_cb_called, 2);
- if (theThread.joinable()) theThread.join();
+ if (theThread.joinable()) {
+ theThread.join();
+ }
}
-TEST(UvAsync, Data) {
+TEST(UvAsyncTest, Data) {
int prepare_cb_called = 0;
int async_cb_called[2] = {0, 0};
int close_cb_called = 0;
@@ -116,7 +116,9 @@
prepare->error.connect([](Error) { FAIL(); });
prepare->prepare.connect([&] {
- if (prepare_cb_called++) return;
+ if (prepare_cb_called++) {
+ return;
+ }
theThread = std::thread([&] {
async->Send(0, [&](int v) {
ASSERT_EQ(v, 0);
@@ -142,10 +144,12 @@
ASSERT_EQ(async_cb_called[1], 1);
ASSERT_EQ(close_cb_called, 1);
- if (theThread.joinable()) theThread.join();
+ if (theThread.joinable()) {
+ theThread.join();
+ }
}
-TEST(UvAsync, DataRef) {
+TEST(UvAsyncTest, DataRef) {
int prepare_cb_called = 0;
int val = 0;
@@ -156,7 +160,9 @@
auto prepare = Prepare::Create(loop);
prepare->prepare.connect([&] {
- if (prepare_cb_called++) return;
+ if (prepare_cb_called++) {
+ return;
+ }
theThread = std::thread([&] { async->Send(1, val); });
});
prepare->Start();
@@ -171,8 +177,9 @@
ASSERT_EQ(val, 1);
- if (theThread.joinable()) theThread.join();
+ if (theThread.joinable()) {
+ theThread.join();
+ }
}
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv
diff --git a/wpiutil/src/test/native/cpp/uv/UvBufferTest.cpp b/wpiutil/src/test/native/cpp/uv/UvBufferTest.cpp
index e837ca9..e262181 100644
--- a/wpiutil/src/test/native/cpp/uv/UvBufferTest.cpp
+++ b/wpiutil/src/test/native/cpp/uv/UvBufferTest.cpp
@@ -1,50 +1,48 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/uv/Buffer.h" // NOLINT(build/include_order)
#include "gtest/gtest.h" // NOLINT(build/include_order)
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
-TEST(UvSimpleBufferPool, ConstructDefault) {
+TEST(UvSimpleBufferPoolTest, ConstructDefault) {
SimpleBufferPool<> pool;
auto buf1 = pool.Allocate();
- ASSERT_EQ(buf1.len, 4096u);
+ ASSERT_EQ(buf1.len, 4096u); // NOLINT
+ pool.Release({&buf1, 1});
}
-TEST(UvSimpleBufferPool, ConstructSize) {
+TEST(UvSimpleBufferPoolTest, ConstructSize) {
SimpleBufferPool<4> pool{8192};
auto buf1 = pool.Allocate();
- ASSERT_EQ(buf1.len, 8192u);
+ ASSERT_EQ(buf1.len, 8192u); // NOLINT
+ pool.Release({&buf1, 1});
}
-TEST(UvSimpleBufferPool, ReleaseReuse) {
+TEST(UvSimpleBufferPoolTest, ReleaseReuse) {
SimpleBufferPool<4> pool;
auto buf1 = pool.Allocate();
auto buf1copy = buf1;
auto origSize = buf1.len;
buf1.len = 8;
- pool.Release(buf1);
+ pool.Release({&buf1, 1});
ASSERT_EQ(buf1.base, nullptr);
auto buf2 = pool.Allocate();
ASSERT_EQ(buf1copy.base, buf2.base);
ASSERT_EQ(buf2.len, origSize);
+ pool.Release({&buf2, 1});
}
-TEST(UvSimpleBufferPool, ClearRemaining) {
+TEST(UvSimpleBufferPoolTest, ClearRemaining) {
SimpleBufferPool<4> pool;
auto buf1 = pool.Allocate();
- pool.Release(buf1);
+ pool.Release({&buf1, 1});
ASSERT_EQ(pool.Remaining(), 1u);
pool.Clear();
ASSERT_EQ(pool.Remaining(), 0u);
}
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv
diff --git a/wpiutil/src/test/native/cpp/uv/UvGetAddrInfoTest.cpp b/wpiutil/src/test/native/cpp/uv/UvGetAddrInfoTest.cpp
index 20d27f1..b397e24 100644
--- a/wpiutil/src/test/native/cpp/uv/UvGetAddrInfoTest.cpp
+++ b/wpiutil/src/test/native/cpp/uv/UvGetAddrInfoTest.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2020 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. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
*
@@ -34,10 +31,9 @@
#define CONCURRENT_COUNT 10
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
-TEST(UvGetAddrInfo, BothNull) {
+TEST(UvGetAddrInfoTest, BothNull) {
int fail_cb_called = 0;
auto loop = Loop::Create();
@@ -47,12 +43,12 @@
});
GetAddrInfo(
- loop, [](const addrinfo&) { FAIL(); }, Twine::createNull());
+ loop, [](const addrinfo&) { FAIL(); }, "");
loop->Run();
ASSERT_EQ(fail_cb_called, 1);
}
-TEST(UvGetAddrInfo, FailedLookup) {
+TEST(UvGetAddrInfoTest, FailedLookup) {
int fail_cb_called = 0;
auto loop = Loop::Create();
@@ -69,7 +65,7 @@
ASSERT_EQ(fail_cb_called, 1);
}
-TEST(UvGetAddrInfo, Basic) {
+TEST(UvGetAddrInfoTest, Basic) {
int getaddrinfo_cbs = 0;
auto loop = Loop::Create();
@@ -84,7 +80,7 @@
}
#ifndef _WIN32
-TEST(UvGetAddrInfo, Concurrent) {
+TEST(UvGetAddrInfoTest, Concurrent) {
int getaddrinfo_cbs = 0;
int callback_counts[CONCURRENT_COUNT];
@@ -110,5 +106,4 @@
}
#endif
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv
diff --git a/wpiutil/src/test/native/cpp/uv/UvGetNameInfoTest.cpp b/wpiutil/src/test/native/cpp/uv/UvGetNameInfoTest.cpp
index 76d22d9..db1eefe 100644
--- a/wpiutil/src/test/native/cpp/uv/UvGetNameInfoTest.cpp
+++ b/wpiutil/src/test/native/cpp/uv/UvGetNameInfoTest.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2020 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. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
*
@@ -32,10 +29,9 @@
#include "wpi/uv/Loop.h"
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
-TEST(UvGetNameInfo, BasicIp4) {
+TEST(UvGetNameInfoTest, BasicIp4) {
int getnameinfo_cbs = 0;
auto loop = Loop::Create();
@@ -55,7 +51,7 @@
ASSERT_EQ(getnameinfo_cbs, 1);
}
-TEST(UvGetNameInfo, BasicIp6) {
+TEST(UvGetNameInfoTest, BasicIp6) {
int getnameinfo_cbs = 0;
auto loop = Loop::Create();
@@ -75,5 +71,4 @@
ASSERT_EQ(getnameinfo_cbs, 1);
}
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv
diff --git a/wpiutil/src/test/native/cpp/uv/UvLoopWalkTest.cpp b/wpiutil/src/test/native/cpp/uv/UvLoopWalkTest.cpp
index 5d2e120..38a3342 100644
--- a/wpiutil/src/test/native/cpp/uv/UvLoopWalkTest.cpp
+++ b/wpiutil/src/test/native/cpp/uv/UvLoopWalkTest.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
*
@@ -32,10 +29,9 @@
#include "wpi/uv/Timer.h"
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
-TEST(UvLoop, Walk) {
+TEST(UvLoopTest, Walk) {
int seen_timer_handle = 0;
auto loop = Loop::Create();
@@ -47,7 +43,9 @@
timer->timeout.connect([&, theTimer = timer.get()] {
theTimer->GetLoopRef().Walk([&](Handle& it) {
- if (&it == timer.get()) seen_timer_handle++;
+ if (&it == timer.get()) {
+ seen_timer_handle++;
+ }
});
theTimer->Close();
});
@@ -61,10 +59,11 @@
// Loop is finished, should not see our timer handle
seen_timer_handle = 0;
loop->Walk([&](Handle& it) {
- if (&it == timer.get()) seen_timer_handle++;
+ if (&it == timer.get()) {
+ seen_timer_handle++;
+ }
});
ASSERT_EQ(seen_timer_handle, 0);
}
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv
diff --git a/wpiutil/src/test/native/cpp/uv/UvTimerTest.cpp b/wpiutil/src/test/native/cpp/uv/UvTimerTest.cpp
index 6e1bd5c..1a6258f 100644
--- a/wpiutil/src/test/native/cpp/uv/UvTimerTest.cpp
+++ b/wpiutil/src/test/native/cpp/uv/UvTimerTest.cpp
@@ -1,18 +1,14 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source 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/uv/Timer.h" // NOLINT(build/include_order)
#include "gtest/gtest.h"
-namespace wpi {
-namespace uv {
+namespace wpi::uv {
-TEST(UvTimer, StartAndStop) {
+TEST(UvTimerTest, StartAndStop) {
auto loop = Loop::Create();
auto handleNoRepeat = Timer::Create(loop);
auto handleRepeat = Timer::Create(loop);
@@ -59,7 +55,7 @@
ASSERT_TRUE(checkTimerRepeatEvent);
}
-TEST(UvTimer, Repeat) {
+TEST(UvTimerTest, Repeat) {
auto loop = Loop::Create();
auto handle = Timer::Create(loop);
@@ -70,5 +66,4 @@
loop->Run(); // forces close callback to run
}
-} // namespace uv
-} // namespace wpi
+} // namespace wpi::uv