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/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/json_serializer.h b/wpiutil/src/main/native/cpp/json_serializer.h
deleted file mode 100644
index 6c5ffbe..0000000
--- a/wpiutil/src/main/native/cpp/json_serializer.h
+++ /dev/null
@@ -1,210 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Modifications Copyright (c) 2017-2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
-/*
- __ _____ _____ _____
- __| | __| | | | JSON for Modern C++
-| | |__ | | | | | | version 3.1.2
-|_____|_____|_____|_|___| https://github.com/nlohmann/json
-
-Licensed under the MIT License <http://opensource.org/licenses/MIT>.
-Copyright (c) 2013-2018 Niels Lohmann <http://nlohmann.me>.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
-#include "wpi/json.h"
-
-#include <clocale> // lconv, localeconv
-#include <cmath> // labs, isfinite, isnan, signbit, ldexp
-#include <locale> // locale
-#include <type_traits>
-
-#include "wpi/raw_ostream.h"
-
-namespace wpi {
-
-class json::serializer
-{
- static constexpr uint8_t UTF8_ACCEPT = 0;
- static constexpr uint8_t UTF8_REJECT = 1;
-
- public:
- /*!
- @param[in] s output stream to serialize to
- @param[in] ichar indentation character to use
- */
- serializer(raw_ostream& s, const char ichar)
- : o(s), loc(std::localeconv()), indent_char(ichar),
- indent_string(512, indent_char)
- {}
-
- // delete because of pointer members
- serializer(const serializer&) = delete;
- serializer& operator=(const serializer&) = delete;
-
- /*!
- @brief internal implementation of the serialization function
-
- This function is called by the public member function dump and organizes
- the serialization internally. The indentation level is propagated as
- additional parameter. In case of arrays and objects, the function is
- called recursively.
-
- - strings and object keys are escaped using `escape_string()`
- - integer numbers are converted implicitly via `operator<<`
- - floating-point numbers are converted to a string using `"%g"` format
-
- @param[in] val value to serialize
- @param[in] pretty_print whether the output shall be pretty-printed
- @param[in] indent_step the indent level
- @param[in] current_indent the current indent level (only used internally)
- */
- void dump(const json& val, const bool pretty_print,
- const bool ensure_ascii,
- const unsigned int indent_step,
- const unsigned int current_indent = 0);
-
- private:
- /*!
- @brief dump escaped string
-
- Escape a string by replacing certain special characters by a sequence of an
- escape character (backslash) and another character and other control
- characters by a sequence of "\u" followed by a four-digit hex
- representation. The escaped string is written to output stream @a o.
-
- @param[in] s the string to escape
- @param[in] ensure_ascii whether to escape non-ASCII characters with
- \uXXXX sequences
-
- @complexity Linear in the length of string @a s.
- */
- void dump_escaped(StringRef s, const bool ensure_ascii);
-
- template <typename NumberType,
- detail::enable_if_t<std::is_same_v<NumberType, uint64_t>, int> = 0>
- bool is_negative_integer(NumberType x) {
- return false;
- }
-
- template <typename NumberType,
- detail::enable_if_t<std::is_same_v<NumberType, int64_t>, int> = 0>
- bool is_negative_integer(NumberType x) {
- return x < 0;
- }
-
- /*!
- @brief dump an integer
-
- Dump a given integer to output stream @a o. Works internally with
- @a number_buffer.
-
- @param[in] x integer number (signed or unsigned) to dump
- @tparam NumberType either @a int64_t or @a uint64_t
- */
- template<typename NumberType, detail::enable_if_t<
- std::is_same<NumberType, uint64_t>::value or
- std::is_same<NumberType, int64_t>::value,
- int> = 0>
- void dump_integer(NumberType x)
- {
- // special case for "0"
- if (x == 0)
- {
- o << '0';
- return;
- }
-
- const bool is_negative = is_negative_integer(x); // see issue #755
- std::size_t i = 0;
-
- while (x != 0)
- {
- // spare 1 byte for '\0'
- assert(i < number_buffer.size() - 1);
-
- const auto digit = std::labs(static_cast<long>(x % 10));
- number_buffer[i++] = static_cast<char>('0' + digit);
- x /= 10;
- }
-
- if (is_negative)
- {
- // make sure there is capacity for the '-'
- assert(i < number_buffer.size() - 2);
- number_buffer[i++] = '-';
- }
-
- std::reverse(number_buffer.begin(), number_buffer.begin() + i);
- o.write(number_buffer.data(), i);
- }
-
- /*!
- @brief dump a floating-point number
-
- Dump a given floating-point number to output stream @a o. Works internally
- with @a number_buffer.
-
- @param[in] x floating-point number to dump
- */
- void dump_float(double x);
-
- /*!
- @brief check whether a string is UTF-8 encoded
-
- The function checks each byte of a string whether it is UTF-8 encoded. The
- result of the check is stored in the @a state parameter. The function must
- be called initially with state 0 (accept). State 1 means the string must
- be rejected, because the current byte is not allowed. If the string is
- completely processed, but the state is non-zero, the string ended
- prematurely; that is, the last byte indicated more bytes should have
- followed.
-
- @param[in,out] state the state of the decoding
- @param[in,out] codep codepoint (valid only if resulting state is UTF8_ACCEPT)
- @param[in] byte next byte to decode
- @return new state
-
- @note The function has been edited: a std::array is used.
-
- @copyright Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
- @sa http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
- */
- static uint8_t decode(uint8_t& state, uint32_t& codep, const uint8_t byte) noexcept;
-
- private:
- /// the output of the serializer
- raw_ostream& o;
-
- /// a (hopefully) large enough character buffer
- std::array<char, 64> number_buffer{{}};
-
- /// the locale
- const std::lconv* loc = nullptr;
-
- /// the indentation character
- const char indent_char;
- /// the indentation string
- std::string indent_string;
-};
-
-} // namespace wpi
diff --git a/wpiutil/src/main/native/cpp/leb128.cpp b/wpiutil/src/main/native/cpp/leb128.cpp
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