Squashed 'third_party/allwpilib/' changes from e4b91005cf..83f1860047
83f1860047 [wpilib] Add/update documentation to PneumaticBase and subclasses (NFC) (#4881)
9872e676d8 [commands] Make Subsystem destructor virtual (#4892)
25db20e49d [hal] Fix segfault in various HAL functions (#4891)
b0c6724eed [glass] Add hamburger menu icon to titlebars (#4874)
f0fa8205ac Add missing compiler flags and fix warnings (#4889)
42fc4cb6bc [wpiutil] SafeThread: Provide start/stop hooks (#4880)
cc166c98d2 [templates] Add Command-based skeleton template (#4861)
3f51f10ad3 [build] Update to 2023v3 image (#4886)
1562eae74a [ntcore] Refactor meta-topic decoding from glass (#4809)
b632b288a3 Fix usages of std::min and std::max to be windows safe (#4887)
c11bd2720f [wpilibc] Add internal function to reset Shuffleboard instance (#4884)
f1151d375f [ntcore] Add method to get server time offset (#4847)
fe1b62647f [hal,wpilib] Update documentation for getComments (NFC) (#4879)
c49a45abbd [build] Fix examples linking in incorrect jni library (#4873)
bc3d01a721 [build] Add platform check to doxygen plugin (#4862)
bc473240ae Add Jetbrains Fleet folder to .gitignore (#4872)
2121bd5fb8 [wpimath] Remove RKF45 (#4870)
835f8470d6 [build] Fix roborio cross-compiler on arm hosts (#4864)
6cfe5de00d [ntcore] Don't deadlock server on early destroy (#4863)
2ac41f3edc [hal, wpilib] Add RobotController.getComments() (#4463)
26bdbf3d41 Java optimization and formatting fixes (#4857)
92149efa11 Spelling and grammar cleanups (#4849)
176fddeb4c [commands] Add functions to HID classes to allow use of axes as BooleanEvents/Triggers (#4762)
87a34af367 [templates] Add bindings to command-based template (#4838)
4534e75787 [examples] Remove redundant MotorControl example (#4837)
1cbebaa2f7 [commands] Remove final semicolon from test macro definition (#4859)
6efb9ee405 [commands] Add constructor for SwerveControllerCommand that takes a HolonomicDriveController (#4785)
1e7fcd5637 [cscore] Change run loop functions to not be mac specific (#4854)
1f940e2b60 [apriltag] Add C++ wrappers, rewrite Java/JNI to match (#4842)
a6d127aedf [build] Add missing task dependency in wpilibjExamples (#4852)
b893b3d6d3 [cscore] Add support for USB cameras on macOS (#4846)
1696a490fa [glass] Add support for alternate NT ports (#4848)
40a22d69bc [glass] Add support for alternate NT ports (#4848)
e84dbfede0 [wpilib] GenericHID: Add rumble both option (#4843)
8aa9dbfa90 [examples] Link apriltag package in examples build.gradle (#4845)
eda2fa8a17 [build] Update Spotless (#4840)
d20594db0d Fix typos (#4839)
dd8ecfdd54 [commands] Fix typo in waitUntil docs (NFC) (#4841)
17ceebfff4 [apriltag] Clean up apriltag JNI (#4823)
8b74ab389d [examples] RapidReactCommandBot: Fix array indices (#4833)
1aad3489c2 [sim] Implement PD total current and power (#4830)
2744991771 [wpimath] Fix docs in SwerveModulePosition (#4825)
ffbf6a1fa2 [commands] Disable regularly failing unit test (#4824)
fbabd0ef15 [commands] Enhance Command Sendable implementations (#4822)
7713f68772 [hal] Use atomic rather then mutex for DS Data updates (#4787)
701995d6cc [examples] Update Command-based starter project (#4778)
bf7068ac27 [wpilibc] Add missing PPS implementation for C++ (#4821)
aae0f52ca6 [ntcore] NetworkTable: fix visibility of get/set value (#4820)
ee02fb7ba7 [hal] Add support for Pulse-Per-Second signal (#4819)
518916ba02 [wpilib] Fix DS mode thread event being manual reset accidentally (#4818)
3997c6635b [hal] Update to new image, use new TCP notify callback and new duty cycle API (#4774)
cc8675a4e5 [examples] Add comment on how to view elevator sim (NFC) (#4482)
fb2c170b6e [ntcore] Simplify local startup (#4803)
7ba8a9ee1f [wpimath] ProfiledPIDController: Add to SendableRegistry (#4656)
c569d8e523 [wpilib] Joystick.getMagnitude(): use hypot() function (#4816)
2a5e89fa97 [apriltag] Improve description of pose coordinates (NFC) (#4810)
cc003c6c38 [apriltag] Fix AprilTagFieldLayout JSON name (#4814)
5522916123 [commands] CommandXBoxController bumper documentation fix (NFC) (#4815)
967b30de3a [glass] Fix NT view UpdateClients() bug (#4808)
3270d4fc86 [wpimath] Rewrite pose estimator docs (#4807)
be39678447 [apriltag] Add test to ensure apriltagjni loads (#4805)
61c75deb2a [commands] Test no-op behavior of scheduling a scheduled command (#4806)
a865f48e96 [ntcore] Pass pub/sub options as a unified PubSubOptions struct (#4794)
f66a667321 [commands] Fix incorrect Trigger docs (NFC) (#4792)
f8d4e9866e [ntcore] Clean up ntcore_test.h (#4804)
7e84ea891f [wpimath] Fix ComputerVisionUtil transform example in parameter docs (NFC) (#4800)
da3ec1be10 [wpimath] Change terminology for ArmFeedforward gravity gain (NFC) (#4791)
944dd7265d [wpilibc] Add C++ Notifier error handling, update java notifier error message (#4795)
6948cea67a [wpiutil] Fix MemoryBuffer initialization (#4797)
a31459bce6 [wpiutil] Fix UnescapeCString overflow when inputSize < 2 (#4796)
4a0ad6b48c [wpimath] Rotation2d: Add reference to angleModulus in docs (NFC) (#4786)
e6552d272e [ntcore] Remove table multi-subscriber (#4789)
bde383f763 [hal] Replace const char* with std::string_view in Driver Station sim functions (#4532)
5a52b51443 [hal] Add RobotController.getSerialNumber() (#4783)
69a66ec5ec [wpilib] Fix multiple motor safety issues (#4784)
989c9fb29a [wpimath] Revert Rotation2D change that limits angles (#4781)
0f5b08ec69 [wpigui] Update imgui to 1.89.1+ (#4780)
fba191099c [examples] AddressableLED: Add unit test (#4779)
b390cad095 [wpilibj] Consistently use ErrorMessages.requireNonNullParam (#4776)
b9772214d9 [wpilib] Sendable: Don't call setter for getter changes
342c375a71 [ntcore] Add subscriber option to exclude single publisher
b0e4053087 [ntcore] Use int for options instead of double
f3e666b7bb [cscore] Convert YUYV and UYVY directly to grayscale (#4777)
b300518bd1 [hal] Add CAN Stream API to Java through JNI bindings (#4193)
be27171236 [wpilibj] Shuffleboard: Check for null sendable (#4772)
4bbdbdfb48 [commands] Move GroupedCommands to CommandScheduler (#4728)
f18fd41ac3 [wpimath] Remove broken and obsoleted ComputerVisionUtil functions (#4775)
2d0faecf4f [glass] DataSource: Add spinlock to protect value (#4771)
348bd107fc [hal] Add CANManufacturer for The Thrifty Bot (#4773)
3149dc64b8 [examples] HatchbotInlined: Use Subsystem factories (#4765)
8618dd4160 [glass, wpilib] Replace remaining references to Speed Controller with Motor Controller (#4769)
72e21a1ed1 [apriltag] Use wpilibsuite fork of apriltag (#4764)
eab0d929e6 [commands] CommandGenericHID POV methods: Fix docs (NFC) (#4760)
6789869663 [wpilib] Call set(0) rather than disable for stopMotor (#4763)
c9dea2968d [cscore] Emit warning that USB Camera isn't supported on OSX (#4766)
8f402645f5 [commands] Fix PIDSubsystem setSetpoint behavior (#4759)
f24ad1d715 [build] Upgrade to googletest 1.12.1 (#4752)
ff88756864 [wpimath] Add new DCMotor functions for alternative calculations and reduction calculation (#4749)
f58873db8e [wpimath] Remove extra terms in matrix for pose estimator docs (#4756)
37e969b41a [wpimath] Add constructors to pose estimators with default standard deviations (#4754)
13cdc29382 [ci] Rename comment command from "/wpiformat" to "/format" (#4755)
6e23985ae6 [examples] Add main include directory to test builds (#4751)
66bb0ffb2c [examples] Add unit testing infrastructure (#4646)
74cc86c4c5 [wpimath] Make transform tests use pose/transform equality operators (#4675)
e22d8cc343 [wpimath] Use Odometry for internal state in Pose Estimation (#4668)
68dba92630 [ci] Update mac and windows builds to Java 17 (#4750)
23bfc2d9ab [sim] Remove unmaintained Gazebo support (#4736)
1f1461e254 [wpilib] Add method to enable/disable LiveWindow in test mode (#4678)
eae68fc165 [wpimath] Add tolerance for Rotation3d rotation matrix special orthogonality (#4744)
4c4545fb4b [apriltag] Suppress warning (#4743)
16ffaa754d [docs] Generate docs for apriltag subproject (#4745)
5e74ff26d8 [apriltag, build] Update native utils, add apriltag impl and JNI (#4733)
53875419a1 [hal] Allow overriding stderr printing by HAL_SendError (#4742)
aa6499e920 [ntcore] Fix special topic multi-subscriber handling (#4740)
df70351107 [build] Fix cmake install of thirdparty includes (#4741)
e9bd50ff9b [glass] NT view: clear meta-topic info on disconnect (#4732)
9b319fd56b [ntcore] Add sub option for local vs remote changes (#4731)
18d28ec5e3 [ntcore] Remove duplicate value checking from ClientImpl
bdfb625211 [ntcore] Send duplicate values to network if necessary
21003e34eb [commands] Update Subsystem factories and example to return CommandBase (#4729)
70080457d5 [commands] Refactor ProxyScheduleCommand, SelectCommand into ProxyCommand (#4534)
e82cd5147b [wpilib] Tweak Color HSV formula and use in AddressableLED (#4724)
ec124bb662 [commands] Allow unsetting a subsystem's default command (#4621)
2b2aa8eef7 [examples] Update all examples to use NWU coordinate conventions (#4725)
cb38bacfe8 [commands] Revert to original Trigger implementation (#4673)
15561338d5 [commands] Remove one more default command isFinished check (#4727)
ca35a2e097 Add simgui files to .gitignore (#4726)
20dbae0cee [examples] Renovate command-based examples (#4409)
1a59737f40 [commands] Add convenience factories (#4460)
42b6d4e3f7 Use defaulted comparison operators in C++ (#4723)
135c13958f [wpigui] Add FontAwesome (#4713)
ffbfc61532 [ntcore] Add NetworkTable table-specific listeners (#4640)
8958b2a4da [commands] Add property tests for command compositions (#4715)
e4ac09077c [wpilib] Add link to MotorSafety article (#4720)
f40de0c120 [commands] Add C++ factory templates (#4686)
51fa3e851f [build] cmake: Use FetchContent instead of ExternalProject (#4714)
1da84b2255 [wpigui] Reload fonts to scale rather than preloading (#4712)
e43e2fbc84 [wpiutil] StringExtras: Add UnescapeCString (#4707)
5804d8fa84 [ntcore] Server: Properly handle multiple subscribers (#4717)
169ef5fabf [glass] Update NT view for topicsOnly and sendAll changes (#4718)
148759ef54 [examples] CANPDP: Expand properties shown (#4687)
58ed112b51 [commands] RepeatCommand: restart on following iteration (#4706)
dd1da77d20 [readme] Fix broken CI badge (#4710)
7cda85df20 [build] Check Gradle plugin repo last to fix CI (#4711)
7ed9b13277 [build] Bump version plugin to fix null tag (#4705)
6b4f26225d [apriltag] Fix pluralization of apriltag artifacts (#4671)
b2d2924b72 [cscore] Add Y16 image support (#4702)
34ec89c041 [wpilibc] Shuffleboard SimpleWidget: Return pointer instead of reference (#4703)
e15200068d [ci] Disable HW testbench runs (#4704)
d5200db6cd [wpimath] Rename HolonomicDriveController.calculate params (#4683)
2ee3d86de4 [wpimath] Clarify Rotation3d roll-pitch-yaw direction (#4699)
9f0a8b930f [cscore] Use MFVideoFormat_L8 for Gray on Windows (#4701)
2bca43779e [cscore] Add UYVY image support (#4700)
4307d0ee8b [glass] Plot: allow for more than 11 plots (#4685)
3fe8d355a1 [examples] StateSpaceDifferentialDriveSimulation: Use encoder reversed constants (#4682)
b44034dadc [ntcore] Allow duplicate client IDs on server (#4676)
52d2c53888 [commands] Rename Java factory wait() to waitSeconds() (#4684)
76e918f71e [build] Fix JNI artifacts linking to incorrect libraries (#4680)
0bee875aff [commands] Change C++ CommandPtr to use CommandBase (#4677)
98e922313b [glass] Don't check IsConnected for NT widgets (#4674)
9a36373b8f [apriltag] Switch 2022 apriltag layout length and width values (#4670)
cf8faa9e67 [wpilib] Update values on controllable sendables (#4667)
5ec067c1f8 [ntcore] Implement keep duplicates pub/sub flag (#4666)
e962fd2916 [ntcore] Allow numeric-compatible value sets (#4620)
88bd67e7de [ci] Update clang repositories to jammy (#4665)
902e8686d3 [wpimath] Rework odometry APIs to improve feature parity (#4645)
e2d49181da Update to native utils 2023.8.0 (#4664)
149bac55b1 [cscore] Add Arducam OV9281 exposure quirk (#4663)
88f7a3ccb9 [wpimath] Fix Pose relativeTo documentation (#4661)
8acce443f0 [examples] Fix swerve examples to use getDistance for turning encoder (#4652)
295a1f8f3b [ntcore] Fix WaitForListenerQueue (#4662)
388e7a4265 [ntcore] Provide mechanism to reset internals of NT instance (#4653)
13aceea8dc [apriltag] Fix FieldDimensions argument order (#4659)
c203f3f0a9 [apriltag] Fix documentation for AprilTagFieldLayout (#4657)
f54d495c90 Fix non initialized hal functionality during motor safety init (#4658)
e6392a1570 [cmd] Change factories return type to CommandBase (#4655)
53904e7cf4 [apriltag] Split AprilTag functionality to a separate library (#4578)
2e88a496c2 [wpimath] Add support for swerve joystick normalization (#4516)
ce4c45df13 [wpimath] Rework function signatures for Pose Estimation / Odometry (#4642)
0401597d3b [readme] Add wpinet to MavenArtifacts.md (#4651)
2e5f9e45bb [wpimath] Remove encoder reset comments on Swerve, Mecanum Odometry and Pose Estimation (#4643)
e4b5795fc7 [docs] Disable Doxygen for memory to fix search (#4636)
03d0ea188c [build] cmake: Add missing wpinet to installed config file (#4637)
3082bd236b [build] Move version file to its own source set (#4638)
b7ca860417 [build] Use build cache for sign step (#4635)
64838e6367 [commands] Remove unsafe default command isFinished check (#4411)
1269d2b901 [myRobot] Disable spotbugs (#4565)
14d8506b72 [wpimath] Fix units docs for LinearSystemId::IdentifyDrivetrainSystem() (#4600)
d1d458db2b [wpimath] Constrain Rotation2d range to -pi to pi (#4611)
f656e99245 [readme] Add links to development build documentation (#4481)
6dd937cef7 [commands] Fix Trigger API docs (NFC) (#4599)
49047c85b9 [commands] Report error on C++ CommandPtr use-after-move (#4575)
d07267fed1 [ci] Upgrade containers to Ubuntu 22.04 and remove libclang installation (#4633)
b53ce1d3f0 [build, wpiutil] Switch macos to universal binaries (#4628)
5a320c326b [upstream_util, wpiutil] Refactor python scripts (#4614)
c4e526d315 [glass] Fix NT Mechanism2D (#4626)
d122e4254f [ci] Run spotlessApply after wpiformat in comment command (#4623)
5a1e7ea036 [wpilibj] FieldObject2d: Add null check to close() (#4619)
179f569113 [ntcore] Notify locally on SetDefault (#4617)
b0f6dc199d [wpilibc] ShuffleboardComponent.WithProperties: Update type (#4615)
7836f661cd [wpimath] Add missing open curly brace to units/base.h (#4613)
dbcc1de37f [wpimath] Add DifferentialDriveFeedforward classes which wrap LinearPlantInversionFeedforward (#4598)
93890c528b [wpimath] Add additional angular acceleration units (#4610)
3d8d5936f9 [wpimath] Add macro for disabling units fmt support (#4609)
2b04159dec [wpimath] Update units/base.h license header (#4608)
2764004fad [wpinet] Fix incorrect jni definitions (#4605)
85f1bb8f2b [wpiutil] Reenable jni check task (#4606)
231ae2c353 [glass] Plot: Fix Y-axis not being saved (#4594)
e92b6dd5f9 [wpilib] Fix AprilTagFieldLayout JSON property name typos (#4597)
2a8e0e1cc8 Update all dependencies that use grgit (#4596)
7d06e517e9 [commands] Move SelectCommand factory impl to header (#4581)
323524fed6 [wpimath] Remove deprecated units/units.h header (#4572)
d426873ed1 [commands] Add missing PS4 triangle methods (#4576)
5be5869b2f [apriltags] Use map as internal data model (#4577)
b1b4c1e9e7 [wpimath] Fix Pose3d transformBy rotation type (#4545)
a4054d702f [commands] Allow composing two triggers directly (#4580)
0190301e09 [wpilibc] Explicitly mark EventLoop as non-copyable/non-movable (#4579)
9d1ce6a6d9 [ntcore] Catch file open error when saving preferences (#4571)
5005e2ca04 [ntcore] Change Java event mask to EnumSet (#4564)
fa44a07938 [upstream-utils][mpack] Add upstream util for mpack (#4500)
4ba16db645 [ntcore] Various fixes and cleanups (#4544)
837415abfd [hal] Fix joysticks either crashing or returning 0 (#4570)
2c20fd0d09 [wpilib] SingleJointedArmSim: Check angle equals limit on wouldHit (#4567)
64a7136e08 [wpimath] SwerveDrivePoseEstimator: Restore comment about encoder reset (#4569)
b2b473b24a [wpilib] Add AprilTag and AprilTagFieldLayout (#4421)
7aab8fa93a [build] Update to Native Utils 2023.6.0 (#4563)
12c2851856 [commands] WrapperCommand: inherit from CommandBase (#4561)
0da169dd84 [wpimath] Remove template argument from ElevatorFeedforward (#4554)
2416827c25 [wpimath] Fix docs for pose estimator local measurement models (#4558)
1177a3522e [wpilib] Fix Xbox/PS4 POV sim for port number constructors (#4548)
102344e27a [commands] HID classes: Add missing methods, tweak return types (#4557)
1831ef3e19 [wpilib] Fix Shuffleboard SuppliedValueWidget (#4559)
a9606ce870 [wpilib] Fix Xbox/PS4 POV sim (#4546)
6c80d5eab3 [wpimath] Remove unused SymbolExports.h include from units/base.h (#4541)
b114006543 [ntcore] Unify listeners (#4536)
32fbfb7da6 [build] cmake: Install ntcore generated include files (#4540)
02465920fb [build] Update native utils to 2023.4.0 (#4539)
3a5a376465 [wpimath] Increase constexpr support in geometry data types (#4231)
1c3c86e9f1 [ntcore] Cache GetEntry(name) values (#4531)
dcda09f90a [command] Rename trigger methods (#4210)
66157397c1 [wpilib] Make drive classes follow NWU axes convention (#4079)
9e22ffbebf [ntcore] Fix null deref in NT3 client (#4530)
648ab6115c [wpigui,dlt,glass,ov] Support arm in GUI tools (#4527)
8bc3b04f5b [wpimath] Make ComputerVisionUtil use 3D geometry classes (#4528)
cfb84a6083 [wpilibc] Don't hang waiting for NT server to start (#4524)
02c47726e1 [wpimath] Remove unused odometry instance from DifferentialDrivePoseEstimator test (#4522)
b2a0093294 [ci] Revert upgrade of github-pages-deploy-action (#4521)
2a98d6b5d7 [wpimath] PIDController: Add getters for position & velocity tolerances (#4458)
9f36301dc8 [ci] Write wpiformat patch to job summary (#4519)
901fc555f4 [wpimath] Position Delta Odometry for Mecanum (#4514)
4170ec6107 [wpimath] Position Delta Odometry for Swerve (#4493)
fe400f68c5 [docs] Add wpinet to docs build (#4517)
794669b346 [ntcore] Revamp listeners (#4511)
dcfa85a5d5 [ci] Build sanitizers with clang-14 (#4518)
15ad855f1d [ntcore] Add UnitTopic<T> (C++ only) (#4497)
11244a49d9 [wpilib] Add IsConnected function to all gyros (#4465)
1d2e8eb153 [build] Update myRobot deployment (#4515)
ad53fb19b4 [hal] Use new HMB api for addressable LED (#4479)
ba850bac3b [hal] Add more shutdown checks and motor safety shutdown (#4510)
023a5989f8 [ntcore] Fix typo in NetworkServer client connect message (#4512)
c970011ccc [docs] Add Doxygen aliases used by Foonathan memory (#4509)
07a43c3d9a [readme] Document clang-format version and /wpiformat (#4503)
a05b212b04 [ci] Revert changes to wpiformat task from #4501 (#4508)
09faf31b67 [commands] Replace Command HID inheritance with delegation (#4470)
9e1f9c1133 [commands] Add command factories (#4476)
f19d2b9b84 [ci] Add NUMBER environment variable to comment command commit script (#4507)
a28f93863c [ci] Push comment command commit directly to PR (#4506)
c9f61669b8 [ci] Fix comment command commit push (#4505)
dcce5ad3b3 [ci] Update github-script API usage (#4504)
6836e5923d [wpilibc] Restore get duty cycle scale factor (#4502)
335188c652 [dlt] Add deselect/select all buttons to download view (#4499)
60a29dcb99 [glass] Field2D: Add "hidden" option for objects (#4498)
b55d5b3034 [ci] Update deprecated github actions (#4501)
10ed4b3969 [ntcore] Various NT4 fixes (#4474)
4a401b89d7 [hal, wpilib] New DS thread model and implementation (#3787)
c195b4fc46 [wpimath] Clean up PoseEstimator nominal dt docs (#4496)
8f2e34c6a3 [build] Remove wpilib prefix from CMake flat install (#4492)
150d692df7 [wpimath] Remove unused private PoseEstimator function (#4495)
3e5bfff1b5 [wpimath] FromFieldRelativeSpeeds: Add ChassisSpeeds overload (#4494)
9c7e66a27d [commands] C++: Add CommandPtr overload for SetDefaultCommand (#4488)
0ca274866b [build] Fix CMake system library opt-ins (#4487)
dc037f8d41 [commands] Remove EndlessCommand (#4483)
16cdc741cf [wpimath] Add Pose3d(Pose2d) constructor (#4485)
9d5055176d [build] cmake: Allow disabling ntcore build (#4486)
d1e66e1296 [build] Compile all java code with inline string concatenation (#4490)
1fc098e696 Enable log macros to work with no args (#4475)
878cc8defb [wpilib] LiveWindow: Add enableAllTelemetry() (#4480)
8153911160 [build] Fix MSVC runtime archiver to grab default runtime (#4478)
fbdc810887 Upgrade to C++20 (#4239)
396143004c [ntcore] Add ntcoreffi binary (#4471)
1f45732700 [build] Update to 2023.2.4 native-utils and new dependencies (#4473)
574cb41c18 [ntcore] Various fixes (#4469)
d9d6c425e7 [build] Force Java 11 source compatibility (#4472)
58b6484dbe Switch away from NI interrupt manager to custom implementation (#3705)
ca43fe2798 [wpimath] Use Units conversions in ComputerVisionUtil docs (NFC) (#4464)
87a64ccedc [hal] Convert DutyCycle Raw output to be a high time measurement (#4466)
89a3d00297 [commands] Add FinallyDo and HandleInterrupt decorators (#4412)
1497665f96 [commands] Add C++ versions of Java-only decorators (#4457)
27b173374e [wpimath] Add minLinearAccel parameter to DifferentialDriveAccelerationLimiter (#4422)
2a13dba8ac [wpilib] TrajectoryUtil: Fix ambiguous documentation (NFC) (#4461)
77301b126c [ntcore] NetworkTables 4 (#3217)
90cfa00115 [build] cmake: Fix libssh include directory order (#4459)
5cf961edb9 [commands] Refactor lambda-based commands to inherit FunctionalCommand (#4451)
b2276e47de [wpimath] Enable continuous angle input for HolonomicDriveController (#4453)
893b46139a [fieldImages] Add utilities to simplify loading of fields (#4456)
60e29627c0 [commands] C++ unique_ptr migration (#4319)
3b81cf6c35 [wpilib] Improve Color.toString (#4450)
5c067d30a0 [wpinet] WebSocket: Add SendFrames() (#4445)
ceaf493811 [wpiutil] MakeJByteArray: Use span<uint8> instead of string_view (#4446)
10e04e2b13 [examples] FrisbeeBot: Fix reference capture (#4449)
726f67c64b [build] Add exeSplitSetup (#4444)
c7b7624c1c [wpiutil] Add MessagePack utility functions (#4448)
d600529ec0 [wpinet] uv::Async: Add UnsafeSend() (#4447)
b53b3526a2 [wpimath] Add CoordinateSystem conversion for Transform3d (#4443)
38bb23eb18 [wpimath] Add scalar multiply and divide operators to all geometry classes (#4438)
3937ff8221 [wpilib] Remove deprecated Controller class (#4440)
abbfe244b5 [wpilib] Improve Color FromHSV (#4439)
4ddb8aa0dd [sim] Provide function that resets all simulation data (#4016)
a791470de7 Clean up Java warning suppressions (#4433)
17f504f548 [hal,wpilib] Fix SPI Mode Setting (#4434)
773198537c [wpiutil] Add wpi::scope_exit (#4432)
5ac658c8f0 [wpiutil] Logger: Conditionalize around WPI_LOG (#4431)
8767e4a941 [wpiutil] DataLog: Fix SetMetadata output (#4430)
8c4af073f4 [wpiutil] Synchronization: shutdown race protection (#4429)
c79f38584a [build] Fix Java integration tests (#4428)
36c08dd97c [build] Fix cmake install of fmtlib (#4426)
69b7b3dd7d [ci] Remove the Windows cmake job (#4425)
738c75fed8 [readme] Fix formatting/linting link (#4423)
4eb1d03fb3 [wpimath] Document C++ LinearFilter exception (#4417)
ba4ec6c967 [build] Fix clang-tidy false positive on Linux (#4406)
97836f0e55 [commands] Fix ProfiledPIDSubsystem setGoal behavior (#4414)
fdfb85f695 [wpimath] Remove Java LQR constructor that takes a controller gain matrix (#4419)
ab1baf4832 [wpimath] Add rotation matrix constructor to Rotation3d (#4413)
9730032866 [wpimath] Document LQR and KalmanFilter exceptions (#4418)
5b656eecf6 [wpimath] Fix HTML5 entity (#4420)
9ae38eaa7c [commands] Add owning overload to ProxyScheduleCommand (#4405)
cb33bd71df [commands] deprecate withInterrupt decorator (#4407)
d9b4e7b8bf [commands] Revert "Change grouping decorator impl to flatten nested group structures (#3335)" (#4402)
0389bf5214 [hal] REVPH: Improve handling of disconnected CAN Bus (#4169)
4267fa08d1 [wpilibc] ADIS IMUs: Fix memory leak (#4170)
65c8fbd452 [wpilib] MotorControllerGroup: Override setVoltage (#4403)
f36162fddc [wpimath] Improve Discretization internal docs (#4400)
5149f7d894 [wpimath] Add two-vector Rotation3d constructor (#4398)
20b5bed1cb [wpimath] Clean up Java Quaternion class (#4399)
f18dd1905d [build] Include all thirdparty sources in distribution (#4397)
aa9d7f1cdc [wpiutil] Import foonathan memory (#4306)
2742662254 [ci] Remove a couple of obsolete clang-tidy checks (#4396)
a5df391166 [hal, wpilib] Fix up DIO pulse API (#4387)
59e6706b75 [glass] Turn on docking by default
8461bb1e03 [glass] Add support for saving docking info
b873e208b4 [wpigui] Add support for imgui config flags
873e72df8c [build] Update imgui to 1.88 docking branch
c8bd6fc5b4 [ci] Fix comment-command (take 2) (#4395)
fed68b83b4 [ci] Fix comment-command action not running runners (#4393)
0ef8a4e1df [wpimath] Support formatting more Eigen types (#4391)
c393b3b367 [build] Update to native utils 2023.1.0 and Gradle 7.5.1 (#4392)
b5a17f762c [wpimath] Add direction to slew rate limiter (#4377)
fafc81ed1a [wpiutil] Upgrade to fmt 9.1.0 (#4389)
cc56bdc787 [wpiutil] SafeThread: Add Synchronization object variant (#4382)
4254438d8d [commands] Mark command group lifecycle methods as final (#4385)
97c15af238 [wpimath] LinearSystemId: Fix docs, move C++ impls out of header (#4388)
d22ff8a158 [wpiutil] Add JNI access to C++ stderr (#4381)
fdb5a2791f [wpiutil] jni_util: Add Mac-friendly MakeJLongArray/JArrayRef (#4383)
c3a93fb995 [commands] Revamp Interruptible (#4192)
f2a8d38d2a [commands] Rename Command.repeat to repeatedly (#4379)
9e24c6eac0 [wpiutil] Logger: paren-protect instance usage in macro (#4384)
fe4d12ce22 [wpimath] Add LTV controller derivations and make enums private (#4380)
eb08486039 [build] Fix MacOS binary rpath generation (#4376)
ccf83c634a [build] Use native-utils platform names instead of raw strings (#4375)
3fd69749e7 [docs] Upgrade to doxygen 1.9.4 (#4370)
594df5fc08 [wpinet] uv/util.h: Pull in ws2_32.lib on Windows for ntohs (#4371)
539070820d [ci] Enable asan for wpinet and wpiutil (#4369)
564a56d99b [wpinet] Fix memory leak in WorkerThreadTest (#4368)
5adf50d93c [upstream_utils] Refactor upstream_utils scripts (#4367)
d80e8039d7 [wpiutil] Suppress fmtlib clang-tidy warning in C++20 consteval contexts (#4364)
0e6d67b23b [upstream_utils] Remove yapf format disable comment (#4366)
be5270697a [build] Suppress enum-enum deprecation warning in OpenCV (#4365)
8d28851263 Add Rosetta install command to build requirements (#4363)
3d2115c93e [wpinet] include-what-you-use in MulticastTest (#4360)
91002ae3cc [wpimath] Upgrade to Drake 1.6.0 (#4361)
148c18e658 [wpinet] Upgrade to libuv 1.44.2 (#4362)
a2a5c926b6 Fix clang-tidy warnings (#4359)
ea6b1d8449 [wpiutil] Remove unused ManagedStatic class (#4358)
ac9be78e27 Use stricter C++ type conversions (#4357)
151dabb2af [wpiutil] Upgrade to fmt 9.0.0 (#4337)
340465c929 [ci] Upgrade to clang-format and clang-tidy 14 (NFC) (#4347)
d45bcddd15 [examples] Add comments to StateSpaceDifferentialDrive (#4341)
0e0786331a Update LLVM libraries to 14.0.6 (#4350)
c5db23f296 [wpimath] Add Eigen sparse matrix and iterative solver support (#4349)
44abc8dfa6 [upstream_utils] Remove git version from upstream patches (#4351)
3fdb2f767d [wpimath] Add comments with Ramsete equations (#4348)
0485f05da9 [wpilibjExamples] Upgrade jacoco to match allwpilib (#4346)
0a5eb65231 [wpinet] Handle empty txt block for mdns announcer (#4072)
19ffebaf3e [wpilib] Add reference to I2C Lockup to API Docs (NFC) (#4340)
ce1a90d639 [hal] Replace SerialHelper "goto done" with continue (#4342)
d25af48797 [ci] Make upstream_utils CI fail on untracked files (#4339)
ebb836dacb [examples] Fix negations in event loop examples (#4334)
d83e202f00 [upstream_utils] Update paths in update_fmt.py (#4338)
3ccf806064 [wpimath] Remove redundant LinearFilter.finiteDifference() argument (#4335)
6f1e01f8bd [wpimath] Document example of online filtering for LinearFilter.finiteDifference() (#4336)
1023c34b1c [readme] Update location of ni-libraries (#4333)
faa29d596c [wpilib] Improve Notifier docs (NFC) (#4326)
add00a96ed [wpimath] Improve DifferentialDriveAccelerationLimiter docs (NFC) (#4323)
82fac41244 [wpimath] Better document trackwidth parameters (NFC) (#4324)
5eb44e22a9 Format Python scripts with black (NFC) (#4325)
2e09fa7325 [build] Fix mpack cmake (#4322)
fe3c24b1ee [command] Add ignoringDisable decorator (#4305)
aa221597bc [build] Add M1 builds, change arm name, update to 2023 deps (#4315)
579a8ee229 [ci] Use one worker for Windows release Gradle build (#4318)
5105c5eab6 [wpilibj] Change "final" to "exit" in the IterativeRobotBase JavaDoc (NFC) (#4317)
787fe6e7a5 [wpiutil] Separate third party libraries (#4190)
6671f8d099 [wpigui] Update portable file dialogs (#4316)
9ac9b69aa2 [command] Reorder Scheduler operations (#4261)
e61028cb18 [build] halsim_gui: Add wpinet dependency (#4313)
661d23eaf5 [glass] Add precision setting for NetworkTable view (#4311)
666040e3e5 [hal] Throw exceptions for invalid sizes in I2C and SPI JNI (#4312)
aebc272449 [build] Upgrade to spotbugs Gradle plugin 5.0.8 (#4310)
fd884581e4 [wpilib] Add BooleanEvent/Trigger factories on HID classes (#4247)
9b1bf5c7f1 [wpimath] Move Drake and Eigen to thirdparty folders (#4307)
c9e620a920 [wpilibc] Change EventLoop data structure to vector (#4304)
41d40dd62f [wpinet] Fix libuv unused variable warning on Mac (#4299)
30f5b68264 [wpinet] Fix JNI loading error (#4295)
f7b3f4b90e [examples] Getting Started: Change Joystick to XboxController (#4194)
a99c11c14c [wpimath] Replace UKF implementation with square root form (#4168)
45b7fc445b [wpilib] Add EventLoop (#4104)
16a4888c52 [wpilib] Default off LiveWindow telemetry (#4301)
17752f1337 [ci] Split debug and release Windows builds (#4277)
abb45a68db [commands] Remove custom test wrappers (#4296)
1280a54ef3 [upstream_utils]: Make work with Python 3.8 (#4298)
f2d243fa68 [build] Change defaults for Java lints (#4300)
a4787130f4 Update using development build to work with 2023 gradlerio (#4294)
af7985e46c [wpiutil] Use invoke_result_t instead of result_of in future.h (#4293)
e9d1b5c2d0 [hal] Remove deprecated SimDevice functions (#4209)
45b598d236 [wpilibj] Add toString() methods to Color and Color8Bit (#4286)
fc37265da5 [wpimath] Add angle measurement convention to ArmFeedforward docs (NFC) (#4285)
a4ec13eb0e [wpilibjexamples] Remove unnecessary voltage desaturation
2fa52007af [wpilibc] Use GetBatteryVoltage() in MotorController::SetVoltage
d9f9cd1140 [wpimath] Reset prev_time on pose estimator reset (#4283)
8b6df88783 [wpilibj] Tachometer.getFrequency(): Fix bug (#4281)
345cff08c0 [wpiutil] Make wpi::array constexpr (#4278)
57428112ac [wpimath] Upgrade to Drake v1.3.0 (#4279)
a18d4ff154 [build] Fix tools not being copied when built with -Ponly* (#4276)
d1cd07b9f3 [wpigui] Add OpenURL (#4273)
e67f8e917a [glass] Use glfwSetKeyCallback for Enter key remap (#4275)
be2fedfe50 [wpimath] Add stdexcept include for std::invalid_argument (IWYU) (#4274)
7ad2be172e [build] Update native-utils to 2023.0.1 (#4272)
abc605c9c9 [ci] Update workflows to 20.04 base image (#4271)
3e94805220 [wpiutil] Reduce llvm collections patches (#4268)
db2e1d170e [upstream_utils] Document how to update thirdparty libraries (#4253)
96ebdcaf16 [wpimath] Remove unused Eigen AutoDiff module (#4267)
553b2a3b12 [upstream_utils] Fix stackwalker (#4265)
3e13ef42eb [wpilibc] Add missing std::array #include (include-what-you-use) (#4266)
d651a1fcec Fix internal deprecation warnings (#4257)
b193b318c1 [commands] Add unless() decorator (#4244)
ef3714223b [commands] Remove docs reference to obsolete interrupted() method (NFC) (#4262)
3d8dbbbac3 [readme] Add quickstart (#4225)
013efdde25 [wpinet] Wrap a number of newer libuv features (#4260)
816aa4e465 [wpilib] Add Pneumatics sim classes (#4033)
046c2c8972 [wpilibc] Rename SpeedControllerGroupTest.cpp (#4258)
d80e9cdf64 [upstream_utils] Use shallow clones for thirdparty repos (#4255)
7576136b4a [upstream_utils] Make update_llvm.py executable (#4254)
c3b223ce60 [wpiutil] Vendor llvm and update to 13.0.0 (#4224)
5aa67f56e6 [wpimath] Clean up math comments (#4252)
fff4d1f44e [wpimath] Extend Eigen warning suppression to GCC 12 (#4251)
0d9956273c [wpimath] Add CoordinateSystem.convert() translation and rotation overloads (#4227)
3fada4e0b4 [wpinet] Update to libuv 1.44.1 (#4232)
65b23ac45e [wpilibc] Fix return value of DriverStation::GetJoystickAxisType() (#4230)
4ac34c0141 [upstream_utils] Cleanup update_libuv.py (#4249)
8bd614bb1e [upstream_utils] Use "git am" instead of "git apply" for patches (#4248)
4253d6d5f0 [upstream_utils] Apply "git am" patches individually (#4250)
6a4752dcdc Fix GCC 12.1 warning false positives (#4246)
5876b40f08 [wpimath] Memoize CoordinateSystem and CoordinateAxis statics (#4241)
5983434a70 [cameraserver] Replace IterativeRobot in comment sample code with TimedRobot (#4238)
a3d44a1e69 [wpimath] Add Translation2d.getAngle() (#4217)
d364bbd5a7 [upstream_utils] Give vendor update scripts execute permissions (#4226)
f341e1b2be [wpimath] Document standard coordinate systems better (NFC) (#4228)
9af389b200 [wpinet] AddrToName: Initialize name (#4229)
2ae4adf2d7 [ci] Add wpiformat command to PRs (#4223)
178b2a1e88 Contributing.md: Correct version of clang-format used (#4222)
18db343cdc [wpiutil, wpinet] Vendor libuv, stack walker (#4219)
f0c821282a [build] Use artifactory mirror (#4220)
d673ead481 [wpinet] Move network portions of wpiutil into new wpinet library (#4077)
b33715db15 [wpimath] Add CoordinateSystem class (#4214)
99424ad562 [sim] Allow creating a PWMSim object from a PWMMotorController (#4039)
dc6f641fd2 [wpimath] PIDController: Reset position and velocity error when reset() is called. (#4064)
f20a20f3f1 [wpimath] Add 3D geometry classes (#4175)
708a4bc3bc [wpimath] Conserve previously calculated swerve module angles when updating states for stationary ChassisSpeeds (#4208)
ef7ed21a9d [wpimath] Improve accuracy of ComputerVisionUtil.calculateDistanceToTarget() (#4215)
b1abf455c1 [wpimath] LTVUnicycleController: Use LUT, provide default hyperparameters (#4213)
d5456cf278 [wpimath] LTVDifferentialDriveController: Remove unused variable (#4212)
99343d40ba [command] Remove old command-based framework (#4211)
ee03a7ad3b Remove most 2022 deprecations (#4205)
ce1a7d698a [wpimath] Refactor WheelVoltages inner class to a separate file (#4203)
87bf70fa8e [wpimath] Add LTV controllers (#4094)
ebd2a303bf [wpimath] Remove deprecated MakeMatrix() function (#4202)
e28776d361 [wpimath] LinearSystemLoop: Add extern templates for common cases
dac1429aa9 [wpimath] LQR: Use extern template instead of Impl class
e767605e94 [wpimath] Add typedefs for common types
97c493241f [wpimath] UnscentedKalmanFilter: Move implementation out-of-line
8ea90d8bc9 [wpimath] ExtendedKalmanFilter: Move implementation out-of-line
ae7b1851ec [wpimath] KalmanFilter: Use extern template instead of Impl class
e3d62c22d3 [wpimath] Add extern templates for common cases
7200c4951d [wpiutil] SymbolExports: Add WPILIB_IMPORTS for dllimport
84056c9347 [wpiutil] SymbolExports: Add EXPORT_TEMPLATE_DECLARE/DEFINE
09cf6eeecb [wpimath] ApplyDeadband: add a scale param (#3865)
03230fc842 [build,ci] Enable artifactory build cache (#4200)
63cf3aaa3f [examples] Don't square ArcadeDrive inputs in auto (#4201)
18ff694f02 [wpimath] Add Rotation2d.fromRadians factory (#4178)
4f79ceedd9 [wpilibc] Add missing #include (#4198)
f7ca72fb41 [command] Rename PerpetualCommand to EndlessCommand (#4177)
a06b3f0307 [hal] Correct documentation on updateNotifierAlarm (#4156)
d926dd1610 [wpimath] Fix pose estimator performance (#4111)
51bc893bc5 [wpiutil] CircularBuffer: Change Java package-private methods to public (#4181)
fbe761f7f6 [build] Increase Gradle JVM heap size (#4172)
5ebe911933 [wpimath] Add DifferentialDriveAccelerationLimiter (#4091)
3919250da2 [wpilibj] Remove finalizers (#4158)
b3aee28388 [commands] Allow BooleanSupplier for Trigger operations (#4103)
9d20ab3024 [wpilib] Allow disabling ElevatorSim gravity (#4145)
aaa69f6717 [ci] Remove 32-bit Windows builds (#4078)
355a11a414 Update Java linters and fix new PMD errors (#4157)
ffc69d406c [examples] Reduce suggested acceleration in Ramsete example (#4171)
922d50079a [wpimath] Units: fix comment in degreesToRotations (NFC) (#4159)
dd163b62ae [wpimath] Rotation2d: Add factory method that uses rotations (#4166)
bd80e220b9 [ci] Upgrade CMake actions (#4161)
aef4b16d4c [wpimath] Remove unnecessary NOLINT in LinearPlantInversionFeedforward (NFC) (#4155)
975171609e [wpilib] Compressor: Rename enabled to isEnabled (#4147)
5bf46a9093 [wpimath] Add ComputerVisionUtil (#4124)
f27a1f9bfb [commands] Fix JoystickButton.getAsBoolean (#4131)
1b26e2d5da [commands] Add RepeatCommand (#4009)
88222daa3d [hal] Fix misspelling in AnalogInput/Output docs (NFC) (#4153)
81c5b41ce1 [wpilibj] Document MechanismLigament2d angle unit (NFC) (#4142)
9650e6733e [wpiutil] DataLog: Document finish and thread safety (NFC) (#4140)
c8905ec29a [wpimath] Remove ImplicitModelFollower dt argument (#4119)
b4620f01f9 [wpimath] Fix Rotation2d interpolation in Java (#4125)
2e462a19d3 [wpimath] Constexprify units unary operators (#4138)
069f932e59 [build] Fix gl3w cmake build (#4139)
126e3de91a [wpilibc] Remove unused SetPriority() call from Ultrasonic (#4123)
ba0dccaae4 [wpimath] Fix reference to Rotation2d.fromRadians() (#4118)
e1b6e5f212 [wpilib] Improve MotorSafety documentation (NFC) (#4120)
8d79dc8738 [wpimath] Add ImplicitModelFollower (#4056)
78108c2aba [wpimath] Fix PIDController having incorrect error after calling SetSetpoint() (#4070)
cdafc723fb [examples] Remove unused LinearPlantInversionFeedforward includes (#4069)
0d70884dce [wpimath] Add InterpolatedTreeMap (#4073)
765efa325e [wpimath] Remove redundant column index from vectors (#4116)
89ffcbbe41 [wpimath] Update TrapezoidProfile class name in comment (NFC) (#4107)
95ae23b0e7 [wpimath] Improve EKF numerical stability (#4093)
d5cb6fed67 [wpimath] Support zero cost entries in MakeCostMatrix() (#4100)
d0fef18378 [wpimath] Remove redundant `this.` from ExtendedKalmanFilter.java (#4115)
d640c0f41f [wpimath] Fix pose estimator local measurement standard deviation docs (NFC) (#4113)
a2fa5e3ff7 [wpilibc] BatterySim: Provide non-initializer list versions of Calculate (#4076)
a3eea9958e [hal] Add link to FRC CAN Spec (NFC) (#4086)
db27331d7b [wpilib] Update DifferentialDrive docs (NFC) (#4085)
fdfb31f164 [dlt] Export boolean[] values (#4082)
f93c3331b3 [wpigui] disable changing directory when initializing on MacOS (#4092)
ab7ac4fbb9 [build] Fix various warnings in cmake builds (#4081)
bc39a1a293 [wpilibc] Fix moved pneumatics objects not destructing properly (#4068)
2668130e70 [wpimath] Remove SwerveDrivePoseEstimator encoder reset warning (#4066)
d27ed3722b [ci] Set actions workflow concurrency (#4060)
dae18308c9 [wpimath] Minor fixes to Rotation2d docs (NFC) (#4055)
d66555e42f [datalogtool] Add datalogtool
9f52d8a3b1 [wpilib] DriverStation: Add DataLog support for modes and joystick data
757ea91932 [wpilib] Add DataLogManager
02a804f1c5 [ntcore] Add DataLog support
9b500df0d9 [wpiutil] Add high speed data logging
5a89575b3a [wpiutil] Import customized LLVM MemoryBuffer
b8c4d7527b [wpiutil] Add MappedFileRegion
ac5d46cfa7 [wpilibc] Fix ProfiledPID SetTolerance default velocity value (#4054)
bc9e96e86f [wpilib] Absolute Encoder API and behavior fixes (#4052)
f88c435dd0 [hal] Add mechanism to cancel all periodic callbacks (#4049)
Change-Id: I49aa5b08abbefc7a045e99e19d48ce2cd8fc4d1b
git-subtree-dir: third_party/allwpilib
git-subtree-split: 83f1860047c86aa3330fcb41caf3b2047e074804
Signed-off-by: James Kuszmaul <jabukuszmaul+collab@gmail.com>
diff --git a/wpiutil/src/main/native/cpp/Base64.cpp b/wpiutil/src/main/native/cpp/Base64.cpp
index 8f1f810..bc1d3ba 100644
--- a/wpiutil/src/main/native/cpp/Base64.cpp
+++ b/wpiutil/src/main/native/cpp/Base64.cpp
@@ -138,8 +138,8 @@
return Base64Decode(os, encoded);
}
-span<uint8_t> Base64Decode(std::string_view encoded, size_t* num_read,
- SmallVectorImpl<uint8_t>& buf) {
+std::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);
@@ -193,19 +193,19 @@
return os.str();
}
-void Base64Encode(raw_ostream& os, span<const uint8_t> plain) {
+void Base64Encode(raw_ostream& os, std::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) {
+void Base64Encode(std::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,
+std::string_view Base64Encode(std::span<const uint8_t> plain,
SmallVectorImpl<char>& buf) {
buf.clear();
raw_svector_ostream os(buf);
diff --git a/wpiutil/src/main/native/cpp/DataLog.cpp b/wpiutil/src/main/native/cpp/DataLog.cpp
new file mode 100644
index 0000000..7009628
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/DataLog.cpp
@@ -0,0 +1,821 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; 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/DataLog.h"
+
+#include "wpi/Synchronization.h"
+
+#ifndef _WIN32
+#include <unistd.h>
+#endif
+
+#ifdef _WIN32
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+
+#include <windows.h> // NOLINT(build/include_order)
+
+#endif
+
+#include <atomic>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <random>
+#include <vector>
+
+#include "fmt/format.h"
+#include "wpi/Endian.h"
+#include "wpi/Logger.h"
+#include "wpi/MathExtras.h"
+#include "wpi/fs.h"
+#include "wpi/timestamp.h"
+
+using namespace wpi::log;
+
+static constexpr size_t kBlockSize = 16 * 1024;
+static constexpr size_t kRecordMaxHeaderSize = 17;
+
+template <typename T>
+static unsigned int WriteVarInt(uint8_t* buf, T val) {
+ unsigned int len = 0;
+ do {
+ *buf++ = static_cast<unsigned int>(val) & 0xff;
+ ++len;
+ val >>= 8;
+ } while (val != 0);
+ return len;
+}
+
+// min size: 4, max size: 17
+static unsigned int WriteRecordHeader(uint8_t* buf, uint32_t entry,
+ uint64_t timestamp,
+ uint32_t payloadSize) {
+ uint8_t* origbuf = buf++;
+
+ unsigned int entryLen = WriteVarInt(buf, entry);
+ buf += entryLen;
+ unsigned int payloadLen = WriteVarInt(buf, payloadSize);
+ buf += payloadLen;
+ unsigned int timestampLen =
+ WriteVarInt(buf, timestamp == 0 ? wpi::Now() : timestamp);
+ buf += timestampLen;
+ *origbuf =
+ ((timestampLen - 1) << 4) | ((payloadLen - 1) << 2) | (entryLen - 1);
+ return buf - origbuf;
+}
+
+class DataLog::Buffer {
+ public:
+ explicit Buffer(size_t alloc = kBlockSize)
+ : m_buf{new uint8_t[alloc]}, m_maxLen{alloc} {}
+ ~Buffer() { delete[] m_buf; }
+
+ Buffer(const Buffer&) = delete;
+ Buffer& operator=(const Buffer&) = delete;
+
+ Buffer(Buffer&& oth)
+ : m_buf{oth.m_buf}, m_len{oth.m_len}, m_maxLen{oth.m_maxLen} {
+ oth.m_buf = nullptr;
+ oth.m_len = 0;
+ oth.m_maxLen = 0;
+ }
+
+ Buffer& operator=(Buffer&& oth) {
+ if (m_buf) {
+ delete[] m_buf;
+ }
+ m_buf = oth.m_buf;
+ m_len = oth.m_len;
+ m_maxLen = oth.m_maxLen;
+ oth.m_buf = nullptr;
+ oth.m_len = 0;
+ oth.m_maxLen = 0;
+ return *this;
+ }
+
+ uint8_t* Reserve(size_t size) {
+ assert(size <= GetRemaining());
+ uint8_t* rv = m_buf + m_len;
+ m_len += size;
+ return rv;
+ }
+
+ void Unreserve(size_t size) { m_len -= size; }
+
+ void Clear() { m_len = 0; }
+
+ size_t GetRemaining() const { return m_maxLen - m_len; }
+
+ std::span<uint8_t> GetData() { return {m_buf, m_len}; }
+ std::span<const uint8_t> GetData() const { return {m_buf, m_len}; }
+
+ private:
+ uint8_t* m_buf;
+ size_t m_len = 0;
+ size_t m_maxLen;
+};
+
+static void DefaultLog(unsigned int level, const char* file, unsigned int line,
+ const char* msg) {
+ if (level > wpi::WPI_LOG_INFO) {
+ fmt::print(stderr, "DataLog: {}\n", msg);
+ } else if (level == wpi::WPI_LOG_INFO) {
+ fmt::print("DataLog: {}\n", msg);
+ }
+}
+
+static wpi::Logger defaultMessageLog{DefaultLog};
+
+DataLog::DataLog(std::string_view dir, std::string_view filename, double period,
+ std::string_view extraHeader)
+ : DataLog{defaultMessageLog, dir, filename, period, extraHeader} {}
+
+DataLog::DataLog(wpi::Logger& msglog, std::string_view dir,
+ std::string_view filename, double period,
+ std::string_view extraHeader)
+ : m_msglog{msglog},
+ m_period{period},
+ m_extraHeader{extraHeader},
+ m_newFilename{filename},
+ m_thread{[this, dir = std::string{dir}] { WriterThreadMain(dir); }} {}
+
+DataLog::DataLog(std::function<void(std::span<const uint8_t> data)> write,
+ double period, std::string_view extraHeader)
+ : DataLog{defaultMessageLog, std::move(write), period, extraHeader} {}
+
+DataLog::DataLog(wpi::Logger& msglog,
+ std::function<void(std::span<const uint8_t> data)> write,
+ double period, std::string_view extraHeader)
+ : m_msglog{msglog},
+ m_period{period},
+ m_extraHeader{extraHeader},
+ m_thread{[this, write = std::move(write)] {
+ WriterThreadMain(std::move(write));
+ }} {}
+
+DataLog::~DataLog() {
+ {
+ std::scoped_lock lock{m_mutex};
+ m_active = false;
+ m_doFlush = true;
+ }
+ m_cond.notify_all();
+ m_thread.join();
+}
+
+void DataLog::SetFilename(std::string_view filename) {
+ {
+ std::scoped_lock lock{m_mutex};
+ m_newFilename = filename;
+ }
+ m_cond.notify_all();
+}
+
+void DataLog::Flush() {
+ {
+ std::scoped_lock lock{m_mutex};
+ m_doFlush = true;
+ }
+ m_cond.notify_all();
+}
+
+void DataLog::Pause() {
+ std::scoped_lock lock{m_mutex};
+ m_paused = true;
+}
+
+void DataLog::Resume() {
+ std::scoped_lock lock{m_mutex};
+ m_paused = false;
+}
+
+static void WriteToFile(fs::file_t f, std::span<const uint8_t> data,
+ std::string_view filename, wpi::Logger& msglog) {
+ do {
+#ifdef _WIN32
+ DWORD ret;
+ if (!WriteFile(f, data.data(), data.size(), &ret, nullptr)) {
+ WPI_ERROR(msglog, "Error writing to log file '{}': {}", filename,
+ GetLastError());
+ break;
+ }
+#else
+ ssize_t ret = ::write(f, data.data(), data.size());
+ if (ret < 0) {
+ // If it's a recoverable error, swallow it and retry the write
+ if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) {
+ continue;
+ }
+
+ // Otherwise it's a non-recoverable error; quit trying
+ WPI_ERROR(msglog, "Error writing to log file '{}': {}", filename,
+ std::strerror(errno));
+ break;
+ }
+#endif
+
+ // The write may have written some or all of the data
+ data = data.subspan(ret);
+ } while (data.size() > 0);
+}
+
+static std::string MakeRandomFilename() {
+ // build random filename
+ static std::random_device dev;
+ static std::mt19937 rng(dev());
+ std::uniform_int_distribution<int> dist(0, 15);
+ const char* v = "0123456789abcdef";
+ std::string filename = "wpilog_";
+ for (int i = 0; i < 16; i++) {
+ filename += v[dist(rng)];
+ }
+ filename += ".wpilog";
+ return filename;
+}
+
+void DataLog::WriterThreadMain(std::string_view dir) {
+ std::chrono::duration<double> periodTime{m_period};
+
+ std::error_code ec;
+ fs::path dirPath{dir};
+ std::string filename;
+
+ {
+ std::scoped_lock lock{m_mutex};
+ filename = std::move(m_newFilename);
+ m_newFilename.clear();
+ }
+
+ if (filename.empty()) {
+ filename = MakeRandomFilename();
+ }
+
+ // try preferred filename, or randomize it a few times, before giving up
+ fs::file_t f;
+ for (int i = 0; i < 5; ++i) {
+ // open file for append
+#ifdef _WIN32
+ // WIN32 doesn't allow combination of CreateNew and Append
+ f = fs::OpenFileForWrite(dirPath / filename, ec, fs::CD_CreateNew,
+ fs::OF_None);
+#else
+ f = fs::OpenFileForWrite(dirPath / filename, ec, fs::CD_CreateNew,
+ fs::OF_Append);
+#endif
+ if (ec) {
+ WPI_ERROR(m_msglog, "Could not open log file '{}': {}",
+ (dirPath / filename).string(), ec.message());
+ // try again with random filename
+ filename = MakeRandomFilename();
+ } else {
+ break;
+ }
+ }
+
+ if (f == fs::kInvalidFile) {
+ WPI_ERROR(m_msglog, "Could not open log file, no log being saved");
+ } else {
+ WPI_INFO(m_msglog, "Logging to '{}'", (dirPath / filename).string());
+ }
+
+ // write header (version 1.0)
+ if (f != fs::kInvalidFile) {
+ const uint8_t header[] = {'W', 'P', 'I', 'L', 'O', 'G', 0, 1};
+ WriteToFile(f, header, filename, m_msglog);
+ uint8_t extraLen[4];
+ support::endian::write32le(extraLen, m_extraHeader.size());
+ WriteToFile(f, extraLen, filename, m_msglog);
+ if (m_extraHeader.size() > 0) {
+ WriteToFile(f,
+ {reinterpret_cast<const uint8_t*>(m_extraHeader.data()),
+ m_extraHeader.size()},
+ filename, m_msglog);
+ }
+ }
+
+ std::vector<Buffer> toWrite;
+
+ std::unique_lock lock{m_mutex};
+ while (m_active) {
+ bool doFlush = false;
+ auto timeoutTime = std::chrono::steady_clock::now() + periodTime;
+ if (m_cond.wait_until(lock, timeoutTime) == std::cv_status::timeout) {
+ doFlush = true;
+ }
+
+ if (!m_newFilename.empty()) {
+ auto newFilename = std::move(m_newFilename);
+ m_newFilename.clear();
+ lock.unlock();
+ // rename
+ if (filename != newFilename) {
+ fs::rename(dirPath / filename, dirPath / newFilename, ec);
+ }
+ if (ec) {
+ WPI_ERROR(m_msglog, "Could not rename log file from '{}' to '{}': {}",
+ filename, newFilename, ec.message());
+ } else {
+ WPI_INFO(m_msglog, "Renamed log file from '{}' to '{}'", filename,
+ newFilename);
+ }
+ filename = std::move(newFilename);
+ lock.lock();
+ }
+
+ if (doFlush || m_doFlush) {
+ // flush to file
+ m_doFlush = false;
+ if (m_outgoing.empty()) {
+ continue;
+ }
+ // swap outgoing with empty vector
+ toWrite.swap(m_outgoing);
+
+ if (f != fs::kInvalidFile) {
+ lock.unlock();
+ // write buffers to file
+ for (auto&& buf : toWrite) {
+ WriteToFile(f, buf.GetData(), filename, m_msglog);
+ }
+
+ // sync to storage
+#if defined(__linux__)
+ ::fdatasync(f);
+#elif defined(__APPLE__)
+ ::fsync(f);
+#endif
+ lock.lock();
+ }
+
+ // release buffers back to free list
+ for (auto&& buf : toWrite) {
+ buf.Clear();
+ m_free.emplace_back(std::move(buf));
+ }
+ toWrite.resize(0);
+ }
+ }
+
+ if (f != fs::kInvalidFile) {
+ fs::CloseFile(f);
+ }
+}
+
+void DataLog::WriterThreadMain(
+ std::function<void(std::span<const uint8_t> data)> write) {
+ std::chrono::duration<double> periodTime{m_period};
+
+ // write header (version 1.0)
+ {
+ const uint8_t header[] = {'W', 'P', 'I', 'L', 'O', 'G', 0, 1};
+ write(header);
+ uint8_t extraLen[4];
+ support::endian::write32le(extraLen, m_extraHeader.size());
+ write(extraLen);
+ if (m_extraHeader.size() > 0) {
+ write({reinterpret_cast<const uint8_t*>(m_extraHeader.data()),
+ m_extraHeader.size()});
+ }
+ }
+
+ std::vector<Buffer> toWrite;
+
+ std::unique_lock lock{m_mutex};
+ while (m_active) {
+ bool doFlush = false;
+ auto timeoutTime = std::chrono::steady_clock::now() + periodTime;
+ if (m_cond.wait_until(lock, timeoutTime) == std::cv_status::timeout) {
+ doFlush = true;
+ }
+
+ if (doFlush || m_doFlush) {
+ // flush to file
+ m_doFlush = false;
+ if (m_outgoing.empty()) {
+ continue;
+ }
+ // swap outgoing with empty vector
+ toWrite.swap(m_outgoing);
+
+ lock.unlock();
+ // write buffers
+ for (auto&& buf : toWrite) {
+ if (!buf.GetData().empty()) {
+ write(buf.GetData());
+ }
+ }
+ lock.lock();
+
+ // release buffers back to free list
+ for (auto&& buf : toWrite) {
+ buf.Clear();
+ m_free.emplace_back(std::move(buf));
+ }
+ toWrite.resize(0);
+ }
+ }
+
+ write({}); // indicate EOF
+}
+
+// Control records use the following format:
+// 1-byte type
+// 4-byte entry
+// rest of data (depending on type)
+
+int DataLog::Start(std::string_view name, std::string_view type,
+ std::string_view metadata, int64_t timestamp) {
+ std::scoped_lock lock{m_mutex};
+ auto& entryInfo = m_entries[name];
+ if (entryInfo.id == 0) {
+ entryInfo.id = ++m_lastId;
+ }
+ auto& savedCount = m_entryCounts[entryInfo.id];
+ ++savedCount;
+ if (savedCount > 1) {
+ if (entryInfo.type != type) {
+ WPI_ERROR(m_msglog,
+ "type mismatch for '{}': was '{}', requested '{}'; ignoring",
+ name, entryInfo.type, type);
+ return 0;
+ }
+ return entryInfo.id;
+ }
+ entryInfo.type = type;
+ size_t strsize = name.size() + type.size() + metadata.size();
+ uint8_t* buf = StartRecord(0, timestamp, 5 + 12 + strsize, 5);
+ *buf++ = impl::kControlStart;
+ wpi::support::endian::write32le(buf, entryInfo.id);
+ AppendStringImpl(name);
+ AppendStringImpl(type);
+ AppendStringImpl(metadata);
+
+ return entryInfo.id;
+}
+
+void DataLog::Finish(int entry, int64_t timestamp) {
+ if (entry <= 0) {
+ return;
+ }
+ std::scoped_lock lock{m_mutex};
+ auto& savedCount = m_entryCounts[entry];
+ if (savedCount == 0) {
+ return;
+ }
+ --savedCount;
+ if (savedCount != 0) {
+ return;
+ }
+ m_entryCounts.erase(entry);
+ uint8_t* buf = StartRecord(0, timestamp, 5, 5);
+ *buf++ = impl::kControlFinish;
+ wpi::support::endian::write32le(buf, entry);
+}
+
+void DataLog::SetMetadata(int entry, std::string_view metadata,
+ int64_t timestamp) {
+ if (entry <= 0) {
+ return;
+ }
+ std::scoped_lock lock{m_mutex};
+ uint8_t* buf = StartRecord(0, timestamp, 5 + 4 + metadata.size(), 5);
+ *buf++ = impl::kControlSetMetadata;
+ wpi::support::endian::write32le(buf, entry);
+ AppendStringImpl(metadata);
+}
+
+uint8_t* DataLog::Reserve(size_t size) {
+ assert(size <= kBlockSize);
+ if (m_outgoing.empty() || size > m_outgoing.back().GetRemaining()) {
+ if (m_free.empty()) {
+ m_outgoing.emplace_back();
+ } else {
+ m_outgoing.emplace_back(std::move(m_free.back()));
+ m_free.pop_back();
+ }
+ }
+ return m_outgoing.back().Reserve(size);
+}
+
+uint8_t* DataLog::StartRecord(uint32_t entry, uint64_t timestamp,
+ uint32_t payloadSize, size_t reserveSize) {
+ uint8_t* buf = Reserve(kRecordMaxHeaderSize + reserveSize);
+ auto headerLen = WriteRecordHeader(buf, entry, timestamp, payloadSize);
+ m_outgoing.back().Unreserve(kRecordMaxHeaderSize - headerLen);
+ buf += headerLen;
+ return buf;
+}
+
+void DataLog::AppendImpl(std::span<const uint8_t> data) {
+ while (data.size() > kBlockSize) {
+ uint8_t* buf = Reserve(kBlockSize);
+ std::memcpy(buf, data.data(), kBlockSize);
+ data = data.subspan(kBlockSize);
+ }
+ uint8_t* buf = Reserve(data.size());
+ std::memcpy(buf, data.data(), data.size());
+}
+
+void DataLog::AppendStringImpl(std::string_view str) {
+ uint8_t* buf = Reserve(4);
+ wpi::support::endian::write32le(buf, str.size());
+ AppendImpl({reinterpret_cast<const uint8_t*>(str.data()), str.size()});
+}
+
+void DataLog::AppendRaw(int entry, std::span<const uint8_t> data,
+ int64_t timestamp) {
+ if (entry <= 0) {
+ return;
+ }
+ std::scoped_lock lock{m_mutex};
+ if (m_paused) {
+ return;
+ }
+ StartRecord(entry, timestamp, data.size(), 0);
+ AppendImpl(data);
+}
+
+void DataLog::AppendRaw2(int entry,
+ std::span<const std::span<const uint8_t>> data,
+ int64_t timestamp) {
+ if (entry <= 0) {
+ return;
+ }
+ std::scoped_lock lock{m_mutex};
+ if (m_paused) {
+ return;
+ }
+ size_t size = 0;
+ for (auto&& chunk : data) {
+ size += chunk.size();
+ }
+ StartRecord(entry, timestamp, size, 0);
+ for (auto chunk : data) {
+ AppendImpl(chunk);
+ }
+}
+
+void DataLog::AppendBoolean(int entry, bool value, int64_t timestamp) {
+ if (entry <= 0) {
+ return;
+ }
+ std::scoped_lock lock{m_mutex};
+ if (m_paused) {
+ return;
+ }
+ uint8_t* buf = StartRecord(entry, timestamp, 1, 1);
+ buf[0] = value ? 1 : 0;
+}
+
+void DataLog::AppendInteger(int entry, int64_t value, int64_t timestamp) {
+ if (entry <= 0) {
+ return;
+ }
+ std::scoped_lock lock{m_mutex};
+ if (m_paused) {
+ return;
+ }
+ uint8_t* buf = StartRecord(entry, timestamp, 8, 8);
+ wpi::support::endian::write64le(buf, value);
+}
+
+void DataLog::AppendFloat(int entry, float value, int64_t timestamp) {
+ if (entry <= 0) {
+ return;
+ }
+ std::scoped_lock lock{m_mutex};
+ if (m_paused) {
+ return;
+ }
+ uint8_t* buf = StartRecord(entry, timestamp, 4, 4);
+ if constexpr (wpi::support::endian::system_endianness() ==
+ wpi::support::little) {
+ std::memcpy(buf, &value, 4);
+ } else {
+ wpi::support::endian::write32le(buf, wpi::FloatToBits(value));
+ }
+}
+
+void DataLog::AppendDouble(int entry, double value, int64_t timestamp) {
+ if (entry <= 0) {
+ return;
+ }
+ std::scoped_lock lock{m_mutex};
+ if (m_paused) {
+ return;
+ }
+ uint8_t* buf = StartRecord(entry, timestamp, 8, 8);
+ if constexpr (wpi::support::endian::system_endianness() ==
+ wpi::support::little) {
+ std::memcpy(buf, &value, 8);
+ } else {
+ wpi::support::endian::write64le(buf, wpi::DoubleToBits(value));
+ }
+}
+
+void DataLog::AppendString(int entry, std::string_view value,
+ int64_t timestamp) {
+ AppendRaw(entry,
+ {reinterpret_cast<const uint8_t*>(value.data()), value.size()},
+ timestamp);
+}
+
+void DataLog::AppendBooleanArray(int entry, std::span<const bool> arr,
+ int64_t timestamp) {
+ if (entry <= 0) {
+ return;
+ }
+ std::scoped_lock lock{m_mutex};
+ if (m_paused) {
+ return;
+ }
+ StartRecord(entry, timestamp, arr.size(), 0);
+ uint8_t* buf;
+ while (arr.size() > kBlockSize) {
+ buf = Reserve(kBlockSize);
+ for (auto val : arr.subspan(0, kBlockSize)) {
+ *buf++ = val ? 1 : 0;
+ }
+ arr = arr.subspan(kBlockSize);
+ }
+ buf = Reserve(arr.size());
+ for (auto val : arr) {
+ *buf++ = val ? 1 : 0;
+ }
+}
+
+void DataLog::AppendBooleanArray(int entry, std::span<const int> arr,
+ int64_t timestamp) {
+ if (entry <= 0) {
+ return;
+ }
+ std::scoped_lock lock{m_mutex};
+ if (m_paused) {
+ return;
+ }
+ StartRecord(entry, timestamp, arr.size(), 0);
+ uint8_t* buf;
+ while (arr.size() > kBlockSize) {
+ buf = Reserve(kBlockSize);
+ for (auto val : arr.subspan(0, kBlockSize)) {
+ *buf++ = val & 1;
+ }
+ arr = arr.subspan(kBlockSize);
+ }
+ buf = Reserve(arr.size());
+ for (auto val : arr) {
+ *buf++ = val & 1;
+ }
+}
+
+void DataLog::AppendBooleanArray(int entry, std::span<const uint8_t> arr,
+ int64_t timestamp) {
+ AppendRaw(entry, arr, timestamp);
+}
+
+void DataLog::AppendIntegerArray(int entry, std::span<const int64_t> arr,
+ int64_t timestamp) {
+ if constexpr (wpi::support::endian::system_endianness() ==
+ wpi::support::little) {
+ AppendRaw(entry,
+ {reinterpret_cast<const uint8_t*>(arr.data()), arr.size() * 8},
+ timestamp);
+ } else {
+ if (entry <= 0) {
+ return;
+ }
+ std::scoped_lock lock{m_mutex};
+ if (m_paused) {
+ return;
+ }
+ StartRecord(entry, timestamp, arr.size() * 8, 0);
+ uint8_t* buf;
+ while ((arr.size() * 8) > kBlockSize) {
+ buf = Reserve(kBlockSize);
+ for (auto val : arr.subspan(0, kBlockSize / 8)) {
+ wpi::support::endian::write64le(buf, val);
+ buf += 8;
+ }
+ arr = arr.subspan(kBlockSize / 8);
+ }
+ buf = Reserve(arr.size() * 8);
+ for (auto val : arr) {
+ wpi::support::endian::write64le(buf, val);
+ buf += 8;
+ }
+ }
+}
+
+void DataLog::AppendFloatArray(int entry, std::span<const float> arr,
+ int64_t timestamp) {
+ if constexpr (wpi::support::endian::system_endianness() ==
+ wpi::support::little) {
+ AppendRaw(entry,
+ {reinterpret_cast<const uint8_t*>(arr.data()), arr.size() * 4},
+ timestamp);
+ } else {
+ if (entry <= 0) {
+ return;
+ }
+ std::scoped_lock lock{m_mutex};
+ if (m_paused) {
+ return;
+ }
+ StartRecord(entry, timestamp, arr.size() * 4, 0);
+ uint8_t* buf;
+ while ((arr.size() * 4) > kBlockSize) {
+ buf = Reserve(kBlockSize);
+ for (auto val : arr.subspan(0, kBlockSize / 4)) {
+ wpi::support::endian::write32le(buf, wpi::FloatToBits(val));
+ buf += 4;
+ }
+ arr = arr.subspan(kBlockSize / 4);
+ }
+ buf = Reserve(arr.size() * 4);
+ for (auto val : arr) {
+ wpi::support::endian::write32le(buf, wpi::FloatToBits(val));
+ buf += 4;
+ }
+ }
+}
+
+void DataLog::AppendDoubleArray(int entry, std::span<const double> arr,
+ int64_t timestamp) {
+ if constexpr (wpi::support::endian::system_endianness() ==
+ wpi::support::little) {
+ AppendRaw(entry,
+ {reinterpret_cast<const uint8_t*>(arr.data()), arr.size() * 8},
+ timestamp);
+ } else {
+ if (entry <= 0) {
+ return;
+ }
+ std::scoped_lock lock{m_mutex};
+ if (m_paused) {
+ return;
+ }
+ StartRecord(entry, timestamp, arr.size() * 8, 0);
+ uint8_t* buf;
+ while ((arr.size() * 8) > kBlockSize) {
+ buf = Reserve(kBlockSize);
+ for (auto val : arr.subspan(0, kBlockSize / 8)) {
+ wpi::support::endian::write64le(buf, wpi::DoubleToBits(val));
+ buf += 8;
+ }
+ arr = arr.subspan(kBlockSize / 8);
+ }
+ buf = Reserve(arr.size() * 8);
+ for (auto val : arr) {
+ wpi::support::endian::write64le(buf, wpi::DoubleToBits(val));
+ buf += 8;
+ }
+ }
+}
+
+void DataLog::AppendStringArray(int entry, std::span<const std::string> arr,
+ int64_t timestamp) {
+ if (entry <= 0) {
+ return;
+ }
+ // storage: 4-byte array length, each string prefixed by 4-byte length
+ // calculate total size
+ size_t size = 4;
+ for (auto&& str : arr) {
+ size += 4 + str.size();
+ }
+ std::scoped_lock lock{m_mutex};
+ if (m_paused) {
+ return;
+ }
+ uint8_t* buf = StartRecord(entry, timestamp, size, 4);
+ wpi::support::endian::write32le(buf, arr.size());
+ for (auto&& str : arr) {
+ AppendStringImpl(str);
+ }
+}
+
+void DataLog::AppendStringArray(int entry,
+ std::span<const std::string_view> arr,
+ int64_t timestamp) {
+ if (entry <= 0) {
+ return;
+ }
+ // storage: 4-byte array length, each string prefixed by 4-byte length
+ // calculate total size
+ size_t size = 4;
+ for (auto&& str : arr) {
+ size += 4 + str.size();
+ }
+ std::scoped_lock lock{m_mutex};
+ if (m_paused) {
+ return;
+ }
+ uint8_t* buf = StartRecord(entry, timestamp, size, 4);
+ wpi::support::endian::write32le(buf, arr.size());
+ for (auto sv : arr) {
+ AppendStringImpl(sv);
+ }
+}
diff --git a/wpiutil/src/main/native/cpp/DataLogReader.cpp b/wpiutil/src/main/native/cpp/DataLogReader.cpp
new file mode 100644
index 0000000..96f6689
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/DataLogReader.cpp
@@ -0,0 +1,307 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; 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/DataLogReader.h"
+
+#include "wpi/DataLog.h"
+#include "wpi/Endian.h"
+#include "wpi/MathExtras.h"
+
+using namespace wpi::log;
+
+static bool ReadString(std::span<const uint8_t>* buf, std::string_view* str) {
+ if (buf->size() < 4) {
+ *str = {};
+ return false;
+ }
+ uint32_t len = wpi::support::endian::read32le(buf->data());
+ if (len > (buf->size() - 4)) {
+ *str = {};
+ return false;
+ }
+ *str = {reinterpret_cast<const char*>(buf->data() + 4), len};
+ *buf = buf->subspan(len + 4);
+ return true;
+}
+
+bool DataLogRecord::IsStart() const {
+ return m_entry == 0 && m_data.size() >= 17 &&
+ m_data[0] == impl::kControlStart;
+}
+
+bool DataLogRecord::IsFinish() const {
+ return m_entry == 0 && m_data.size() == 5 &&
+ m_data[0] == impl::kControlFinish;
+}
+
+bool DataLogRecord::IsSetMetadata() const {
+ return m_entry == 0 && m_data.size() >= 9 &&
+ m_data[0] == impl::kControlSetMetadata;
+}
+
+bool DataLogRecord::GetStartData(StartRecordData* out) const {
+ if (!IsStart()) {
+ return false;
+ }
+ out->entry = wpi::support::endian::read32le(&m_data[1]);
+ auto buf = m_data.subspan(5);
+ if (!ReadString(&buf, &out->name)) {
+ return false;
+ }
+ if (!ReadString(&buf, &out->type)) {
+ return false;
+ }
+ if (!ReadString(&buf, &out->metadata)) {
+ return false;
+ }
+ return true;
+}
+
+bool DataLogRecord::GetFinishEntry(int* out) const {
+ if (!IsFinish()) {
+ return false;
+ }
+ *out = wpi::support::endian::read32le(&m_data[1]);
+ return true;
+}
+
+bool DataLogRecord::GetSetMetadataData(MetadataRecordData* out) const {
+ if (!IsSetMetadata()) {
+ return false;
+ }
+ out->entry = wpi::support::endian::read32le(&m_data[1]);
+ auto buf = m_data.subspan(5);
+ return ReadString(&buf, &out->metadata);
+}
+
+bool DataLogRecord::GetBoolean(bool* value) const {
+ if (m_data.size() != 1) {
+ return false;
+ }
+ *value = m_data[0] != 0;
+ return true;
+}
+
+bool DataLogRecord::GetInteger(int64_t* value) const {
+ if (m_data.size() != 8) {
+ return false;
+ }
+ *value = wpi::support::endian::read64le(m_data.data());
+ return true;
+}
+
+bool DataLogRecord::GetFloat(float* value) const {
+ if (m_data.size() != 4) {
+ return false;
+ }
+ *value = wpi::BitsToFloat(wpi::support::endian::read32le(m_data.data()));
+ return true;
+}
+
+bool DataLogRecord::GetDouble(double* value) const {
+ if (m_data.size() != 8) {
+ return false;
+ }
+ *value = wpi::BitsToDouble(wpi::support::endian::read64le(m_data.data()));
+ return true;
+}
+
+bool DataLogRecord::GetString(std::string_view* value) const {
+ *value = {reinterpret_cast<const char*>(m_data.data()), m_data.size()};
+ return true;
+}
+
+bool DataLogRecord::GetBooleanArray(std::vector<int>* arr) const {
+ arr->clear();
+ arr->reserve(m_data.size());
+ for (auto v : m_data) {
+ arr->push_back(v);
+ }
+ return true;
+}
+
+bool DataLogRecord::GetIntegerArray(std::vector<int64_t>* arr) const {
+ arr->clear();
+ if ((m_data.size() % 8) != 0) {
+ return false;
+ }
+ arr->reserve(m_data.size() / 8);
+ for (size_t pos = 0; pos < m_data.size(); pos += 8) {
+ arr->push_back(wpi::support::endian::read64le(&m_data[pos]));
+ }
+ return true;
+}
+
+bool DataLogRecord::GetFloatArray(std::vector<float>* arr) const {
+ arr->clear();
+ if ((m_data.size() % 4) != 0) {
+ return false;
+ }
+ arr->reserve(m_data.size() / 4);
+ for (size_t pos = 0; pos < m_data.size(); pos += 4) {
+ arr->push_back(
+ wpi::BitsToFloat(wpi::support::endian::read32le(&m_data[pos])));
+ }
+ return true;
+}
+
+bool DataLogRecord::GetDoubleArray(std::vector<double>* arr) const {
+ arr->clear();
+ if ((m_data.size() % 8) != 0) {
+ return false;
+ }
+ arr->reserve(m_data.size() / 8);
+ for (size_t pos = 0; pos < m_data.size(); pos += 8) {
+ arr->push_back(
+ wpi::BitsToDouble(wpi::support::endian::read64le(&m_data[pos])));
+ }
+ return true;
+}
+
+bool DataLogRecord::GetStringArray(std::vector<std::string_view>* arr) const {
+ arr->clear();
+ if (m_data.size() < 4) {
+ return false;
+ }
+ uint32_t size = wpi::support::endian::read32le(m_data.data());
+ // sanity check size
+ if (size > ((m_data.size() - 4) / 4)) {
+ return false;
+ }
+ auto buf = m_data.subspan(4);
+ arr->reserve(size);
+ for (uint32_t i = 0; i < size; ++i) {
+ std::string_view str;
+ if (!ReadString(&buf, &str)) {
+ arr->clear();
+ return false;
+ }
+ arr->push_back(str);
+ }
+ // any left over? treat as corrupt
+ if (!buf.empty()) {
+ arr->clear();
+ return false;
+ }
+ return true;
+}
+
+DataLogReader::DataLogReader(std::unique_ptr<MemoryBuffer> buffer)
+ : m_buf{std::move(buffer)} {}
+
+bool DataLogReader::IsValid() const {
+ if (!m_buf) {
+ return false;
+ }
+ auto buf = m_buf->GetBuffer();
+ return buf.size() >= 12 &&
+ std::string_view{reinterpret_cast<const char*>(buf.data()), 6} ==
+ "WPILOG" &&
+ wpi::support::endian::read16le(&buf[6]) >= 0x0100;
+}
+
+uint16_t DataLogReader::GetVersion() const {
+ if (!m_buf) {
+ return 0;
+ }
+ auto buf = m_buf->GetBuffer();
+ if (buf.size() < 12) {
+ return 0;
+ }
+ return wpi::support::endian::read16le(&buf[6]);
+}
+
+std::string_view DataLogReader::GetExtraHeader() const {
+ if (!m_buf) {
+ return {};
+ }
+ auto buf = m_buf->GetBuffer();
+ if (buf.size() < 8) {
+ return {};
+ }
+ std::string_view rv;
+ buf = buf.subspan(8);
+ ReadString(&buf, &rv);
+ return rv;
+}
+
+DataLogReader::iterator DataLogReader::begin() const {
+ if (!m_buf) {
+ return end();
+ }
+ auto buf = m_buf->GetBuffer();
+ if (buf.size() < 12) {
+ return end();
+ }
+ uint32_t size = wpi::support::endian::read32le(&buf[8]);
+ if (buf.size() < (12 + size)) {
+ return end();
+ }
+ return DataLogIterator{this, 12 + size};
+}
+
+static uint64_t ReadVarInt(std::span<const uint8_t> buf) {
+ uint64_t val = 0;
+ int shift = 0;
+ for (auto v : buf) {
+ val |= static_cast<uint64_t>(v) << shift;
+ shift += 8;
+ }
+ return val;
+}
+
+bool DataLogReader::GetRecord(size_t* pos, DataLogRecord* out) const {
+ if (!m_buf) {
+ return false;
+ }
+ auto buf = m_buf->GetBuffer();
+ if (*pos >= buf.size()) {
+ return false;
+ }
+ buf = buf.subspan(*pos);
+ if (buf.size() < 4) { // minimum header length
+ return false;
+ }
+ unsigned int entryLen = (buf[0] & 0x3) + 1;
+ unsigned int sizeLen = ((buf[0] >> 2) & 0x3) + 1;
+ unsigned int timestampLen = ((buf[0] >> 4) & 0x7) + 1;
+ unsigned int headerLen = 1 + entryLen + sizeLen + timestampLen;
+ if (buf.size() < headerLen) {
+ return false;
+ }
+ int entry = ReadVarInt(buf.subspan(1, entryLen));
+ uint32_t size = ReadVarInt(buf.subspan(1 + entryLen, sizeLen));
+ if (size > (buf.size() - headerLen)) {
+ return false;
+ }
+ int64_t timestamp =
+ ReadVarInt(buf.subspan(1 + entryLen + sizeLen, timestampLen));
+ *out = DataLogRecord{entry, timestamp, buf.subspan(headerLen, size)};
+ *pos += headerLen + size;
+ return true;
+}
+
+bool DataLogReader::GetNextRecord(size_t* pos) const {
+ if (!m_buf) {
+ return false;
+ }
+ auto buf = m_buf->GetBuffer();
+ if (buf.size() < (*pos + 4)) { // minimum header length
+ return false;
+ }
+ unsigned int entryLen = (buf[*pos] & 0x3) + 1;
+ unsigned int sizeLen = ((buf[*pos] >> 2) & 0x3) + 1;
+ unsigned int timestampLen = ((buf[*pos] >> 4) & 0x7) + 1;
+ unsigned int headerLen = 1 + entryLen + sizeLen + timestampLen;
+ if (buf.size() < (*pos + headerLen)) {
+ return false;
+ }
+ uint32_t size = ReadVarInt(buf.subspan(*pos + 1 + entryLen, sizeLen));
+ // check this way to avoid overflow
+ if (size >= (buf.size() - *pos - headerLen)) {
+ return false;
+ }
+ *pos += headerLen + size;
+ return true;
+}
diff --git a/wpiutil/src/main/native/cpp/DsClient.cpp b/wpiutil/src/main/native/cpp/DsClient.cpp
deleted file mode 100644
index 455f10c..0000000
--- a/wpiutil/src/main/native/cpp/DsClient.cpp
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright (c) FIRST and other WPILib contributors.
-// Open Source Software; you can modify and/or share it under the terms of
-// the WPILib BSD license file in the root directory of this project.
-
-#include "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
deleted file mode 100644
index c86176e..0000000
--- a/wpiutil/src/main/native/cpp/EventLoopRunner.cpp
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright (c) FIRST and other WPILib contributors.
-// Open Source Software; you can modify and/or share it under the terms of
-// the WPILib BSD license file in the root directory of this project.
-
-#include "wpi/EventLoopRunner.h"
-
-#include "wpi/SmallVector.h"
-#include "wpi/condition_variable.h"
-#include "wpi/mutex.h"
-#include "wpi/uv/AsyncFunction.h"
-#include "wpi/uv/Loop.h"
-
-using namespace wpi;
-
-class EventLoopRunner::Thread : public SafeThread {
- public:
- using UvExecFunc = uv::AsyncFunction<void(LoopFunc)>;
-
- Thread() : m_loop(uv::Loop::Create()) {
- // set up async handles
- if (!m_loop) {
- return;
- }
-
- // run function
- m_doExec = UvExecFunc::Create(
- m_loop, [loop = m_loop.get()](auto out, LoopFunc func) {
- func(*loop);
- out.set_value();
- });
- }
-
- void Main() override {
- if (m_loop) {
- m_loop->Run();
- }
- }
-
- // the loop
- std::shared_ptr<uv::Loop> m_loop;
-
- // run function
- std::weak_ptr<UvExecFunc> m_doExec;
-};
-
-EventLoopRunner::EventLoopRunner() {
- m_owner.Start();
-}
-
-EventLoopRunner::~EventLoopRunner() {
- Stop();
-}
-
-void EventLoopRunner::Stop() {
- ExecAsync([](uv::Loop& loop) {
- // close all handles; this will (eventually) stop the loop
- loop.Walk([](uv::Handle& h) {
- h.SetLoopClosing(true);
- h.Close();
- });
- });
- m_owner.Join();
-}
-
-void EventLoopRunner::ExecAsync(LoopFunc func) {
- if (auto thr = m_owner.GetThread()) {
- if (auto doExec = thr->m_doExec.lock()) {
- doExec->Call(std::move(func));
- }
- }
-}
-
-void EventLoopRunner::ExecSync(LoopFunc func) {
- wpi::future<void> f;
- if (auto thr = m_owner.GetThread()) {
- if (auto doExec = thr->m_doExec.lock()) {
- f = doExec->Call(std::move(func));
- }
- }
- if (f.valid()) {
- f.wait();
- }
-}
-
-std::shared_ptr<uv::Loop> EventLoopRunner::GetLoop() {
- if (auto thr = m_owner.GetThread()) {
- return thr->m_loop;
- }
- return nullptr;
-}
diff --git a/wpiutil/src/main/native/cpp/HttpParser.cpp b/wpiutil/src/main/native/cpp/HttpParser.cpp
deleted file mode 100644
index 3c18e0f..0000000
--- a/wpiutil/src/main/native/cpp/HttpParser.cpp
+++ /dev/null
@@ -1,193 +0,0 @@
-// Copyright (c) FIRST and other WPILib contributors.
-// Open Source Software; you can modify and/or share it under the terms of
-// the WPILib BSD license file in the root directory of this project.
-
-#include "wpi/HttpParser.h"
-
-using namespace wpi;
-
-uint32_t HttpParser::GetParserVersion() {
- return static_cast<uint32_t>(http_parser_version());
-}
-
-HttpParser::HttpParser(Type type) {
- http_parser_init(&m_parser,
- static_cast<http_parser_type>(static_cast<int>(type)));
- m_parser.data = this;
-
- http_parser_settings_init(&m_settings);
-
- // Unlike the underlying http_parser library, we don't perform callbacks
- // (other than body) with partial data; instead we buffer and call the user
- // callback only when the data is complete.
-
- // on_message_begin: initialize our state, call user callback
- m_settings.on_message_begin = [](http_parser* p) -> int {
- auto& self = *static_cast<HttpParser*>(p->data);
- self.m_urlBuf.clear();
- self.m_state = kStart;
- self.messageBegin();
- return self.m_aborted;
- };
-
- // on_url: collect into buffer
- m_settings.on_url = [](http_parser* p, const char* at, size_t length) -> int {
- auto& self = *static_cast<HttpParser*>(p->data);
- // append to buffer
- if ((self.m_urlBuf.size() + length) > self.m_maxLength) {
- return 1;
- }
- self.m_urlBuf += std::string_view{at, length};
- self.m_state = kUrl;
- return 0;
- };
-
- // on_status: collect into buffer, call user URL callback
- m_settings.on_status = [](http_parser* p, const char* at,
- size_t length) -> int {
- auto& self = *static_cast<HttpParser*>(p->data);
- // use valueBuf for the status
- if ((self.m_valueBuf.size() + length) > self.m_maxLength) {
- return 1;
- }
- self.m_valueBuf += std::string_view{at, length};
- self.m_state = kStatus;
- return 0;
- };
-
- // on_header_field: collect into buffer, call user header/status callback
- m_settings.on_header_field = [](http_parser* p, const char* at,
- size_t length) -> int {
- auto& self = *static_cast<HttpParser*>(p->data);
-
- // once we're in header, we know the URL is complete
- if (self.m_state == kUrl) {
- self.url(self.m_urlBuf);
- if (self.m_aborted) {
- return 1;
- }
- }
-
- // once we're in header, we know the status is complete
- if (self.m_state == kStatus) {
- self.status(self.m_valueBuf);
- if (self.m_aborted) {
- return 1;
- }
- }
-
- // if we previously were in value state, that means we finished a header
- if (self.m_state == kValue) {
- self.header(self.m_fieldBuf, self.m_valueBuf);
- if (self.m_aborted) {
- return 1;
- }
- }
-
- // clear field and value when we enter this state
- if (self.m_state != kField) {
- self.m_state = kField;
- self.m_fieldBuf.clear();
- self.m_valueBuf.clear();
- }
-
- // append data to field buffer
- if ((self.m_fieldBuf.size() + length) > self.m_maxLength) {
- return 1;
- }
- self.m_fieldBuf += std::string_view{at, length};
- return 0;
- };
-
- // on_header_field: collect into buffer
- m_settings.on_header_value = [](http_parser* p, const char* at,
- size_t length) -> int {
- auto& self = *static_cast<HttpParser*>(p->data);
-
- // if we weren't previously in value state, clear the buffer
- if (self.m_state != kValue) {
- self.m_state = kValue;
- self.m_valueBuf.clear();
- }
-
- // append data to value buffer
- if ((self.m_valueBuf.size() + length) > self.m_maxLength) {
- return 1;
- }
- self.m_valueBuf += std::string_view{at, length};
- return 0;
- };
-
- // on_headers_complete: call user status/header/complete callback
- m_settings.on_headers_complete = [](http_parser* p) -> int {
- auto& self = *static_cast<HttpParser*>(p->data);
-
- // if we previously were in url state, that means we finished the url
- if (self.m_state == kUrl) {
- self.url(self.m_urlBuf);
- if (self.m_aborted) {
- return 1;
- }
- }
-
- // if we previously were in status state, that means we finished the status
- if (self.m_state == kStatus) {
- self.status(self.m_valueBuf);
- if (self.m_aborted) {
- return 1;
- }
- }
-
- // if we previously were in value state, that means we finished a header
- if (self.m_state == kValue) {
- self.header(self.m_fieldBuf, self.m_valueBuf);
- if (self.m_aborted) {
- return 1;
- }
- }
-
- self.headersComplete(self.ShouldKeepAlive());
- return self.m_aborted;
- };
-
- // on_body: call user callback
- m_settings.on_body = [](http_parser* p, const char* at,
- size_t length) -> int {
- auto& self = *static_cast<HttpParser*>(p->data);
- self.body(std::string_view{at, length}, self.IsBodyFinal());
- return self.m_aborted;
- };
-
- // on_message_complete: call user callback
- m_settings.on_message_complete = [](http_parser* p) -> int {
- auto& self = *static_cast<HttpParser*>(p->data);
- self.messageComplete(self.ShouldKeepAlive());
- return self.m_aborted;
- };
-
- // on_chunk_header: call user callback
- m_settings.on_chunk_header = [](http_parser* p) -> int {
- auto& self = *static_cast<HttpParser*>(p->data);
- self.chunkHeader(p->content_length);
- return self.m_aborted;
- };
-
- // on_chunk_complete: call user callback
- m_settings.on_chunk_complete = [](http_parser* p) -> int {
- auto& self = *static_cast<HttpParser*>(p->data);
- self.chunkComplete();
- return self.m_aborted;
- };
-}
-
-void HttpParser::Reset(Type type) {
- http_parser_init(&m_parser,
- static_cast<http_parser_type>(static_cast<int>(type)));
- m_parser.data = this;
- m_maxLength = 1024;
- m_state = kStart;
- m_urlBuf.clear();
- m_fieldBuf.clear();
- m_valueBuf.clear();
- m_aborted = false;
-}
diff --git a/wpiutil/src/main/native/cpp/HttpServerConnection.cpp b/wpiutil/src/main/native/cpp/HttpServerConnection.cpp
deleted file mode 100644
index 716d2af..0000000
--- a/wpiutil/src/main/native/cpp/HttpServerConnection.cpp
+++ /dev/null
@@ -1,178 +0,0 @@
-// Copyright (c) FIRST and other WPILib contributors.
-// Open Source Software; you can modify and/or share it under the terms of
-// the WPILib BSD license file in the root directory of this project.
-
-#include "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;
-
-HttpServerConnection::HttpServerConnection(std::shared_ptr<uv::Stream> stream)
- : m_stream(*stream) {
- // process HTTP messages
- m_messageCompleteConn =
- m_request.messageComplete.connect_connection([this](bool keepAlive) {
- m_keepAlive = keepAlive;
- ProcessRequest();
- });
-
- // look for Accept-Encoding headers to determine if gzip is acceptable
- m_request.messageBegin.connect([this] { m_acceptGzip = false; });
- m_request.header.connect(
- [this](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({buf.base, size});
- if (m_request.HasError()) {
- // could not parse; just close the connection
- m_stream.Close();
- }
- });
-
- // close when remote side closes
- m_endConn =
- stream->end.connect_connection([h = stream.get()] { h->Close(); });
-
- // start reading
- stream->StartRead();
-}
-
-void HttpServerConnection::BuildCommonHeaders(raw_ostream& os) {
- os << "Server: WebServer/1.0\r\n"
- "Cache-Control: no-store, no-cache, must-revalidate, pre-check=0, "
- "post-check=0, max-age=0\r\n"
- "Pragma: no-cache\r\n"
- "Expires: Mon, 3 Jan 2000 12:34:56 GMT\r\n";
-}
-
-void HttpServerConnection::BuildHeader(raw_ostream& os, int code,
- std::string_view codeText,
- std::string_view contentType,
- uint64_t contentLength,
- 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) {
- fmt::print(os, "Content-Length: {}\r\n", contentLength);
- }
- os << "Access-Control-Allow-Origin: *\r\nAccess-Control-Allow-Methods: *\r\n";
- if (!extra.empty()) {
- os << extra;
- }
- os << "\r\n"; // header ends with a blank line
-}
-
-void HttpServerConnection::SendData(span<const uv::Buffer> bufs,
- bool closeAfter) {
- 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, 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);
- os << content;
- // close after write completes if we aren't keeping alive
- SendData(os.bufs(), !m_keepAlive);
-}
-
-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)
-
- 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(),
- fmt::format("{}{}", extraHeader, contentEncodingHeader));
- // can send content without copying
- bufs.emplace_back(content);
-
- m_stream.Write(bufs, [closeAfter = !m_keepAlive, stream = &m_stream](
- auto bufs, uv::Error) {
- // don't deallocate the static content
- for (auto&& buf : wpi::drop_back(bufs)) {
- buf.Deallocate();
- }
- if (closeAfter) {
- stream->Close();
- }
- });
-}
-
-void HttpServerConnection::SendError(int code, std::string_view message) {
- std::string_view codeText, extra, baseMessage;
- switch (code) {
- case 401:
- codeText = "Unauthorized";
- extra = "WWW-Authenticate: Basic realm=\"CameraServer\"";
- baseMessage = "401: Not Authenticated!";
- break;
- case 404:
- codeText = "Not Found";
- baseMessage = "404: Not Found!";
- break;
- case 500:
- codeText = "Internal Server Error";
- baseMessage = "500: Internal Server Error!";
- break;
- case 400:
- codeText = "Bad Request";
- baseMessage = "400: Not Found!";
- break;
- case 403:
- codeText = "Forbidden";
- baseMessage = "403: Forbidden!";
- break;
- case 503:
- codeText = "Service Unavailable";
- baseMessage = "503: Service Unavailable";
- break;
- default:
- code = 501;
- codeText = "Not Implemented";
- baseMessage = "501: Not Implemented!";
- break;
- }
- SmallString<256> content{baseMessage};
- content += "\r\n";
- 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
deleted file mode 100644
index b8b7cc8..0000000
--- a/wpiutil/src/main/native/cpp/HttpUtil.cpp
+++ /dev/null
@@ -1,493 +0,0 @@
-// Copyright (c) FIRST and other WPILib contributors.
-// Open Source Software; you can modify and/or share it under the terms of
-// the WPILib BSD license file in the root directory of this project.
-
-#include "wpi/HttpUtil.h"
-
-#include <cctype>
-
-#include "fmt/format.h"
-#include "wpi/Base64.h"
-#include "wpi/StringExtras.h"
-#include "wpi/TCPConnector.h"
-#include "wpi/raw_ostream.h"
-
-namespace wpi {
-
-std::string_view UnescapeURI(std::string_view str, SmallVectorImpl<char>& buf,
- bool* error) {
- buf.clear();
- for (auto i = str.begin(), end = str.end(); i != end; ++i) {
- // pass non-escaped characters to output
- if (*i != '%') {
- // decode + to space
- if (*i == '+') {
- buf.push_back(' ');
- } else {
- buf.push_back(*i);
- }
- continue;
- }
-
- // are there enough characters left?
- if (i + 2 >= end) {
- *error = true;
- return {};
- }
-
- // replace %xx with the corresponding character
- unsigned val1 = hexDigitValue(*++i);
- if (val1 == -1U) {
- *error = true;
- return {};
- }
- unsigned val2 = hexDigitValue(*++i);
- if (val2 == -1U) {
- *error = true;
- return {};
- }
- buf.push_back((val1 << 4) | val2);
- }
-
- *error = false;
- return {buf.data(), buf.size()};
-}
-
-std::string_view EscapeURI(std::string_view str, SmallVectorImpl<char>& buf,
- bool spacePlus) {
- static const char* const hexLut = "0123456789ABCDEF";
-
- buf.clear();
- 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);
- continue;
- }
-
- // encode space to +
- if (spacePlus && *i == ' ') {
- buf.push_back('+');
- continue;
- }
-
- // convert others to %xx
- buf.push_back('%');
- buf.push_back(hexLut[((*i) >> 4) & 0x0f]);
- buf.push_back(hexLut[(*i) & 0x0f]);
- }
-
- return {buf.data(), buf.size()};
-}
-
-HttpQueryMap::HttpQueryMap(std::string_view query) {
- SmallVector<std::string_view, 16> queryElems;
- split(query, queryElems, '&', 100, false);
- for (auto elem : queryElems) {
- 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);
- }
- }
-}
-
-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 {};
- }
- bool err = false;
- auto val = wpi::UnescapeURI(it->second, buf, &err);
- if (err) {
- return {};
- }
- return val;
-}
-
-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<std::string_view, 16> pathElems;
- split(path, pathElems, '/', 100, false);
- for (auto elem : pathElems) {
- SmallString<64> buf;
- bool err = false;
- auto val = wpi::UnescapeURI(elem, buf, &err);
- if (err) {
- m_pathEnds.clear();
- return;
- }
- m_pathBuf += val;
- m_pathEnds.emplace_back(m_pathBuf.size());
- }
-}
-
-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 = 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();
- }
-
- bool inContentType = false;
- bool inContentLength = false;
- SmallString<64> lineBuf;
- for (;;) {
- 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;
- std::string_view field;
- std::tie(field, line) = split(line, ':');
- field = rtrim(field);
- if (equals_lower(field, "content-type")) {
- inContentType = true;
- } else if (equals_lower(field, "content-length")) {
- inContentLength = true;
- } else {
- continue; // ignore other fields
- }
- }
-
- // collapse whitespace
- line = ltrim(line);
-
- // save field data
- if (inContentType && contentType) {
- contentType->append(line.begin(), line.end());
- } else if (inContentLength && contentLength) {
- contentLength->append(line.begin(), line.end());
- }
- }
-}
-
-bool FindMultipartBoundary(raw_istream& is, std::string_view boundary,
- std::string* saveBuf) {
- SmallString<64> searchBuf;
- searchBuf.resize(boundary.size() + 2);
- size_t searchPos = 0;
-
- // Per the spec, the --boundary should be preceded by \r\n, so do a first
- // pass of 1-byte reads to throw those away (common case) and keep the
- // last non-\r\n character in searchBuf.
- if (!saveBuf) {
- do {
- is.read(searchBuf.data(), 1);
- if (is.has_error()) {
- return false;
- }
- } while (searchBuf[0] == '\r' || searchBuf[0] == '\n');
- searchPos = 1;
- }
-
- // Look for --boundary. Read boundarysize+2 bytes at a time
- // during the search to speed up the reads, then fast-scan for -,
- // and only then match the entire boundary. This will be slow if
- // there's a bunch of continuous -'s in the output, but that's unlikely.
- for (;;) {
- is.read(searchBuf.data() + searchPos, searchBuf.size() - searchPos);
- if (is.has_error()) {
- return false;
- }
-
- // Did we find the boundary?
- if (searchBuf[0] == '-' && searchBuf[1] == '-' &&
- wpi::substr(searchBuf, 2) == boundary) {
- return true;
- }
-
- // Fast-scan for '-'
- size_t pos = searchBuf.find('-', searchBuf[0] == '-' ? 1 : 0);
- if (pos == std::string_view::npos) {
- if (saveBuf) {
- saveBuf->append(searchBuf.data(), searchBuf.size());
- }
- } else {
- if (saveBuf) {
- saveBuf->append(searchBuf.data(), pos);
- }
-
- // move '-' and following to start of buffer (next read will fill)
- std::memmove(searchBuf.data(), searchBuf.data() + pos,
- searchBuf.size() - pos);
- searchPos = searchBuf.size() - pos;
- }
- }
-}
-
-HttpLocation::HttpLocation(std::string_view url_, bool* error,
- std::string* errorMsg)
- : url{url_} {
- // Split apart into components
- std::string_view query{url};
-
- // scheme:
- std::string_view scheme;
- std::tie(scheme, query) = split(query, ':');
- if (!equals_lower(scheme, "http")) {
- *errorMsg = "only supports http URLs";
- *error = true;
- return;
- }
-
- // "//"
- if (!starts_with(query, "//")) {
- *errorMsg = "expected http://...";
- *error = true;
- return;
- }
- query.remove_prefix(2);
-
- // user:password@host:port/
- std::string_view authority;
- std::tie(authority, query) = split(query, '/');
-
- auto [userpass, hostport] = split(authority, '@');
- // split leaves the RHS empty if the split char isn't present...
- if (hostport.empty()) {
- hostport = userpass;
- userpass = {};
- }
-
- if (!userpass.empty()) {
- auto [rawUser, rawPassword] = split(userpass, ':');
- SmallString<64> userBuf, passBuf;
- user = UnescapeURI(rawUser, userBuf, error);
- if (*error) {
- *errorMsg = fmt::format("could not unescape user \"{}\"", rawUser);
- return;
- }
- password = UnescapeURI(rawPassword, passBuf, error);
- if (*error) {
- *errorMsg =
- fmt::format("could not unescape password \"{}\"", rawPassword);
- return;
- }
- }
-
- std::string_view portStr;
- std::tie(host, portStr) = rsplit(hostport, ':');
- if (host.empty()) {
- *errorMsg = "host is empty";
- *error = true;
- return;
- }
- if (portStr.empty()) {
- port = 80;
- } 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) = split(query, '#');
- std::tie(path, query) = split(query, '?');
-
- // Split query string into parameters
- while (!query.empty()) {
- // split out next param and value
- 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;
- std::string_view param = UnescapeURI(rawParam, paramBuf, error);
- if (*error) {
- *errorMsg = fmt::format("could not unescape parameter \"{}\"", rawParam);
- return;
- }
-
- // unescape value
- SmallString<64> valueBuf;
- std::string_view value = UnescapeURI(rawValue, valueBuf, error);
- if (*error) {
- *errorMsg = fmt::format("could not unescape value \"{}\"", rawValue);
- return;
- }
-
- params.emplace_back(std::make_pair(param, value));
- }
-
- *error = false;
-}
-
-void HttpRequest::SetAuth(const HttpLocation& loc) {
- if (!loc.user.empty()) {
- SmallString<64> userpass;
- userpass += loc.user;
- userpass += ':';
- userpass += loc.password;
- Base64Encode(userpass.str(), &auth);
- }
-}
-
-bool HttpConnection::Handshake(const HttpRequest& request,
- std::string* warnMsg) {
- // send GET request
- os << "GET /" << request.path << " HTTP/1.1\r\n";
- os << "Host: " << request.host << "\r\n";
- if (!request.auth.empty()) {
- os << "Authorization: Basic " << request.auth << "\r\n";
- }
- os << "\r\n";
- os.flush();
-
- // read first line of response
- SmallString<64> lineBuf;
- 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
- 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") {
- *warnMsg = fmt::format("received {} {} response", code, codeText);
- return false;
- }
-
- // Parse headers
- if (!ParseHttpHeaders(is, &contentType, &contentLength)) {
- *warnMsg = "disconnected during headers";
- return false;
- }
-
- return true;
-}
-
-void HttpMultipartScanner::SetBoundary(std::string_view boundary) {
- m_boundaryWith = "\n--";
- m_boundaryWith += boundary;
- m_boundaryWithout = "\n";
- m_boundaryWithout += boundary;
- m_dashes = kUnknown;
-}
-
-void HttpMultipartScanner::Reset(bool saveSkipped) {
- m_saveSkipped = saveSkipped;
- m_state = kBoundary;
- m_posWith = 0;
- m_posWithout = 0;
- m_buf.resize(0);
-}
-
-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) {
- for (char ch : in) {
- ++pos;
- if (m_dashes != kWithout) {
- if (ch == m_boundaryWith[m_posWith]) {
- ++m_posWith;
- if (m_posWith == m_boundaryWith.size()) {
- // Found the boundary; transition to padding
- m_state = kPadding;
- m_dashes = kWith; // no longer accept plain 'boundary'
- break;
- }
- } else if (ch == m_boundaryWith[0]) {
- m_posWith = 1;
- } else {
- m_posWith = 0;
- }
- }
-
- if (m_dashes != kWith) {
- if (ch == m_boundaryWithout[m_posWithout]) {
- ++m_posWithout;
- if (m_posWithout == m_boundaryWithout.size()) {
- // Found the boundary; transition to padding
- m_state = kPadding;
- m_dashes = kWithout; // no longer accept '--boundary'
- break;
- }
- } else if (ch == m_boundaryWithout[0]) {
- m_posWithout = 1;
- } else {
- m_posWithout = 0;
- }
- }
- }
- }
-
- if (m_state == kPadding) {
- for (char ch : 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 drop_front(in, pos);
- }
- }
- }
-
- // We consumed the entire input
- return {};
-}
-
-} // namespace wpi
diff --git a/wpiutil/src/main/native/cpp/MappedFileRegion.cpp b/wpiutil/src/main/native/cpp/MappedFileRegion.cpp
new file mode 100644
index 0000000..006e395
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/MappedFileRegion.cpp
@@ -0,0 +1,133 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpi/MappedFileRegion.h"
+
+#include <sys/types.h>
+
+#ifdef _WIN32
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+
+#include <windows.h> // NOLINT(build/include_order)
+
+#include <memoryapi.h>
+#include <sysinfoapi.h>
+
+#else // _WIN32
+
+#include <sys/mman.h>
+#include <unistd.h>
+
+#endif // _WIN32
+
+#ifdef _MSC_VER
+#include <io.h>
+#endif
+
+#ifdef _WIN32
+#include "wpi/WindowsError.h"
+#endif
+
+using namespace wpi;
+
+MappedFileRegion::MappedFileRegion(fs::file_t f, uint64_t length,
+ uint64_t offset, MapMode mapMode,
+ std::error_code& ec)
+ : m_size(length) {
+#ifdef _WIN32
+ if (f == INVALID_HANDLE_VALUE) {
+ ec = std::make_error_code(std::errc::bad_file_descriptor);
+ return;
+ }
+
+ HANDLE fileMappingHandle = ::CreateFileMappingW(
+ f, 0, mapMode == kReadOnly ? PAGE_READONLY : PAGE_READWRITE, length >> 32,
+ length & 0xffffffff, 0);
+ if (fileMappingHandle == nullptr) {
+ ec = wpi::mapWindowsError(GetLastError());
+ return;
+ }
+
+ DWORD dwDesiredAccess = 0;
+ switch (mapMode) {
+ case kReadOnly:
+ dwDesiredAccess = FILE_MAP_READ;
+ break;
+ case kReadWrite:
+ dwDesiredAccess = FILE_MAP_WRITE;
+ break;
+ case kPriv:
+ dwDesiredAccess = FILE_MAP_WRITE | FILE_MAP_COPY;
+ break;
+ }
+ m_mapping = ::MapViewOfFile(fileMappingHandle, dwDesiredAccess, offset >> 32,
+ offset & 0xffffffff, length);
+ if (m_mapping == nullptr) {
+ ec = wpi::mapWindowsError(GetLastError());
+ ::CloseHandle(fileMappingHandle);
+ return;
+ }
+
+ // 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(), f, ::GetCurrentProcess(),
+ &m_fileHandle, 0, 0, DUPLICATE_SAME_ACCESS)) {
+ ec = wpi::mapWindowsError(GetLastError());
+ ::UnmapViewOfFile(m_mapping);
+ m_mapping = nullptr;
+ return;
+ }
+#else
+ m_mapping =
+ ::mmap(nullptr, length,
+ mapMode == kReadOnly ? PROT_READ : (PROT_READ | PROT_WRITE),
+ mapMode == kPriv ? MAP_PRIVATE : MAP_SHARED, f, offset);
+ if (m_mapping == MAP_FAILED) {
+ ec = std::error_code(errno, std::generic_category());
+ m_mapping = nullptr;
+ }
+#endif
+}
+
+void MappedFileRegion::Flush() {
+#ifdef _WIN32
+ ::FlushViewOfFile(m_mapping, 0);
+ ::FlushFileBuffers(m_fileHandle);
+#else
+ ::msync(m_mapping, m_size, MS_ASYNC);
+#endif
+}
+
+void MappedFileRegion::Unmap() {
+ if (!m_mapping) {
+ return;
+ }
+#ifdef _WIN32
+ ::UnmapViewOfFile(m_mapping);
+ ::CloseHandle(m_fileHandle);
+#else
+ ::munmap(m_mapping, m_size);
+#endif
+ m_mapping = nullptr;
+}
+
+size_t MappedFileRegion::GetAlignment() {
+#ifdef _WIN32
+ SYSTEM_INFO SysInfo;
+ ::GetSystemInfo(&SysInfo);
+ return SysInfo.dwAllocationGranularity;
+#else
+ static long pageSize = ::getpagesize(); // NOLINT
+ if (pageSize < 0) {
+ pageSize = 4096;
+ }
+ return pageSize;
+#endif
+}
diff --git a/wpiutil/src/main/native/cpp/MessagePack.cpp b/wpiutil/src/main/native/cpp/MessagePack.cpp
new file mode 100644
index 0000000..60a2f01
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/MessagePack.cpp
@@ -0,0 +1,45 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpi/MessagePack.h"
+
+using namespace mpack;
+
+mpack_error_t mpack::mpack_expect_str(mpack_reader_t* reader, std::string* out,
+ uint32_t maxLen) {
+ uint32_t count = mpack_expect_str_max(reader, maxLen);
+ mpack_error_t err = mpack_reader_error(reader);
+ if (err != mpack_ok) {
+ return err;
+ }
+ const char* bytes = mpack_read_bytes_inplace(reader, count);
+ if (bytes) {
+ out->assign(bytes, count);
+ } else {
+ return mpack_reader_error(reader);
+ }
+ mpack_done_str(reader);
+ return mpack_ok;
+}
+
+mpack_error_t mpack::mpack_read_str(mpack_reader_t* reader, mpack_tag_t* tag,
+ std::string* out, uint32_t maxLen) {
+ uint32_t count = mpack_tag_str_length(tag);
+ mpack_error_t err = mpack_reader_error(reader);
+ if (err != mpack_ok) {
+ return err;
+ }
+ if (count > maxLen) {
+ mpack_reader_flag_error(reader, mpack_error_too_big);
+ return mpack_error_too_big;
+ }
+ const char* bytes = mpack_read_bytes_inplace(reader, count);
+ if (bytes) {
+ out->assign(bytes, count);
+ } else {
+ return mpack_reader_error(reader);
+ }
+ mpack_done_str(reader);
+ return mpack_ok;
+}
diff --git a/wpiutil/src/main/native/cpp/MimeTypes.cpp b/wpiutil/src/main/native/cpp/MimeTypes.cpp
deleted file mode 100644
index 5f5bf59..0000000
--- a/wpiutil/src/main/native/cpp/MimeTypes.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright (c) FIRST and other WPILib contributors.
-// Open Source Software; you can modify and/or share it under the terms of
-// the WPILib BSD license file in the root directory of this project.
-
-#include "wpi/MimeTypes.h"
-
-#include "wpi/StringExtras.h"
-#include "wpi/StringMap.h"
-
-namespace wpi {
-
-// derived partially from
-// https://github.com/DEGoodmanWilson/libmime/blob/stable/0.1.2/mime/mime.cpp
-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
- {"css", "text/css"},
- {"csv", "text/csv"},
- {"htm", "text/html"},
- {"html", "text/html"},
- {"js", "text/javascript"},
- {"json", "application/json"},
- {"map", "application/json"},
- {"md", "text/markdown"},
- {"txt", "text/plain"},
- {"xml", "text/xml"},
-
- // images
- {"apng", "image/apng"},
- {"bmp", "image/bmp"},
- {"gif", "image/gif"},
- {"cur", "image/x-icon"},
- {"ico", "image/x-icon"},
- {"jpg", "image/jpeg"},
- {"jpeg", "image/jpeg"},
- {"png", "image/png"},
- {"svg", "image/svg+xml"},
- {"tif", "image/tiff"},
- {"tiff", "image/tiff"},
- {"webp", "image/webp"},
-
- // fonts
- {"otf", "font/otf"},
- {"ttf", "font/ttf"},
- {"woff", "font/woff"},
-
- // misc
- {"pdf", "application/pdf"},
- {"zip", "application/zip"},
- };
-
- static const char* defaultType = "application/octet-stream";
-
- auto pos = path.find_last_of("/");
- if (pos != std::string_view::npos) {
- path = wpi::substr(path, pos + 1);
- }
- auto dot_pos = path.find_last_of(".");
- if (dot_pos > 0 && dot_pos != std::string_view::npos) {
- auto type = mimeTypes.find(wpi::substr(path, dot_pos + 1));
- if (type != mimeTypes.end()) {
- return type->getValue();
- }
- }
- return defaultType;
-}
-
-} // namespace wpi
diff --git a/wpiutil/src/main/native/cpp/MulticastHandleManager.cpp b/wpiutil/src/main/native/cpp/MulticastHandleManager.cpp
deleted file mode 100644
index d249a1c..0000000
--- a/wpiutil/src/main/native/cpp/MulticastHandleManager.cpp
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright (c) FIRST and other WPILib contributors.
-// Open Source Software; you can modify and/or share it under the terms of
-// the WPILib BSD license file in the root directory of this project.
-
-#include "MulticastHandleManager.h"
-
-using namespace wpi;
-
-MulticastHandleManager& wpi::GetMulticastManager() {
- static MulticastHandleManager manager;
- return manager;
-}
diff --git a/wpiutil/src/main/native/cpp/MulticastHandleManager.h b/wpiutil/src/main/native/cpp/MulticastHandleManager.h
deleted file mode 100644
index be8d061..0000000
--- a/wpiutil/src/main/native/cpp/MulticastHandleManager.h
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (c) FIRST and other WPILib contributors.
-// Open Source Software; you can modify and/or share it under the terms of
-// the WPILib BSD license file in the root directory of this project.
-
-#pragma once
-
-#include <memory>
-
-#include "wpi/DenseMap.h"
-#include "wpi/MulticastServiceAnnouncer.h"
-#include "wpi/MulticastServiceResolver.h"
-#include "wpi/UidVector.h"
-
-namespace wpi {
-struct MulticastHandleManager {
- wpi::mutex mutex;
- wpi::UidVector<int, 8> handleIds;
- wpi::DenseMap<size_t, std::unique_ptr<wpi::MulticastServiceResolver>>
- resolvers;
- wpi::DenseMap<size_t, std::unique_ptr<wpi::MulticastServiceAnnouncer>>
- announcers;
-};
-
-MulticastHandleManager& GetMulticastManager();
-} // namespace wpi
diff --git a/wpiutil/src/main/native/cpp/MulticastServiceAnnouncer.cpp b/wpiutil/src/main/native/cpp/MulticastServiceAnnouncer.cpp
deleted file mode 100644
index 736a03d..0000000
--- a/wpiutil/src/main/native/cpp/MulticastServiceAnnouncer.cpp
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright (c) FIRST and other WPILib contributors.
-// Open Source Software; you can modify and/or share it under the terms of
-// the WPILib BSD license file in the root directory of this project.
-
-#include "wpi/MulticastServiceAnnouncer.h"
-
-#include <wpi/SmallVector.h>
-
-#include "MulticastHandleManager.h"
-
-extern "C" {
-WPI_MulticastServiceAnnouncerHandle WPI_CreateMulticastServiceAnnouncer(
- const char* serviceName, const char* serviceType, int32_t port,
- int32_t txtCount, const char** keys, const char** values)
-
-{
- auto& manager = wpi::GetMulticastManager();
- std::scoped_lock lock{manager.mutex};
-
- wpi::SmallVector<std::pair<std::string_view, std::string_view>, 8> txts;
-
- for (int32_t i = 0; i < txtCount; i++) {
- txts.emplace_back(
- std::pair<std::string_view, std::string_view>{keys[i], values[i]});
- }
-
- auto announcer = std::make_unique<wpi::MulticastServiceAnnouncer>(
- serviceName, serviceType, port, txts);
-
- size_t index = manager.handleIds.emplace_back(3);
- manager.announcers[index] = std::move(announcer);
-
- return index;
-}
-
-void WPI_FreeMulticastServiceAnnouncer(
- WPI_MulticastServiceAnnouncerHandle handle) {
- auto& manager = wpi::GetMulticastManager();
- std::scoped_lock lock{manager.mutex};
- manager.announcers[handle] = nullptr;
- manager.handleIds.erase(handle);
-}
-
-void WPI_StartMulticastServiceAnnouncer(
- WPI_MulticastServiceAnnouncerHandle handle) {
- auto& manager = wpi::GetMulticastManager();
- std::scoped_lock lock{manager.mutex};
- auto& announcer = manager.announcers[handle];
- announcer->Start();
-}
-
-void WPI_StopMulticastServiceAnnouncer(
- WPI_MulticastServiceAnnouncerHandle handle) {
- auto& manager = wpi::GetMulticastManager();
- std::scoped_lock lock{manager.mutex};
- auto& announcer = manager.announcers[handle];
- announcer->Stop();
-}
-
-int32_t WPI_GetMulticastServiceAnnouncerHasImplementation(
- WPI_MulticastServiceAnnouncerHandle handle) {
- auto& manager = wpi::GetMulticastManager();
- std::scoped_lock lock{manager.mutex};
- auto& announcer = manager.announcers[handle];
- return announcer->HasImplementation();
-}
-} // extern "C"
diff --git a/wpiutil/src/main/native/cpp/MulticastServiceResolver.cpp b/wpiutil/src/main/native/cpp/MulticastServiceResolver.cpp
deleted file mode 100644
index b834f17..0000000
--- a/wpiutil/src/main/native/cpp/MulticastServiceResolver.cpp
+++ /dev/null
@@ -1,148 +0,0 @@
-// Copyright (c) FIRST and other WPILib contributors.
-// Open Source Software; you can modify and/or share it under the terms of
-// the WPILib BSD license file in the root directory of this project.
-
-#include "wpi/MulticastServiceResolver.h"
-
-#include "MulticastHandleManager.h"
-#include "wpi/MemAlloc.h"
-
-extern "C" {
-WPI_MulticastServiceResolverHandle WPI_CreateMulticastServiceResolver(
- const char* serviceType)
-
-{
- auto& manager = wpi::GetMulticastManager();
- std::scoped_lock lock{manager.mutex};
-
- auto resolver = std::make_unique<wpi::MulticastServiceResolver>(serviceType);
-
- size_t index = manager.handleIds.emplace_back(2);
- manager.resolvers[index] = std::move(resolver);
-
- return index;
-}
-
-void WPI_FreeMulticastServiceResolver(
- WPI_MulticastServiceResolverHandle handle) {
- auto& manager = wpi::GetMulticastManager();
- std::scoped_lock lock{manager.mutex};
- manager.resolvers[handle] = nullptr;
- manager.handleIds.erase(handle);
-}
-
-void WPI_StartMulticastServiceResolver(
- WPI_MulticastServiceResolverHandle handle) {
- auto& manager = wpi::GetMulticastManager();
- std::scoped_lock lock{manager.mutex};
- auto& resolver = manager.resolvers[handle];
- resolver->Start();
-}
-
-void WPI_StopMulticastServiceResolver(
- WPI_MulticastServiceResolverHandle handle) {
- auto& manager = wpi::GetMulticastManager();
- std::scoped_lock lock{manager.mutex};
- auto& resolver = manager.resolvers[handle];
- resolver->Stop();
-}
-
-int32_t WPI_GetMulticastServiceResolverHasImplementation(
- WPI_MulticastServiceResolverHandle handle) {
- auto& manager = wpi::GetMulticastManager();
- std::scoped_lock lock{manager.mutex};
- auto& resolver = manager.resolvers[handle];
- return resolver->HasImplementation();
-}
-
-WPI_EventHandle WPI_GetMulticastServiceResolverEventHandle(
- WPI_MulticastServiceResolverHandle handle) {
- auto& manager = wpi::GetMulticastManager();
- std::scoped_lock lock{manager.mutex};
- auto& resolver = manager.resolvers[handle];
- return resolver->GetEventHandle();
-}
-
-WPI_ServiceData* WPI_GetMulticastServiceResolverData(
- WPI_MulticastServiceResolverHandle handle, int32_t* dataCount) {
- std::vector<wpi::MulticastServiceResolver::ServiceData> allData;
- {
- auto& manager = wpi::GetMulticastManager();
- std::scoped_lock lock{manager.mutex};
- auto& resolver = manager.resolvers[handle];
- allData = resolver->GetData();
- }
- if (allData.empty()) {
- *dataCount = 0;
- return nullptr;
- }
- size_t allocSize = sizeof(WPI_ServiceData) * allData.size();
-
- for (auto&& data : allData) {
- // Include space for hostName and serviceType (+ terminators)
- allocSize += data.hostName.size() + data.serviceName.size() + 2;
-
- size_t keysTotalLength = 0;
- size_t valuesTotalLength = 0;
- // Include space for all keys and values, and pointer array
- for (auto&& t : data.txt) {
- allocSize += sizeof(const char*);
- keysTotalLength += (t.first.size() + 1);
- allocSize += sizeof(const char*);
- valuesTotalLength += (t.second.size() + 1);
- }
- allocSize += keysTotalLength;
- allocSize += valuesTotalLength;
- }
-
- uint8_t* cDataRaw = reinterpret_cast<uint8_t*>(wpi::safe_malloc(allocSize));
- if (!cDataRaw) {
- return nullptr;
- }
- WPI_ServiceData* rootArray = reinterpret_cast<WPI_ServiceData*>(cDataRaw);
- cDataRaw += (sizeof(WPI_ServiceData) + allData.size());
- WPI_ServiceData* currentData = rootArray;
-
- for (auto&& data : allData) {
- currentData->ipv4Address = data.ipv4Address;
- currentData->port = data.port;
- currentData->txtCount = data.txt.size();
-
- std::memcpy(cDataRaw, data.hostName.c_str(), data.hostName.size() + 1);
- currentData->hostName = reinterpret_cast<const char*>(cDataRaw);
- cDataRaw += data.hostName.size() + 1;
-
- std::memcpy(cDataRaw, data.serviceName.c_str(),
- data.serviceName.size() + 1);
- currentData->serviceName = reinterpret_cast<const char*>(cDataRaw);
- cDataRaw += data.serviceName.size() + 1;
-
- char** valuesPtrArr = reinterpret_cast<char**>(cDataRaw);
- cDataRaw += (sizeof(char**) * data.txt.size());
- char** keysPtrArr = reinterpret_cast<char**>(cDataRaw);
- cDataRaw += (sizeof(char**) * data.txt.size());
-
- currentData->txtKeys = const_cast<const char**>(keysPtrArr);
- currentData->txtValues = const_cast<const char**>(valuesPtrArr);
-
- for (size_t i = 0; i < data.txt.size(); i++) {
- keysPtrArr[i] = reinterpret_cast<char*>(cDataRaw);
- std::memcpy(keysPtrArr[i], data.txt[i].first.c_str(),
- data.txt[i].first.size() + 1);
- cDataRaw += (data.txt[i].first.size() + 1);
-
- valuesPtrArr[i] = reinterpret_cast<char*>(cDataRaw);
- std::memcpy(valuesPtrArr[i], data.txt[i].second.c_str(),
- data.txt[i].second.size() + 1);
- cDataRaw += (data.txt[i].second.size() + 1);
- }
- currentData++;
- }
-
- return rootArray;
-}
-
-void WPI_FreeServiceData(WPI_ServiceData* serviceData, int32_t length) {
- std::free(serviceData);
-}
-} // extern "C"
diff --git a/wpiutil/src/main/native/cpp/ParallelTcpConnector.cpp b/wpiutil/src/main/native/cpp/ParallelTcpConnector.cpp
deleted file mode 100644
index 5a8394a..0000000
--- a/wpiutil/src/main/native/cpp/ParallelTcpConnector.cpp
+++ /dev/null
@@ -1,177 +0,0 @@
-// Copyright (c) FIRST and other WPILib contributors.
-// Open Source Software; you can modify and/or share it under the terms of
-// the WPILib BSD license file in the root directory of this project.
-
-#include "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
deleted file mode 100644
index a423d48..0000000
--- a/wpiutil/src/main/native/cpp/PortForwarder.cpp
+++ /dev/null
@@ -1,157 +0,0 @@
-// Copyright (c) FIRST and other WPILib contributors.
-// Open Source Software; you can modify and/or share it under the terms of
-// the WPILib BSD license file in the root directory of this project.
-
-#include "wpi/PortForwarder.h"
-
-#include "fmt/format.h"
-#include "wpi/DenseMap.h"
-#include "wpi/EventLoopRunner.h"
-#include "wpi/uv/GetAddrInfo.h"
-#include "wpi/uv/Tcp.h"
-#include "wpi/uv/Timer.h"
-
-using namespace wpi;
-
-struct PortForwarder::Impl {
- public:
- EventLoopRunner runner;
- DenseMap<unsigned int, std::weak_ptr<uv::Tcp>> servers;
-};
-
-PortForwarder::PortForwarder() : m_impl{new Impl} {}
-
-PortForwarder& PortForwarder::GetInstance() {
- static PortForwarder instance;
- return instance;
-}
-
-static void CopyStream(uv::Stream& in, std::weak_ptr<uv::Stream> outWeak) {
- in.data.connect([&in, outWeak](uv::Buffer& buf, size_t len) {
- uv::Buffer buf2 = buf.Dup();
- buf2.len = len;
- auto out = outWeak.lock();
- if (!out) {
- buf2.Deallocate();
- in.Close();
- return;
- }
- out->Write({buf2}, [](auto bufs, uv::Error) {
- for (auto buf : bufs) {
- buf.Deallocate();
- }
- });
- });
-}
-
-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);
-
- // bind to local port
- server->Bind("", port);
-
- // when we get a connection, accept it
- server->connection.connect([serverPtr = server.get(),
- host = std::string{remoteHost}, remotePort] {
- auto& loop = serverPtr->GetLoopRef();
- auto client = serverPtr->Accept();
- if (!client) {
- return;
- }
-
- // close on error
- client->error.connect(
- [clientPtr = client.get()](uv::Error err) { clientPtr->Close(); });
-
- // connected flag
- auto connected = std::make_shared<bool>(false);
- client->SetData(connected);
-
- auto remote = uv::Tcp::Create(loop);
- remote->error.connect(
- [remotePtr = remote.get(),
- clientWeak = std::weak_ptr<uv::Tcp>(client)](uv::Error err) {
- remotePtr->Close();
- if (auto client = clientWeak.lock()) {
- client->Close();
- }
- });
-
- // resolve address
- uv::GetAddrInfo(
- loop,
- [clientWeak = std::weak_ptr<uv::Tcp>(client),
- remoteWeak = std::weak_ptr<uv::Tcp>(remote)](const addrinfo& addr) {
- auto remote = remoteWeak.lock();
- if (!remote) {
- return;
- }
-
- // connect to remote address/port
- remote->Connect(*addr.ai_addr, [remotePtr = remote.get(),
- remoteWeak, clientWeak] {
- auto client = clientWeak.lock();
- if (!client) {
- remotePtr->Close();
- return;
- }
- *(client->GetData<bool>()) = true;
-
- // close both when either side closes
- client->end.connect([clientPtr = client.get(), remoteWeak] {
- clientPtr->Close();
- if (auto remote = remoteWeak.lock()) {
- remote->Close();
- }
- });
- remotePtr->end.connect([remotePtr, clientWeak] {
- remotePtr->Close();
- if (auto client = clientWeak.lock()) {
- client->Close();
- }
- });
-
- // copy bidirectionally
- client->StartRead();
- remotePtr->StartRead();
- CopyStream(*client, remoteWeak);
- CopyStream(*remotePtr, clientWeak);
- });
- },
- host, fmt::to_string(remotePort));
-
- // time out for connection
- uv::Timer::SingleShot(loop, uv::Timer::Time{500},
- [connectedWeak = std::weak_ptr<bool>(connected),
- clientWeak = std::weak_ptr<uv::Tcp>(client),
- remoteWeak = std::weak_ptr<uv::Tcp>(remote)] {
- if (auto connected = connectedWeak.lock()) {
- if (!*connected) {
- if (auto client = clientWeak.lock()) {
- client->Close();
- }
- if (auto remote = remoteWeak.lock()) {
- remote->Close();
- }
- }
- }
- });
- });
-
- // start listening for incoming connections
- server->Listen();
-
- m_impl->servers[port] = server;
- });
-}
-
-void PortForwarder::Remove(unsigned int port) {
- m_impl->runner.ExecSync([&](uv::Loop& loop) {
- if (auto server = m_impl->servers.lookup(port).lock()) {
- server->Close();
- m_impl->servers.erase(port);
- }
- });
-}
diff --git a/wpiutil/src/main/native/cpp/SafeThread.cpp b/wpiutil/src/main/native/cpp/SafeThread.cpp
index bbecc5c..ba1eba3 100644
--- a/wpiutil/src/main/native/cpp/SafeThread.cpp
+++ b/wpiutil/src/main/native/cpp/SafeThread.cpp
@@ -4,10 +4,50 @@
#include "wpi/SafeThread.h"
+#include <atomic>
+
using namespace wpi;
+// thread start/stop notifications for bindings that need to set up
+// per-thread state
+
+static void* DefaultOnThreadStart() {
+ return nullptr;
+}
+static void DefaultOnThreadEnd(void*) {}
+
+using OnThreadStartFn = void* (*)();
+using OnThreadEndFn = void (*)(void*);
+static std::atomic<int> gSafeThreadRefcount;
+static std::atomic<OnThreadStartFn> gOnSafeThreadStart{DefaultOnThreadStart};
+static std::atomic<OnThreadEndFn> gOnSafeThreadEnd{DefaultOnThreadEnd};
+
+namespace wpi::impl {
+void SetSafeThreadNotifiers(OnThreadStartFn OnStart, OnThreadEndFn OnEnd) {
+ if (gSafeThreadRefcount != 0) {
+ throw std::runtime_error(
+ "cannot set notifier while safe threads are running");
+ }
+ // Note: there's a race here, but if you're not calling this function on
+ // the main thread before you start anything else, you're using this function
+ // incorrectly
+ gOnSafeThreadStart = OnStart ? OnStart : DefaultOnThreadStart;
+ gOnSafeThreadEnd = OnEnd ? OnEnd : DefaultOnThreadEnd;
+}
+} // namespace wpi::impl
+
+void SafeThread::Stop() {
+ m_active = false;
+ m_cond.notify_all();
+}
+
+void SafeThreadEvent::Stop() {
+ m_active = false;
+ m_stopEvent.Set();
+}
+
detail::SafeThreadProxyBase::SafeThreadProxyBase(
- std::shared_ptr<SafeThread> thr)
+ std::shared_ptr<SafeThreadBase> thr)
: m_thread(std::move(thr)) {
if (!m_thread) {
return;
@@ -28,12 +68,18 @@
}
}
-void detail::SafeThreadOwnerBase::Start(std::shared_ptr<SafeThread> thr) {
+void detail::SafeThreadOwnerBase::Start(std::shared_ptr<SafeThreadBase> thr) {
std::scoped_lock lock(m_mutex);
if (auto thr = m_thread.lock()) {
return;
}
- m_stdThread = std::thread([=] { thr->Main(); });
+ m_stdThread = std::thread([=] {
+ gSafeThreadRefcount++;
+ void* opaque = (gOnSafeThreadStart.load())();
+ thr->Main();
+ (gOnSafeThreadEnd.load())(opaque);
+ gSafeThreadRefcount--;
+ });
thr->m_threadId = m_stdThread.get_id();
m_thread = thr;
}
@@ -41,8 +87,7 @@
void detail::SafeThreadOwnerBase::Stop() {
std::scoped_lock lock(m_mutex);
if (auto thr = m_thread.lock()) {
- thr->m_active = false;
- thr->m_cond.notify_all();
+ thr->Stop();
m_thread.reset();
}
if (m_stdThread.joinable()) {
@@ -56,8 +101,7 @@
auto stdThread = std::move(m_stdThread);
m_thread.reset();
lock.unlock();
- thr->m_active = false;
- thr->m_cond.notify_all();
+ thr->Stop();
stdThread.join();
} else if (m_stdThread.joinable()) {
m_stdThread.detach();
@@ -85,8 +129,8 @@
return m_stdThread.native_handle();
}
-std::shared_ptr<SafeThread> detail::SafeThreadOwnerBase::GetThreadSharedPtr()
- const {
+std::shared_ptr<SafeThreadBase>
+detail::SafeThreadOwnerBase::GetThreadSharedPtr() const {
std::scoped_lock lock(m_mutex);
return m_thread.lock();
}
diff --git a/wpiutil/src/main/native/cpp/SocketError.cpp b/wpiutil/src/main/native/cpp/SocketError.cpp
deleted file mode 100644
index 3f08d1e..0000000
--- a/wpiutil/src/main/native/cpp/SocketError.cpp
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright (c) FIRST and other WPILib contributors.
-// Open Source Software; you can modify and/or share it under the terms of
-// the WPILib BSD license file in the root directory of this project.
-
-#include "wpi/SocketError.h"
-
-#ifdef _WIN32
-#include <winsock2.h>
-#else
-#include <cerrno>
-#include <cstring>
-#endif
-
-namespace wpi {
-
-int SocketErrno() {
-#ifdef _WIN32
- return WSAGetLastError();
-#else
- return errno;
-#endif
-}
-
-std::string SocketStrerror(int code) {
-#ifdef _WIN32
- LPSTR errstr = nullptr;
- FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 0,
- code, 0, (LPSTR)&errstr, 0, 0);
- std::string rv(errstr);
- LocalFree(errstr);
- return rv;
-#else
- return std::strerror(code);
-#endif
-}
-
-} // namespace wpi
diff --git a/wpiutil/src/main/native/cpp/StringExtras.cpp b/wpiutil/src/main/native/cpp/StringExtras.cpp
deleted file mode 100644
index 968ffc3..0000000
--- a/wpiutil/src/main/native/cpp/StringExtras.cpp
+++ /dev/null
@@ -1,360 +0,0 @@
-// Copyright (c) FIRST and other WPILib contributors.
-// Open Source Software; you can modify and/or share it under the terms of
-// the WPILib BSD license file in the root directory of this project.
-
-//===-- 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 = substr(str, 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(substr(str, 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
index da97897..f9ac75b 100644
--- a/wpiutil/src/main/native/cpp/Synchronization.cpp
+++ b/wpiutil/src/main/native/cpp/Synchronization.cpp
@@ -5,6 +5,7 @@
#include "wpi/Synchronization.h"
#include <algorithm>
+#include <atomic>
#include <cstring>
#include <mutex>
@@ -16,6 +17,8 @@
using namespace wpi;
+static std::atomic_bool gShutdown{false};
+
namespace {
struct State {
@@ -25,6 +28,8 @@
};
struct HandleManager {
+ ~HandleManager() { gShutdown = true; }
+
wpi::mutex mutex;
wpi::UidVector<int, 8> eventIds;
wpi::UidVector<int, 8> semaphoreIds;
@@ -40,6 +45,9 @@
WPI_EventHandle wpi::CreateEvent(bool manualReset, bool initialState) {
auto& manager = GetManager();
+ if (gShutdown) {
+ return {};
+ }
std::scoped_lock lock{manager.mutex};
auto index = manager.eventIds.emplace_back(0);
@@ -61,6 +69,9 @@
DestroySignalObject(handle);
auto& manager = GetManager();
+ if (gShutdown) {
+ return;
+ }
std::scoped_lock lock{manager.mutex};
manager.eventIds.erase(handle & 0xffffff);
}
@@ -83,6 +94,9 @@
WPI_SemaphoreHandle wpi::CreateSemaphore(int initialCount, int maximumCount) {
auto& manager = GetManager();
+ if (gShutdown) {
+ return {};
+ }
std::scoped_lock lock{manager.mutex};
auto index = manager.semaphoreIds.emplace_back(maximumCount);
@@ -104,6 +118,9 @@
DestroySignalObject(handle);
auto& manager = GetManager();
+ if (gShutdown) {
+ return;
+ }
std::scoped_lock lock{manager.mutex};
manager.eventIds.erase(handle & 0xffffff);
}
@@ -119,6 +136,9 @@
int index = handle & 0xffffff;
auto& manager = GetManager();
+ if (gShutdown) {
+ return true;
+ }
std::scoped_lock lock{manager.mutex};
auto it = manager.states.find(handle);
if (it == manager.states.end()) {
@@ -146,22 +166,26 @@
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);
+ std::span(&handle, 1), std::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) {
+std::span<WPI_Handle> wpi::WaitForObjects(std::span<const WPI_Handle> handles,
+ std::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,
+std::span<WPI_Handle> wpi::WaitForObjects(std::span<const WPI_Handle> handles,
+ std::span<WPI_Handle> signaled,
double timeout, bool* timedOut) {
auto& manager = GetManager();
+ if (gShutdown) {
+ *timedOut = false;
+ return {};
+ }
std::unique_lock lock{manager.mutex};
wpi::condition_variable cv;
bool addedWaiters = false;
@@ -240,6 +264,9 @@
void wpi::CreateSignalObject(WPI_Handle handle, bool manualReset,
bool initialState) {
auto& manager = GetManager();
+ if (gShutdown) {
+ return;
+ }
std::scoped_lock lock{manager.mutex};
auto& state = manager.states[handle];
state.signaled = initialState ? 1 : 0;
@@ -248,6 +275,9 @@
void wpi::SetSignalObject(WPI_Handle handle) {
auto& manager = GetManager();
+ if (gShutdown) {
+ return;
+ }
std::scoped_lock lock{manager.mutex};
auto it = manager.states.find(handle);
if (it == manager.states.end()) {
@@ -266,6 +296,9 @@
void wpi::ResetSignalObject(WPI_Handle handle) {
auto& manager = GetManager();
+ if (gShutdown) {
+ return;
+ }
std::scoped_lock lock{manager.mutex};
auto it = manager.states.find(handle);
if (it != manager.states.end()) {
@@ -275,6 +308,9 @@
void wpi::DestroySignalObject(WPI_Handle handle) {
auto& manager = GetManager();
+ if (gShutdown) {
+ return;
+ }
std::scoped_lock lock{manager.mutex};
auto it = manager.states.find(handle);
@@ -332,8 +368,8 @@
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))
+ return wpi::WaitForObjects(std::span(handles, handles_count),
+ std::span(signaled, handles_count))
.size();
}
@@ -341,8 +377,8 @@
WPI_Handle* signaled, double timeout,
int* timed_out) {
bool timedOutBool;
- auto signaledResult = wpi::WaitForObjects(wpi::span(handles, handles_count),
- wpi::span(signaled, handles_count),
+ auto signaledResult = wpi::WaitForObjects(std::span(handles, handles_count),
+ std::span(signaled, handles_count),
timeout, &timedOutBool);
*timed_out = timedOutBool ? 1 : 0;
return signaledResult.size();
diff --git a/wpiutil/src/main/native/cpp/TCPAcceptor.cpp b/wpiutil/src/main/native/cpp/TCPAcceptor.cpp
deleted file mode 100644
index 8d12ac3..0000000
--- a/wpiutil/src/main/native/cpp/TCPAcceptor.cpp
+++ /dev/null
@@ -1,210 +0,0 @@
-/*
- TCPAcceptor.cpp
-
- TCPAcceptor class definition. TCPAcceptor provides methods to passively
- establish TCP/IP connections with clients.
-
- ------------------------------------------
-
- Copyright (c) 2013 [Vic Hargrave - http://vichargrave.com]
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-#include "wpi/TCPAcceptor.h"
-
-#include <cstdio>
-#include <cstring>
-
-#ifdef _WIN32
-#define _WINSOCK_DEPRECATED_NO_WARNINGS
-#include <WinSock2.h>
-#include <Ws2tcpip.h>
-#pragma comment(lib, "Ws2_32.lib")
-#else
-#include <arpa/inet.h>
-#include <fcntl.h>
-#include <netinet/in.h>
-#include <unistd.h>
-#endif
-
-#include "wpi/Logger.h"
-#include "wpi/SmallString.h"
-#include "wpi/SocketError.h"
-
-using namespace wpi;
-
-TCPAcceptor::TCPAcceptor(int port, std::string_view address, Logger& logger)
- : m_lsd(0),
- m_port(port),
- m_address(address),
- m_listening(false),
- m_logger(logger) {
- m_shutdown = false;
-#ifdef _WIN32
- WSAData wsaData;
- WORD wVersionRequested = MAKEWORD(2, 2);
- (void)WSAStartup(wVersionRequested, &wsaData);
-#endif
-}
-
-TCPAcceptor::~TCPAcceptor() {
- if (m_lsd > 0) {
- shutdown();
-#ifdef _WIN32
- closesocket(m_lsd);
-#else
- close(m_lsd);
-#endif
- }
-#ifdef _WIN32
- WSACleanup();
-#endif
-}
-
-int TCPAcceptor::start() {
- if (m_listening) {
- return 0;
- }
-
- m_lsd = socket(PF_INET, SOCK_STREAM, 0);
- if (m_lsd < 0) {
- WPI_ERROR(m_logger, "{}", "could not create socket");
- return -1;
- }
- struct sockaddr_in address;
-
- std::memset(&address, 0, sizeof(address));
- address.sin_family = PF_INET;
- if (m_address.size() > 0) {
-#ifdef _WIN32
- SmallString<128> addr_copy(m_address);
- addr_copy.push_back('\0');
- int res = InetPton(PF_INET, addr_copy.data(), &(address.sin_addr));
-#else
- int res = inet_pton(PF_INET, m_address.c_str(), &(address.sin_addr));
-#endif
- if (res != 1) {
- WPI_ERROR(m_logger, "could not resolve {} address", m_address);
- return -1;
- }
- } else {
- address.sin_addr.s_addr = INADDR_ANY;
- }
- address.sin_port = htons(m_port);
-
-#ifdef _WIN32
- int optval = 1;
- setsockopt(m_lsd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
- reinterpret_cast<char*>(&optval), sizeof optval);
-#else
- int optval = 1;
- setsockopt(m_lsd, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&optval),
- sizeof optval);
-#endif
-
- int result = bind(m_lsd, reinterpret_cast<struct sockaddr*>(&address),
- sizeof(address));
- if (result != 0) {
- WPI_ERROR(m_logger, "bind() to port {} failed: {}", m_port,
- SocketStrerror());
- return result;
- }
-
- result = listen(m_lsd, 5);
- if (result != 0) {
- WPI_ERROR(m_logger, "listen() on port {} failed: {}", m_port,
- SocketStrerror());
- return result;
- }
- m_listening = true;
- return result;
-}
-
-void TCPAcceptor::shutdown() {
- m_shutdown = true;
-#ifdef _WIN32
- ::shutdown(m_lsd, SD_BOTH);
-
- // this is ugly, but the easiest way to do this
- // force wakeup of accept() with a non-blocking connect to ourselves
- struct sockaddr_in address;
-
- std::memset(&address, 0, sizeof(address));
- address.sin_family = PF_INET;
- SmallString<128> addr_copy;
- if (m_address.size() > 0)
- addr_copy = m_address;
- else
- addr_copy = "127.0.0.1";
- addr_copy.push_back('\0');
- int size = sizeof(address);
- if (WSAStringToAddress(addr_copy.data(), PF_INET, nullptr,
- (struct sockaddr*)&address, &size) != 0)
- return;
- address.sin_port = htons(m_port);
-
- int result = -1, sd = socket(AF_INET, SOCK_STREAM, 0);
- if (sd < 0)
- return;
-
- // Set socket to non-blocking
- u_long mode = 1;
- ioctlsocket(sd, FIONBIO, &mode);
-
- // Try to connect
- ::connect(sd, (struct sockaddr*)&address, sizeof(address));
-
- // Close
- ::closesocket(sd);
-
-#else
- ::shutdown(m_lsd, SHUT_RDWR);
- int nullfd = ::open("/dev/null", O_RDONLY);
- if (nullfd >= 0) {
- ::dup2(nullfd, m_lsd);
- ::close(nullfd);
- }
-#endif
-}
-
-std::unique_ptr<NetworkStream> TCPAcceptor::accept() {
- if (!m_listening || m_shutdown) {
- return nullptr;
- }
-
- struct sockaddr_in address;
-#ifdef _WIN32
- int len = sizeof(address);
-#else
- socklen_t len = sizeof(address);
-#endif
- std::memset(&address, 0, sizeof(address));
- int sd = ::accept(m_lsd, reinterpret_cast<struct sockaddr*>(&address), &len);
- if (sd < 0) {
- if (!m_shutdown) {
- WPI_ERROR(m_logger, "accept() on port {} failed: {}", m_port,
- SocketStrerror());
- }
- return nullptr;
- }
- if (m_shutdown) {
-#ifdef _WIN32
- closesocket(sd);
-#else
- close(sd);
-#endif
- return nullptr;
- }
- return std::unique_ptr<NetworkStream>(new TCPStream(sd, &address));
-}
diff --git a/wpiutil/src/main/native/cpp/TCPConnector.cpp b/wpiutil/src/main/native/cpp/TCPConnector.cpp
deleted file mode 100644
index ed97962..0000000
--- a/wpiutil/src/main/native/cpp/TCPConnector.cpp
+++ /dev/null
@@ -1,218 +0,0 @@
-/*
- TCPConnector.h
-
- TCPConnector class definition. TCPConnector provides methods to actively
- establish TCP/IP connections with a server.
-
- ------------------------------------------
-
- Copyright (c) 2013 [Vic Hargrave - http://vichargrave.com]
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
-*/
-
-#include "wpi/TCPConnector.h"
-
-#include <fcntl.h>
-
-#include <cerrno>
-#include <cstdio>
-#include <cstring>
-
-#ifdef _WIN32
-#include <WS2tcpip.h>
-#include <WinSock2.h>
-#else
-#include <arpa/inet.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <sys/select.h>
-#include <unistd.h>
-#endif
-
-#include "wpi/Logger.h"
-#include "wpi/SmallString.h"
-#include "wpi/SocketError.h"
-#include "wpi/TCPStream.h"
-
-using namespace wpi;
-
-static int ResolveHostName(const char* hostname, struct in_addr* addr) {
- struct addrinfo hints;
- struct addrinfo* res;
-
- hints.ai_flags = 0;
- hints.ai_family = AF_INET;
- hints.ai_socktype = SOCK_STREAM;
- hints.ai_protocol = 0;
- hints.ai_addrlen = 0;
- hints.ai_addr = nullptr;
- hints.ai_canonname = nullptr;
- hints.ai_next = nullptr;
- int result = getaddrinfo(hostname, nullptr, &hints, &res);
- if (result == 0) {
- std::memcpy(
- addr, &(reinterpret_cast<struct sockaddr_in*>(res->ai_addr)->sin_addr),
- sizeof(struct in_addr));
- freeaddrinfo(res);
- }
- return result;
-}
-
-std::unique_ptr<NetworkStream> TCPConnector::connect(const char* server,
- int port, Logger& logger,
- int timeout) {
-#ifdef _WIN32
- struct WSAHelper {
- WSAHelper() {
- WSAData wsaData;
- WORD wVersionRequested = MAKEWORD(2, 2);
- WSAStartup(wVersionRequested, &wsaData);
- }
- ~WSAHelper() { WSACleanup(); }
- };
- static WSAHelper helper;
-#endif
- struct sockaddr_in address;
-
- std::memset(&address, 0, sizeof(address));
- address.sin_family = AF_INET;
- if (ResolveHostName(server, &(address.sin_addr)) != 0) {
-#ifdef _WIN32
- SmallString<128> addr_copy(server);
- addr_copy.push_back('\0');
- int res = InetPton(PF_INET, addr_copy.data(), &(address.sin_addr));
-#else
- int res = inet_pton(PF_INET, server, &(address.sin_addr));
-#endif
- if (res != 1) {
- WPI_ERROR(logger, "could not resolve {} address", server);
- return nullptr;
- }
- }
- address.sin_port = htons(port);
-
- if (timeout == 0) {
- int sd = socket(AF_INET, SOCK_STREAM, 0);
- if (sd < 0) {
- WPI_ERROR(logger, "{}", "could not create socket");
- return nullptr;
- }
- if (::connect(sd, reinterpret_cast<struct sockaddr*>(&address),
- sizeof(address)) != 0) {
- WPI_ERROR(logger, "connect() to {} port {} failed: {}", server, port,
- SocketStrerror());
-#ifdef _WIN32
- closesocket(sd);
-#else
- ::close(sd);
-#endif
- return nullptr;
- }
- return std::unique_ptr<NetworkStream>(new TCPStream(sd, &address));
- }
-
- fd_set sdset;
- struct timeval tv;
- socklen_t len;
- int result = -1, valopt, sd = socket(AF_INET, SOCK_STREAM, 0);
- if (sd < 0) {
- WPI_ERROR(logger, "{}", "could not create socket");
- return nullptr;
- }
-
-// Set socket to non-blocking
-#ifdef _WIN32
- u_long mode = 1;
- if (ioctlsocket(sd, FIONBIO, &mode) == SOCKET_ERROR)
- WPI_WARNING(logger, "could not set socket to non-blocking: {}",
- SocketStrerror());
-#else
- int arg;
- arg = fcntl(sd, F_GETFL, nullptr);
- if (arg < 0) {
- WPI_WARNING(logger, "could not set socket to non-blocking: {}",
- SocketStrerror());
- } else {
- arg |= O_NONBLOCK;
- if (fcntl(sd, F_SETFL, arg) < 0) {
- WPI_WARNING(logger, "could not set socket to non-blocking: {}",
- SocketStrerror());
- }
- }
-#endif
-
- // Connect with time limit
- if ((result = ::connect(sd, reinterpret_cast<struct sockaddr*>(&address),
- sizeof(address))) < 0) {
- int my_errno = SocketErrno();
-#ifdef _WIN32
- if (my_errno == WSAEWOULDBLOCK || my_errno == WSAEINPROGRESS) {
-#else
- if (my_errno == EWOULDBLOCK || my_errno == EINPROGRESS) {
-#endif
- tv.tv_sec = timeout;
- tv.tv_usec = 0;
- FD_ZERO(&sdset);
- FD_SET(sd, &sdset);
- if (select(sd + 1, nullptr, &sdset, nullptr, &tv) > 0) {
- len = sizeof(int);
- getsockopt(sd, SOL_SOCKET, SO_ERROR, reinterpret_cast<char*>(&valopt),
- &len);
- if (valopt) {
- WPI_ERROR(logger, "select() to {} port {} error {} - {}", server,
- port, valopt, SocketStrerror(valopt));
- } else {
- // connection established
- result = 0;
- }
- } else {
- WPI_INFO(logger, "connect() to {} port {} timed out", server, port);
- }
- } else {
- WPI_ERROR(logger, "connect() to {} port {} error {} - {}", server, port,
- SocketErrno(), SocketStrerror());
- }
- }
-
-// Return socket to blocking mode
-#ifdef _WIN32
- mode = 0;
- if (ioctlsocket(sd, FIONBIO, &mode) == SOCKET_ERROR)
- WPI_WARNING(logger, "could not set socket to blocking: {}",
- SocketStrerror());
-#else
- arg = fcntl(sd, F_GETFL, nullptr);
- if (arg < 0) {
- WPI_WARNING(logger, "could not set socket to blocking: {}",
- SocketStrerror());
- } else {
- arg &= (~O_NONBLOCK);
- if (fcntl(sd, F_SETFL, arg) < 0) {
- WPI_WARNING(logger, "could not set socket to blocking: {}",
- SocketStrerror());
- }
- }
-#endif
-
- // Create stream object if connected, close if not.
- if (result == -1) {
-#ifdef _WIN32
- closesocket(sd);
-#else
- ::close(sd);
-#endif
- return nullptr;
- }
- return std::unique_ptr<NetworkStream>(new TCPStream(sd, &address));
-}
diff --git a/wpiutil/src/main/native/cpp/TCPConnector_parallel.cpp b/wpiutil/src/main/native/cpp/TCPConnector_parallel.cpp
deleted file mode 100644
index 26258cf..0000000
--- a/wpiutil/src/main/native/cpp/TCPConnector_parallel.cpp
+++ /dev/null
@@ -1,131 +0,0 @@
-// Copyright (c) FIRST and other WPILib contributors.
-// Open Source Software; you can modify and/or share it under the terms of
-// the WPILib BSD license file in the root directory of this project.
-
-#include "wpi/TCPConnector.h" // NOLINT(build/include_order)
-
-#include <atomic>
-#include <chrono>
-#include <thread>
-#include <tuple>
-
-#include "wpi/SmallSet.h"
-#include "wpi/condition_variable.h"
-#include "wpi/mutex.h"
-
-using namespace wpi;
-
-// MSVC < 1900 doesn't have support for thread_local
-#if !defined(_MSC_VER) || _MSC_VER >= 1900
-// clang check for availability of thread_local
-#if !defined(__has_feature) || __has_feature(cxx_thread_local)
-#define HAVE_THREAD_LOCAL
-#endif
-#endif
-
-std::unique_ptr<NetworkStream> TCPConnector::connect_parallel(
- span<const std::pair<const char*, int>> servers, Logger& logger,
- int timeout) {
- if (servers.empty()) {
- return nullptr;
- }
-
- // structure to make sure we don't start duplicate workers
- struct GlobalState {
- wpi::mutex mtx;
-#ifdef HAVE_THREAD_LOCAL
- SmallSet<std::pair<std::string, int>, 16> active;
-#else
- SmallSet<std::tuple<std::thread::id, std::string, int>, 16> active;
-#endif
- };
-#ifdef HAVE_THREAD_LOCAL
- thread_local auto global = std::make_shared<GlobalState>();
-#else
- static auto global = std::make_shared<GlobalState>();
- auto this_id = std::this_thread::get_id();
-#endif
- auto local = global; // copy to an automatic variable for lambda capture
-
- // structure shared between threads and this function
- struct Result {
- wpi::mutex mtx;
- wpi::condition_variable cv;
- std::unique_ptr<NetworkStream> stream;
- std::atomic<unsigned int> count{0};
- std::atomic<bool> done{false};
- };
- auto result = std::make_shared<Result>();
-
- // start worker threads; this is I/O bound so we don't limit to # of procs
- Logger* plogger = &logger;
- unsigned int num_workers = 0;
- for (const auto& server : servers) {
- std::pair<std::string, int> server_copy{std::string{server.first},
- server.second};
-#ifdef HAVE_THREAD_LOCAL
- const auto& active_tracker = server_copy;
-#else
- std::tuple<std::thread::id, std::string, int> active_tracker{
- this_id, server_copy.first, server_copy.second};
-#endif
-
- // don't start a new worker if we had a previously still-active connection
- // attempt to the same server
- {
- std::scoped_lock lock(local->mtx);
- if (local->active.count(active_tracker) > 0) {
- continue; // already in set
- }
- }
-
- ++num_workers;
-
- // start the worker
- std::thread([=] {
- if (!result->done) {
- // add to global state
- {
- std::scoped_lock lock(local->mtx);
- local->active.insert(active_tracker);
- }
-
- // try to connect
- auto stream = connect(server_copy.first.c_str(), server_copy.second,
- *plogger, timeout);
-
- // remove from global state
- {
- std::scoped_lock lock(local->mtx);
- local->active.erase(active_tracker);
- }
-
- // successful connection
- if (stream) {
- std::scoped_lock lock(result->mtx);
- if (!result->done.exchange(true)) {
- result->stream = std::move(stream);
- }
- }
- }
- ++result->count;
- result->cv.notify_all();
- }).detach();
- }
-
- // wait for a result, timeout, or all finished
- std::unique_lock lock(result->mtx);
- if (timeout == 0) {
- result->cv.wait(
- lock, [&] { return result->stream || result->count >= num_workers; });
- } else {
- auto timeout_time =
- std::chrono::steady_clock::now() + std::chrono::seconds(timeout);
- result->cv.wait_until(lock, timeout_time, [&] {
- return result->stream || result->count >= num_workers;
- });
- }
-
- // no need to wait for remaining worker threads; shared_ptr will clean up
- return std::move(result->stream);
-}
diff --git a/wpiutil/src/main/native/cpp/TCPStream.cpp b/wpiutil/src/main/native/cpp/TCPStream.cpp
deleted file mode 100644
index 4567161..0000000
--- a/wpiutil/src/main/native/cpp/TCPStream.cpp
+++ /dev/null
@@ -1,230 +0,0 @@
-/*
- TCPStream.h
-
- TCPStream class definition. TCPStream provides methods to transfer
- data between peers over a TCP/IP connection.
-
- ------------------------------------------
-
- Copyright (c) 2013 [Vic Hargrave - http://vichargrave.com]
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-#include "wpi/TCPStream.h"
-
-#include <fcntl.h>
-
-#ifdef _WIN32
-#include <winsock2.h>
-#include <ws2tcpip.h>
-#else
-#include <arpa/inet.h>
-#include <netinet/tcp.h>
-#include <unistd.h>
-#endif
-
-#include <cerrno>
-
-using namespace wpi;
-
-TCPStream::TCPStream(int sd, sockaddr_in* address)
- : m_sd(sd), m_blocking(true) {
- char ip[50];
-#ifdef _WIN32
- InetNtop(PF_INET, &(address->sin_addr.s_addr), ip, sizeof(ip) - 1);
-#else
- inet_ntop(PF_INET, reinterpret_cast<in_addr*>(&(address->sin_addr.s_addr)),
- ip, sizeof(ip) - 1);
-#ifdef SO_NOSIGPIPE
- // disable SIGPIPE on Mac OS X
- int set = 1;
- setsockopt(m_sd, SOL_SOCKET, SO_NOSIGPIPE, reinterpret_cast<char*>(&set),
- sizeof set);
-#endif
-#endif
- m_peerIP = ip;
- m_peerPort = ntohs(address->sin_port);
-}
-
-TCPStream::~TCPStream() {
- close();
-}
-
-size_t TCPStream::send(const char* buffer, size_t len, Error* err) {
- if (m_sd < 0) {
- *err = kConnectionClosed;
- return 0;
- }
-#ifdef _WIN32
- WSABUF wsaBuf;
- wsaBuf.buf = const_cast<char*>(buffer);
- wsaBuf.len = (ULONG)len;
- DWORD rv;
- bool result = true;
- while (WSASend(m_sd, &wsaBuf, 1, &rv, 0, nullptr, nullptr) == SOCKET_ERROR) {
- if (WSAGetLastError() != WSAEWOULDBLOCK) {
- result = false;
- break;
- }
- if (!m_blocking) {
- *err = kWouldBlock;
- return 0;
- }
- Sleep(1);
- }
- if (!result) {
- char Buffer[128];
-#ifdef _MSC_VER
- sprintf_s(Buffer, "Send() failed: WSA error=%d\n", WSAGetLastError());
-#else
- std::snprintf(Buffer, sizeof(Buffer), "Send() failed: WSA error=%d\n",
- WSAGetLastError());
-#endif
- OutputDebugStringA(Buffer);
- *err = kConnectionReset;
- return 0;
- }
-#else
-#ifdef MSG_NOSIGNAL
- // disable SIGPIPE on Linux
- ssize_t rv = ::send(m_sd, buffer, len, MSG_NOSIGNAL);
-#else
- ssize_t rv = ::send(m_sd, buffer, len, 0);
-#endif
- if (rv < 0) {
- if (!m_blocking && (errno == EAGAIN || errno == EWOULDBLOCK)) {
- *err = kWouldBlock;
- } else {
- *err = kConnectionReset;
- }
- return 0;
- }
-#endif
- return static_cast<size_t>(rv);
-}
-
-size_t TCPStream::receive(char* buffer, size_t len, Error* err, int timeout) {
- if (m_sd < 0) {
- *err = kConnectionClosed;
- return 0;
- }
-#ifdef _WIN32
- int rv;
-#else
- ssize_t rv;
-#endif
- if (timeout <= 0) {
-#ifdef _WIN32
- rv = recv(m_sd, buffer, len, 0);
-#else
- rv = read(m_sd, buffer, len);
-#endif
- } else if (WaitForReadEvent(timeout)) {
-#ifdef _WIN32
- rv = recv(m_sd, buffer, len, 0);
-#else
- rv = read(m_sd, buffer, len);
-#endif
- } else {
- *err = kConnectionTimedOut;
- return 0;
- }
- if (rv < 0) {
-#ifdef _WIN32
- if (!m_blocking && WSAGetLastError() == WSAEWOULDBLOCK) {
-#else
- if (!m_blocking && (errno == EAGAIN || errno == EWOULDBLOCK)) {
-#endif
- *err = kWouldBlock;
- } else {
- *err = kConnectionReset;
- }
- return 0;
- }
- return static_cast<size_t>(rv);
-}
-
-void TCPStream::close() {
- if (m_sd >= 0) {
-#ifdef _WIN32
- ::shutdown(m_sd, SD_BOTH);
- closesocket(m_sd);
-#else
- ::shutdown(m_sd, SHUT_RDWR);
- ::close(m_sd);
-#endif
- }
- m_sd = -1;
-}
-
-std::string_view TCPStream::getPeerIP() const {
- return m_peerIP;
-}
-
-int TCPStream::getPeerPort() const {
- return m_peerPort;
-}
-
-void TCPStream::setNoDelay() {
- if (m_sd < 0) {
- return;
- }
- int optval = 1;
- setsockopt(m_sd, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char*>(&optval),
- sizeof optval);
-}
-
-bool TCPStream::setBlocking(bool enabled) {
- if (m_sd < 0) {
- return true; // silently accept
- }
-#ifdef _WIN32
- u_long mode = enabled ? 0 : 1;
- if (ioctlsocket(m_sd, FIONBIO, &mode) == SOCKET_ERROR) {
- return false;
- }
-#else
- int flags = fcntl(m_sd, F_GETFL, nullptr);
- if (flags < 0) {
- return false;
- }
- if (enabled) {
- flags &= ~O_NONBLOCK;
- } else {
- flags |= O_NONBLOCK;
- }
- if (fcntl(m_sd, F_SETFL, flags) < 0) {
- return false;
- }
-#endif
- return true;
-}
-
-int TCPStream::getNativeHandle() const {
- return m_sd;
-}
-
-bool TCPStream::WaitForReadEvent(int timeout) {
- fd_set sdset;
- struct timeval tv;
-
- tv.tv_sec = timeout;
- tv.tv_usec = 0;
- FD_ZERO(&sdset);
- FD_SET(m_sd, &sdset);
- if (select(m_sd + 1, &sdset, 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
deleted file mode 100644
index 108ef54..0000000
--- a/wpiutil/src/main/native/cpp/UDPClient.cpp
+++ /dev/null
@@ -1,248 +0,0 @@
-// Copyright (c) FIRST and other WPILib contributors.
-// Open Source Software; you can modify and/or share it under the terms of
-// the WPILib BSD license file in the root directory of this project.
-
-#include "wpi/UDPClient.h"
-
-#ifdef _WIN32
-#include <WinSock2.h>
-#include <Ws2tcpip.h>
-#pragma comment(lib, "Ws2_32.lib")
-#else
-#include <arpa/inet.h>
-#include <fcntl.h>
-#include <netinet/in.h>
-#include <unistd.h>
-#endif
-
-#include "wpi/Logger.h"
-#include "wpi/SmallString.h"
-#include "wpi/SocketError.h"
-
-using namespace wpi;
-
-UDPClient::UDPClient(Logger& logger) : UDPClient("", 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),
- m_port(other.m_port),
- m_address(std::move(other.m_address)),
- m_logger(other.m_logger) {
- other.m_lsd = 0;
- other.m_port = 0;
-}
-
-UDPClient::~UDPClient() {
- if (m_lsd > 0) {
- shutdown();
- }
-}
-
-UDPClient& UDPClient::operator=(UDPClient&& other) {
- if (this == &other) {
- return *this;
- }
- shutdown();
- m_logger = other.m_logger;
- m_lsd = other.m_lsd;
- m_address = std::move(other.m_address);
- m_port = other.m_port;
- other.m_lsd = 0;
- other.m_port = 0;
- return *this;
-}
-
-int UDPClient::start() {
- return start(0);
-}
-
-int UDPClient::start(int port) {
- if (m_lsd > 0) {
- return 0;
- }
-
-#ifdef _WIN32
- WSAData wsaData;
- WORD wVersionRequested = MAKEWORD(2, 2);
- WSAStartup(wVersionRequested, &wsaData);
-#endif
-
- m_lsd = socket(AF_INET, SOCK_DGRAM, 0);
-
- if (m_lsd < 0) {
- WPI_ERROR(m_logger, "{}", "could not create socket");
- return -1;
- }
-
- struct sockaddr_in addr;
- std::memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- if (m_address.size() > 0) {
-#ifdef _WIN32
- SmallString<128> addr_copy(m_address);
- addr_copy.push_back('\0');
- int res = InetPton(PF_INET, addr_copy.data(), &(addr.sin_addr));
-#else
- int res = inet_pton(PF_INET, m_address.c_str(), &(addr.sin_addr));
-#endif
- if (res != 1) {
- WPI_ERROR(m_logger, "could not resolve {} address", m_address);
- return -1;
- }
- } else {
- addr.sin_addr.s_addr = INADDR_ANY;
- }
- addr.sin_port = htons(port);
-
- if (port != 0) {
-#ifdef _WIN32
- int optval = 1;
- setsockopt(m_lsd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
- reinterpret_cast<char*>(&optval), sizeof optval);
-#else
- int optval = 1;
- setsockopt(m_lsd, SOL_SOCKET, SO_REUSEADDR,
- reinterpret_cast<char*>(&optval), sizeof optval);
-#endif
- }
-
- int result = bind(m_lsd, reinterpret_cast<sockaddr*>(&addr), sizeof(addr));
- if (result != 0) {
- WPI_ERROR(m_logger, "bind() failed: {}", SocketStrerror());
- return result;
- }
- m_port = port;
- return 0;
-}
-
-void UDPClient::shutdown() {
- if (m_lsd > 0) {
-#ifdef _WIN32
- ::shutdown(m_lsd, SD_BOTH);
- closesocket(m_lsd);
- WSACleanup();
-#else
- ::shutdown(m_lsd, SHUT_RDWR);
- close(m_lsd);
-#endif
- m_lsd = 0;
- m_port = 0;
- }
-}
-
-int UDPClient::send(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;
- SmallString<128> remoteAddr{server};
- if (remoteAddr.empty()) {
- WPI_ERROR(m_logger, "{}", "server must be passed");
- return -1;
- }
-
-#ifdef _WIN32
- int res = InetPton(AF_INET, remoteAddr.c_str(), &(addr.sin_addr));
-#else
- int res = inet_pton(AF_INET, remoteAddr.c_str(), &(addr.sin_addr));
-#endif
- if (res != 1) {
- WPI_ERROR(m_logger, "could not resolve {} address", server);
- return -1;
- }
- addr.sin_port = htons(port);
-
- // sendto should not block
- int result =
- sendto(m_lsd, reinterpret_cast<const char*>(data.data()), data.size(), 0,
- reinterpret_cast<sockaddr*>(&addr), sizeof(addr));
- return result;
-}
-
-int UDPClient::send(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;
- SmallString<128> remoteAddr{server};
- if (remoteAddr.empty()) {
- WPI_ERROR(m_logger, "{}", "server must be passed");
- return -1;
- }
-
-#ifdef _WIN32
- int res = InetPton(AF_INET, remoteAddr.c_str(), &(addr.sin_addr));
-#else
- int res = inet_pton(AF_INET, remoteAddr.c_str(), &(addr.sin_addr));
-#endif
- if (res != 1) {
- WPI_ERROR(m_logger, "could not resolve {} address", server);
- return -1;
- }
- addr.sin_port = htons(port);
-
- // sendto should not block
- int result = sendto(m_lsd, data.data(), data.size(), 0,
- reinterpret_cast<sockaddr*>(&addr), sizeof(addr));
- return result;
-}
-
-int UDPClient::receive(uint8_t* data_received, int receive_len) {
- if (m_port == 0) {
- return -1; // return if not receiving
- }
- return recv(m_lsd, reinterpret_cast<char*>(data_received), receive_len, 0);
-}
-
-int UDPClient::receive(uint8_t* data_received, int receive_len,
- SmallVectorImpl<char>* addr_received,
- int* port_received) {
- if (m_port == 0) {
- return -1; // return if not receiving
- }
-
- struct sockaddr_in remote;
- socklen_t remote_len = sizeof(remote);
- std::memset(&remote, 0, sizeof(remote));
-
- int result =
- recvfrom(m_lsd, reinterpret_cast<char*>(data_received), receive_len, 0,
- reinterpret_cast<sockaddr*>(&remote), &remote_len);
-
- char ip[50];
-#ifdef _WIN32
- InetNtop(PF_INET, &(remote.sin_addr.s_addr), ip, sizeof(ip) - 1);
-#else
- inet_ntop(PF_INET, reinterpret_cast<in_addr*>(&(remote.sin_addr.s_addr)), ip,
- sizeof(ip) - 1);
-#endif
-
- ip[49] = '\0';
- int addr_len = std::strlen(ip);
- addr_received->clear();
- addr_received->append(&ip[0], &ip[addr_len]);
-
- *port_received = ntohs(remote.sin_port);
-
- return result;
-}
-
-int UDPClient::set_timeout(double timeout) {
- if (timeout < 0) {
- return -1;
- }
- struct timeval tv;
- tv.tv_sec = timeout; // truncating will give seconds
- timeout -= tv.tv_sec; // remove seconds portion
- tv.tv_usec = timeout * 1000000; // fractions of a second to us
- int ret = setsockopt(m_lsd, SOL_SOCKET, SO_RCVTIMEO,
- reinterpret_cast<char*>(&tv), sizeof(tv));
- if (ret < 0) {
- WPI_ERROR(m_logger, "{}", "set timeout failed");
- }
- return ret;
-}
diff --git a/wpiutil/src/main/native/cpp/WebSocket.cpp b/wpiutil/src/main/native/cpp/WebSocket.cpp
deleted file mode 100644
index 4bb49d3..0000000
--- a/wpiutil/src/main/native/cpp/WebSocket.cpp
+++ /dev/null
@@ -1,656 +0,0 @@
-// Copyright (c) FIRST and other WPILib contributors.
-// Open Source Software; you can modify and/or share it under the terms of
-// the WPILib BSD license file in the root directory of this project.
-
-#include "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"
-
-using namespace wpi;
-
-namespace {
-class WebSocketWriteReq : public uv::WriteReq {
- public:
- explicit WebSocketWriteReq(
- 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;
-};
-} // namespace
-
-class WebSocket::ClientHandshakeData {
- public:
- ClientHandshakeData() {
- // key is a random nonce
- static std::random_device rd;
- static std::default_random_engine gen{rd()};
- std::uniform_int_distribution<unsigned int> dist(0, 255);
- char nonce[16]; // the nonce sent to the server
- for (char& v : nonce) {
- v = static_cast<char>(dist(gen));
- }
- raw_svector_ostream os(key);
- Base64Encode(os, {nonce, 16});
- }
- ~ClientHandshakeData() {
- if (auto t = timer.lock()) {
- t->Stop();
- t->Close();
- }
- }
-
- SmallString<64> key; // the key sent to the server
- SmallVector<std::string, 2> protocols; // valid protocols
- HttpParser parser{HttpParser::kResponse}; // server response parser
- bool hasUpgrade = false;
- bool hasConnection = false;
- bool hasAccept = false;
- bool hasProtocol = false;
-
- std::weak_ptr<uv::Timer> timer;
-};
-
-static std::string_view AcceptHash(std::string_view key,
- SmallVectorImpl<char>& buf) {
- SHA1 hash;
- hash.Update(key);
- hash.Update("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
- SmallString<64> hashBuf;
- return Base64Encode(hash.RawFinal(hashBuf), buf);
-}
-
-WebSocket::WebSocket(uv::Stream& stream, bool server, const private_init&)
- : m_stream{stream}, m_server{server} {
- // Connect closed and error signals to ourselves
- m_stream.closed.connect([this]() { SetClosed(1006, "handle closed"); });
- m_stream.error.connect([this](uv::Error err) {
- Terminate(1006, fmt::format("stream error: {}", err.name()));
- });
-
- // Start reading
- m_stream.StopRead(); // we may have been reading
- m_stream.StartRead();
- m_stream.data.connect(
- [this](uv::Buffer& buf, size_t size) { HandleIncoming(buf, size); });
- m_stream.end.connect(
- [this]() { Terminate(1006, "remote end closed connection"); });
-}
-
-WebSocket::~WebSocket() = default;
-
-std::shared_ptr<WebSocket> WebSocket::CreateClient(
- 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);
- return ws;
-}
-
-std::shared_ptr<WebSocket> WebSocket::CreateServer(uv::Stream& stream,
- 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, std::string_view reason) {
- SendClose(code, reason);
- if (m_state != FAILED && m_state != CLOSED) {
- m_state = CLOSING;
- }
-}
-
-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, std::string_view reason) {
- if (m_state == FAILED || m_state == CLOSED) {
- return;
- }
- SetClosed(code, reason);
- Shutdown();
-}
-
-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>();
-
- // Build client request
- SmallVector<uv::Buffer, 4> bufs;
- raw_uv_ostream os{bufs, 4096};
-
- os << "GET " << uri << " HTTP/1.1\r\n";
- os << "Host: " << host << "\r\n";
- os << "Upgrade: websocket\r\n";
- os << "Connection: Upgrade\r\n";
- os << "Sec-WebSocket-Key: " << m_clientHandshake->key << "\r\n";
- os << "Sec-WebSocket-Version: 13\r\n";
-
- // protocols (if provided)
- if (!protocols.empty()) {
- os << "Sec-WebSocket-Protocol: ";
- bool first = true;
- for (auto protocol : protocols) {
- if (!first) {
- os << ", ";
- } else {
- first = false;
- }
- os << protocol;
- // also save for later checking against server response
- m_clientHandshake->protocols.emplace_back(protocol);
- }
- os << "\r\n";
- }
-
- // other headers
- for (auto&& header : options.extraHeaders) {
- os << header.first << ": " << header.second << "\r\n";
- }
-
- // finish headers
- os << "\r\n";
-
- // Send client request
- m_stream.Write(bufs, [](auto bufs, uv::Error) {
- for (auto& buf : bufs) {
- buf.Deallocate();
- }
- });
-
- // Set up client response handling
- m_clientHandshake->parser.status.connect([this](std::string_view status) {
- unsigned int code = m_clientHandshake->parser.GetStatusCode();
- if (code != 101) {
- Terminate(code, status);
- }
- });
- m_clientHandshake->parser.header.connect(
- [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 (equals_lower(name, "connection")) {
- if (!equals_lower(value, "upgrade")) {
- return Terminate(1002, "invalid connection response value");
- }
- m_clientHandshake->hasConnection = true;
- } else if (equals_lower(name, "sec-websocket-accept")) {
- // Check against expected response
- SmallString<64> acceptBuf;
- if (!equals(value, AcceptHash(m_clientHandshake->key, acceptBuf))) {
- return Terminate(1002, "invalid accept key");
- }
- m_clientHandshake->hasAccept = true;
- } else if (equals_lower(name, "sec-websocket-extensions")) {
- // No extensions are supported
- 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 (equals_lower(value, protocol)) {
- match = true;
- break;
- }
- }
- if (!match) {
- return Terminate(1003, "unsupported protocol");
- }
- m_clientHandshake->hasProtocol = true;
- m_protocol = value;
- }
- });
- m_clientHandshake->parser.headersComplete.connect([this](bool) {
- if (!m_clientHandshake->hasUpgrade || !m_clientHandshake->hasConnection ||
- !m_clientHandshake->hasAccept ||
- (!m_clientHandshake->hasProtocol &&
- !m_clientHandshake->protocols.empty())) {
- return Terminate(1002, "invalid response");
- }
- if (m_state == CONNECTING) {
- m_state = OPEN;
- open(m_protocol);
- }
- });
-
- // Start handshake timer if a timeout was specified
- if (options.handshakeTimeout != (uv::Timer::Time::max)()) {
- auto timer = uv::Timer::Create(m_stream.GetLoopRef());
- timer->timeout.connect(
- [this]() { Terminate(1006, "connection timed out"); });
- timer->Start(options.handshakeTimeout);
- m_clientHandshake->timer = timer;
- }
-}
-
-void WebSocket::StartServer(std::string_view key, std::string_view version,
- std::string_view protocol) {
- m_protocol = protocol;
-
- // Build server response
- SmallVector<uv::Buffer, 4> bufs;
- raw_uv_ostream os{bufs, 4096};
-
- // Handle unsupported version
- if (version != "13") {
- os << "HTTP/1.1 426 Upgrade Required\r\n";
- os << "Upgrade: WebSocket\r\n";
- os << "Sec-WebSocket-Version: 13\r\n\r\n";
- m_stream.Write(bufs, [this](auto bufs, uv::Error) {
- for (auto& buf : bufs) {
- buf.Deallocate();
- }
- // XXX: Should we support sending a new handshake on the same connection?
- // XXX: "this->" is required by GCC 5.5 (bug)
- this->Terminate(1003, "unsupported protocol version");
- });
- return;
- }
-
- os << "HTTP/1.1 101 Switching Protocols\r\n";
- os << "Upgrade: websocket\r\n";
- os << "Connection: Upgrade\r\n";
-
- // accept hash
- SmallString<64> acceptBuf;
- os << "Sec-WebSocket-Accept: " << AcceptHash(key, acceptBuf) << "\r\n";
-
- if (!protocol.empty()) {
- os << "Sec-WebSocket-Protocol: " << protocol << "\r\n";
- }
-
- // end headers
- os << "\r\n";
-
- // Send server response
- m_stream.Write(bufs, [this](auto bufs, uv::Error) {
- for (auto& buf : bufs) {
- buf.Deallocate();
- }
- if (m_state == CONNECTING) {
- m_state = OPEN;
- open(m_protocol);
- }
- });
-}
-
-void WebSocket::SendClose(uint16_t code, 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 << span{codeMsb};
- os << reason;
- }
- Send(kFlagFin | kOpClose, bufs, [](auto bufs, uv::Error) {
- for (auto&& buf : bufs) {
- buf.Deallocate();
- }
- });
-}
-
-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;
- closed(code, reason);
-}
-
-void WebSocket::Shutdown() {
- m_stream.Shutdown([this] { m_stream.Close(); });
-}
-
-void WebSocket::HandleIncoming(uv::Buffer& buf, size_t size) {
- // ignore incoming data if we're failed or closed
- if (m_state == FAILED || m_state == CLOSED) {
- return;
- }
-
- 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()) {
- return Terminate(1003, "invalid response");
- }
- if (m_state != OPEN) {
- return; // not done with handshake yet
- }
-
- // we're done with the handshake, so release its memory
- m_clientHandshake.reset();
-
- // fall through to process additional data after handshake
- } else {
- return Terminate(1003, "got data on server before response");
- }
- }
-
- // Message processing
- while (!data.empty()) {
- if (m_frameSize == UINT64_MAX) {
- // Need at least two bytes to determine header length
- if (m_header.size() < 2u) {
- size_t toCopy = (std::min)(2u - m_header.size(), data.size());
- m_header.append(data.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");
- }
- }
-
- // Once we have first two bytes, we can calculate the header size
- if (m_headerSize == 0) {
- m_headerSize = 2;
- uint8_t len = m_header[1] & kLenMask;
- if (len == 126) {
- m_headerSize += 2;
- } else if (len == 127) {
- m_headerSize += 8;
- }
- bool masking = (m_header[1] & kFlagMasking) != 0;
- if (masking) {
- m_headerSize += 4; // masking key
- }
- // On server side, incoming messages MUST be masked
- // On client side, incoming messages MUST NOT be masked
- if (m_server && !masking) {
- return Fail(1002, "client data not masked");
- }
- if (!m_server && masking) {
- return Fail(1002, "server data masked");
- }
- }
-
- // Need to complete header to calculate message size
- if (m_header.size() < m_headerSize) {
- size_t toCopy = (std::min)(m_headerSize - m_header.size(), data.size());
- m_header.append(data.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) {
- m_frameSize = (static_cast<uint16_t>(m_header[2]) << 8) |
- static_cast<uint16_t>(m_header[3]);
- } else if (len == 127) {
- m_frameSize = (static_cast<uint64_t>(m_header[2]) << 56) |
- (static_cast<uint64_t>(m_header[3]) << 48) |
- (static_cast<uint64_t>(m_header[4]) << 40) |
- (static_cast<uint64_t>(m_header[5]) << 32) |
- (static_cast<uint64_t>(m_header[6]) << 24) |
- (static_cast<uint64_t>(m_header[7]) << 16) |
- (static_cast<uint64_t>(m_header[8]) << 8) |
- static_cast<uint64_t>(m_header[9]);
- } else {
- m_frameSize = len;
- }
-
- // limit maximum size
- if ((m_payload.size() + m_frameSize) > m_maxMessageSize) {
- return Fail(1009, "message too large");
- }
- }
- }
-
- if (m_frameSize != UINT64_MAX) {
- size_t need = m_frameStart + m_frameSize - m_payload.size();
- size_t toCopy = (std::min)(need, data.size());
- m_payload.append(data.data(), data.data() + toCopy);
- data.remove_prefix(toCopy);
- need -= toCopy;
- if (need == 0) {
- // We have a complete frame
- // If the message had masking, unmask it
- if ((m_header[1] & kFlagMasking) != 0) {
- uint8_t key[4] = {
- m_header[m_headerSize - 4], m_header[m_headerSize - 3],
- m_header[m_headerSize - 2], m_header[m_headerSize - 1]};
- int n = 0;
- for (uint8_t& ch : span{m_payload}.subspan(m_frameStart)) {
- ch ^= key[n++];
- if (n >= 4) {
- n = 0;
- }
- }
- }
-
- // Handle message
- bool fin = (m_header[0] & kFlagFin) != 0;
- uint8_t opcode = m_header[0] & kOpMask;
- switch (opcode) {
- case kOpCont:
- switch (m_fragmentOpcode) {
- case kOpText:
- if (!m_combineFragments || fin) {
- text(std::string_view{reinterpret_cast<char*>(
- m_payload.data()),
- m_payload.size()},
- fin);
- }
- break;
- case kOpBinary:
- if (!m_combineFragments || fin) {
- binary(m_payload, fin);
- }
- break;
- default:
- // no preceding message?
- return Fail(1002, "invalid continuation message");
- }
- if (fin) {
- m_fragmentOpcode = 0;
- }
- break;
- case kOpText:
- if (m_fragmentOpcode != 0) {
- return Fail(1002, "incomplete fragment");
- }
- if (!m_combineFragments || fin) {
- text(std::string_view{reinterpret_cast<char*>(m_payload.data()),
- m_payload.size()},
- fin);
- }
- if (!fin) {
- m_fragmentOpcode = opcode;
- }
- break;
- case kOpBinary:
- if (m_fragmentOpcode != 0) {
- return Fail(1002, "incomplete fragment");
- }
- if (!m_combineFragments || fin) {
- binary(m_payload, fin);
- }
- if (!fin) {
- m_fragmentOpcode = opcode;
- }
- break;
- case kOpClose: {
- uint16_t code;
- std::string_view reason;
- if (!fin) {
- code = 1002;
- reason = "cannot fragment control frames";
- } else if (m_payload.size() < 2) {
- code = 1005;
- } else {
- code = (static_cast<uint16_t>(m_payload[0]) << 8) |
- static_cast<uint16_t>(m_payload[1]);
- reason = 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);
- }
- SetClosed(code, reason);
- // If we're the server, shutdown the connection.
- if (m_server) {
- Shutdown();
- }
- break;
- }
- case kOpPing:
- if (!fin) {
- return Fail(1002, "cannot fragment control frames");
- }
- ping(m_payload);
- break;
- case kOpPong:
- if (!fin) {
- return Fail(1002, "cannot fragment control frames");
- }
- pong(m_payload);
- break;
- default:
- return Fail(1002, "invalid message opcode");
- }
-
- // Prepare for next message
- m_header.clear();
- m_headerSize = 0;
- if (!m_combineFragments || fin) {
- m_payload.clear();
- }
- m_frameStart = m_payload.size();
- m_frameSize = UINT64_MAX;
- }
- }
- }
-}
-
-void WebSocket::Send(
- uint8_t opcode, 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) {
- err = UV_EAGAIN;
- } else {
- err = UV_ESHUTDOWN;
- }
- SmallVector<uv::Buffer, 4> bufs{data.begin(), data.end()};
- callback(bufs, uv::Error{err});
- return;
- }
-
- auto req = std::make_shared<WebSocketWriteReq>(std::move(callback));
- raw_uv_ostream os{req->m_bufs, 4096};
-
- // opcode (includes FIN bit)
- os << static_cast<unsigned char>(opcode);
-
- // payload length
- uint64_t size = 0;
- for (auto&& buf : data) {
- size += buf.len;
- }
- if (size < 126) {
- os << static_cast<unsigned char>((m_server ? 0x00 : kFlagMasking) | size);
- } else if (size <= 0xffff) {
- os << static_cast<unsigned char>((m_server ? 0x00 : kFlagMasking) | 126);
- const uint8_t sizeMsb[] = {static_cast<uint8_t>((size >> 8) & 0xff),
- static_cast<uint8_t>(size & 0xff)};
- os << span{sizeMsb};
- } else {
- os << static_cast<unsigned char>((m_server ? 0x00 : kFlagMasking) | 127);
- const uint8_t sizeMsb[] = {static_cast<uint8_t>((size >> 56) & 0xff),
- static_cast<uint8_t>((size >> 48) & 0xff),
- static_cast<uint8_t>((size >> 40) & 0xff),
- static_cast<uint8_t>((size >> 32) & 0xff),
- static_cast<uint8_t>((size >> 24) & 0xff),
- static_cast<uint8_t>((size >> 16) & 0xff),
- static_cast<uint8_t>((size >> 8) & 0xff),
- static_cast<uint8_t>(size & 0xff)};
- os << span{sizeMsb};
- }
-
- // clients need to mask the input data
- if (!m_server) {
- // generate masking key
- static std::random_device rd;
- static std::default_random_engine gen{rd()};
- std::uniform_int_distribution<unsigned int> dist(0, 255);
- uint8_t key[4];
- for (uint8_t& v : key) {
- v = dist(gen);
- }
- os << 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;
- }
- }
- }
- 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(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();
- req->m_bufs.append(data.begin(), data.end());
- m_stream.Write(req->m_bufs, req);
- }
-}
diff --git a/wpiutil/src/main/native/cpp/WebSocketServer.cpp b/wpiutil/src/main/native/cpp/WebSocketServer.cpp
deleted file mode 100644
index 1562f3b..0000000
--- a/wpiutil/src/main/native/cpp/WebSocketServer.cpp
+++ /dev/null
@@ -1,171 +0,0 @@
-// Copyright (c) FIRST and other WPILib contributors.
-// Open Source Software; you can modify and/or share it under the terms of
-// the WPILib BSD license file in the root directory of this project.
-
-#include "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"
-
-using namespace wpi;
-
-WebSocketServerHelper::WebSocketServerHelper(HttpParser& req) {
- req.header.connect([this](std::string_view name, std::string_view value) {
- if (equals_lower(name, "host")) {
- m_gotHost = true;
- } 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 (equals_lower(name, "sec-websocket-version")) {
- m_version = value;
- } else if (equals_lower(name, "sec-websocket-protocol")) {
- // Protocols are comma delimited, repeated headers add to list
- SmallVector<std::string_view, 2> protocols;
- split(value, protocols, ",", -1, false);
- for (auto protocol : protocols) {
- protocol = trim(protocol);
- if (!protocol.empty()) {
- m_protocols.emplace_back(protocol);
- }
- }
- }
- });
- req.headersComplete.connect([&req, this](bool) {
- if (req.IsUpgrade() && IsUpgrade()) {
- upgrade();
- }
- });
-}
-
-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 {true, protocol};
- }
- }
- }
- return {false, {}};
-}
-
-WebSocketServer::WebSocketServer(uv::Stream& stream,
- 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{std::move(options)} {
- // Header handling
- 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");
- }
- }
- }
- });
- m_req.url.connect([this](std::string_view name) {
- if (m_options.checkUrl) {
- if (!m_options.checkUrl(name)) {
- Abort(404, "Not Found");
- }
- }
- });
- m_req.headersComplete.connect([this](bool) {
- // We only accept websocket connections
- if (!m_helper.IsUpgrade() || !m_req.IsUpgrade()) {
- Abort(426, "Upgrade Required");
- }
- });
-
- // Handle upgrade event
- m_helper.upgrade.connect([this] {
- if (m_aborted) {
- return;
- }
-
- // Negotiate sub-protocol
- SmallVector<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();
-
- // Accepting the stream may destroy this (as it replaces the stream user
- // data), so grab a shared pointer first.
- auto self = shared_from_this();
-
- // Accept the upgrade
- auto ws = m_helper.Accept(m_stream, protocol);
-
- // Connect the websocket open event to our connected event.
- ws->open.connect_extended(
- [self, s = ws.get()](auto conn, 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(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(); });
- m_endConn = stream.end.connect_connection([this] { m_stream.Close(); });
-}
-
-std::shared_ptr<WebSocketServer> WebSocketServer::Create(
- uv::Stream& stream, span<const std::string_view> protocols,
- const ServerOptions& options) {
- auto server = std::make_shared<WebSocketServer>(stream, protocols, options,
- private_init{});
- stream.SetData(server);
- return server;
-}
-
-void WebSocketServer::Abort(uint16_t code, std::string_view reason) {
- if (m_aborted) {
- return;
- }
- m_aborted = true;
-
- // Build response
- SmallVector<uv::Buffer, 4> bufs;
- raw_uv_ostream os{bufs, 1024};
-
- // Handle unsupported version
- 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();
- }
- 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
index 34d6cca..fad6a66 100644
--- a/wpiutil/src/main/native/cpp/fs.cpp
+++ b/wpiutil/src/main/native/cpp/fs.cpp
@@ -82,17 +82,6 @@
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;
@@ -251,12 +240,6 @@
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.
diff --git a/wpiutil/src/main/native/cpp/hostname.cpp b/wpiutil/src/main/native/cpp/hostname.cpp
deleted file mode 100644
index d907023..0000000
--- a/wpiutil/src/main/native/cpp/hostname.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright (c) FIRST and other WPILib contributors.
-// Open Source Software; you can modify and/or share it under the terms of
-// the WPILib BSD license file in the root directory of this project.
-
-#include "wpi/hostname.h"
-
-#include <cstdlib>
-#include <string>
-#include <string_view>
-
-#include "uv.h"
-#include "wpi/SmallVector.h"
-
-namespace wpi {
-
-std::string GetHostname() {
- std::string rv;
- char name[256];
- size_t size = sizeof(name);
-
- int err = uv_os_gethostname(name, &size);
- if (err == 0) {
- rv.assign(name, size);
- } else if (err == UV_ENOBUFS) {
- char* name2 = static_cast<char*>(std::malloc(size));
- err = uv_os_gethostname(name2, &size);
- if (err == 0) {
- rv.assign(name2, size);
- }
- std::free(name2);
- }
-
- return rv;
-}
-
-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);
-
- name.clear();
-
- int err = uv_os_gethostname(tmpName, &size);
- if (err == 0) {
- name.append(tmpName, tmpName + size);
- } else if (err == UV_ENOBUFS) {
- name.resize(size);
- err = uv_os_gethostname(name.data(), &size);
- if (err != 0) {
- size = 0;
- }
- }
-
- return {name.data(), size};
-}
-
-} // namespace wpi
diff --git a/wpiutil/src/main/native/cpp/http_parser.cpp b/wpiutil/src/main/native/cpp/http_parser.cpp
deleted file mode 100644
index bc442b2..0000000
--- a/wpiutil/src/main/native/cpp/http_parser.cpp
+++ /dev/null
@@ -1,2475 +0,0 @@
-/* Copyright Joyent, Inc. and other Node contributors.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to
- * deal in the Software without restriction, including without limitation the
- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE.
- */
-#include "wpi/http_parser.h"
-#include <assert.h>
-#include <stddef.h>
-#include <ctype.h>
-#include <string.h>
-#include <limits.h>
-
-#ifdef _WIN32
-#pragma warning(disable : 4018 26451)
-#endif
-
-#ifndef ULLONG_MAX
-# define ULLONG_MAX ((uint64_t) -1) /* 2^64-1 */
-#endif
-
-#ifndef MIN
-# define MIN(a,b) ((a) < (b) ? (a) : (b))
-#endif
-
-#ifndef ARRAY_SIZE
-# define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
-#endif
-
-#ifndef BIT_AT
-# define BIT_AT(a, i) \
- (!!((unsigned int) (a)[(unsigned int) (i) >> 3] & \
- (1 << ((unsigned int) (i) & 7))))
-#endif
-
-#ifndef ELEM_AT
-# define ELEM_AT(a, i, v) ((unsigned int) (i) < ARRAY_SIZE(a) ? (a)[(i)] : (v))
-#endif
-
-#define SET_ERRNO(e) \
-do { \
- parser->nread = nread; \
- parser->http_errno = (e); \
-} while(0)
-
-#define CURRENT_STATE() p_state
-#define UPDATE_STATE(V) p_state = (enum state) (V);
-#define RETURN(V) \
-do { \
- parser->nread = nread; \
- parser->state = CURRENT_STATE(); \
- return (V); \
-} while (0);
-#define REEXECUTE() \
- goto reexecute; \
-
-
-#ifdef __GNUC__
-# define LIKELY(X) __builtin_expect(!!(X), 1)
-# define UNLIKELY(X) __builtin_expect(!!(X), 0)
-#else
-# define LIKELY(X) (X)
-# define UNLIKELY(X) (X)
-#endif
-
-
-/* Run the notify callback FOR, returning ER if it fails */
-#define CALLBACK_NOTIFY_(FOR, ER) \
-do { \
- assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \
- \
- if (LIKELY(settings->on_##FOR)) { \
- parser->state = CURRENT_STATE(); \
- if (UNLIKELY(0 != settings->on_##FOR(parser))) { \
- SET_ERRNO(HPE_CB_##FOR); \
- } \
- UPDATE_STATE(parser->state); \
- \
- /* We either errored above or got paused; get out */ \
- if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) { \
- return (ER); \
- } \
- } \
-} while (0)
-
-/* Run the notify callback FOR and consume the current byte */
-#define CALLBACK_NOTIFY(FOR) CALLBACK_NOTIFY_(FOR, p - data + 1)
-
-/* Run the notify callback FOR and don't consume the current byte */
-#define CALLBACK_NOTIFY_NOADVANCE(FOR) CALLBACK_NOTIFY_(FOR, p - data)
-
-/* Run data callback FOR with LEN bytes, returning ER if it fails */
-#define CALLBACK_DATA_(FOR, LEN, ER) \
-do { \
- assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \
- \
- if (FOR##_mark) { \
- if (LIKELY(settings->on_##FOR)) { \
- parser->state = CURRENT_STATE(); \
- if (UNLIKELY(0 != \
- settings->on_##FOR(parser, FOR##_mark, (LEN)))) { \
- SET_ERRNO(HPE_CB_##FOR); \
- } \
- UPDATE_STATE(parser->state); \
- \
- /* We either errored above or got paused; get out */ \
- if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) { \
- return (ER); \
- } \
- } \
- FOR##_mark = NULL; \
- } \
-} while (0)
-
-/* Run the data callback FOR and consume the current byte */
-#define CALLBACK_DATA(FOR) \
- CALLBACK_DATA_(FOR, p - FOR##_mark, p - data + 1)
-
-/* Run the data callback FOR and don't consume the current byte */
-#define CALLBACK_DATA_NOADVANCE(FOR) \
- CALLBACK_DATA_(FOR, p - FOR##_mark, p - data)
-
-/* Set the mark FOR; non-destructive if mark is already set */
-#define MARK(FOR) \
-do { \
- if (!FOR##_mark) { \
- FOR##_mark = p; \
- } \
-} while (0)
-
-/* Don't allow the total size of the HTTP headers (including the status
- * line) to exceed HTTP_MAX_HEADER_SIZE. This check is here to protect
- * embedders against denial-of-service attacks where the attacker feeds
- * us a never-ending header that the embedder keeps buffering.
- *
- * This check is arguably the responsibility of embedders but we're doing
- * it on the embedder's behalf because most won't bother and this way we
- * make the web a little safer. HTTP_MAX_HEADER_SIZE is still far bigger
- * than any reasonable request or response so this should never affect
- * day-to-day operation.
- */
-#define COUNT_HEADER_SIZE(V) \
-do { \
- nread += (V); \
- if (UNLIKELY(nread > (HTTP_MAX_HEADER_SIZE))) { \
- SET_ERRNO(HPE_HEADER_OVERFLOW); \
- goto error; \
- } \
-} while (0)
-
-
-#define PROXY_CONNECTION "proxy-connection"
-#define CONNECTION "connection"
-#define CONTENT_LENGTH "content-length"
-#define TRANSFER_ENCODING "transfer-encoding"
-#define UPGRADE "upgrade"
-#define CHUNKED "chunked"
-#define KEEP_ALIVE "keep-alive"
-#define CLOSE "close"
-
-namespace wpi {
-
-
-static const char *method_strings[] =
- {
-#define XX(num, name, string) #string,
- HTTP_METHOD_MAP(XX)
-#undef XX
- };
-
-
-/* Tokens as defined by rfc 2616. Also lowercases them.
- * token = 1*<any CHAR except CTLs or separators>
- * separators = "(" | ")" | "<" | ">" | "@"
- * | "," | ";" | ":" | "\" | <">
- * | "/" | "[" | "]" | "?" | "="
- * | "{" | "}" | SP | HT
- */
-static const char tokens[256] = {
-/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */
- 0, 0, 0, 0, 0, 0, 0, 0,
-/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */
- 0, 0, 0, 0, 0, 0, 0, 0,
-/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */
- 0, 0, 0, 0, 0, 0, 0, 0,
-/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */
- 0, 0, 0, 0, 0, 0, 0, 0,
-/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */
- ' ', '!', 0, '#', '$', '%', '&', '\'',
-/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */
- 0, 0, '*', '+', 0, '-', '.', 0,
-/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */
- '0', '1', '2', '3', '4', '5', '6', '7',
-/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */
- '8', '9', 0, 0, 0, 0, 0, 0,
-/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */
- 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g',
-/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */
- 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
-/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */
- 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
-/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */
- 'x', 'y', 'z', 0, 0, 0, '^', '_',
-/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */
- '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
-/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */
- 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
-/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */
- 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
-/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */
- 'x', 'y', 'z', 0, '|', 0, '~', 0 };
-
-
-static const int8_t unhex[256] =
- {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
- ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
- ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
- , 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1
- ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
- ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
- ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
- ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
- };
-
-
-#if HTTP_PARSER_STRICT
-# define T(v) 0
-#else
-# define T(v) v
-#endif
-
-
-static const uint8_t normal_url_char[32] = {
-/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */
- 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
-/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */
- 0 | T(2) | 0 | 0 | T(16) | 0 | 0 | 0,
-/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */
- 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
-/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */
- 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
-/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */
- 0 | 2 | 4 | 0 | 16 | 32 | 64 | 128,
-/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */
- 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
-/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */
- 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
-/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */
- 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0,
-/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */
- 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
-/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */
- 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
-/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */
- 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
-/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */
- 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
-/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */
- 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
-/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */
- 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
-/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */
- 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
-/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */
- 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, };
-
-#undef T
-
-enum state
- { s_dead = 1 /* important that this is > 0 */
-
- , s_start_req_or_res
- , s_res_or_resp_H
- , s_start_res
- , s_res_H
- , s_res_HT
- , s_res_HTT
- , s_res_HTTP
- , s_res_http_major
- , s_res_http_dot
- , s_res_http_minor
- , s_res_http_end
- , s_res_first_status_code
- , s_res_status_code
- , s_res_status_start
- , s_res_status
- , s_res_line_almost_done
-
- , s_start_req
-
- , s_req_method
- , s_req_spaces_before_url
- , s_req_schema
- , s_req_schema_slash
- , s_req_schema_slash_slash
- , s_req_server_start
- , s_req_server
- , s_req_server_with_at
- , s_req_path
- , s_req_query_string_start
- , s_req_query_string
- , s_req_fragment_start
- , s_req_fragment
- , s_req_http_start
- , s_req_http_H
- , s_req_http_HT
- , s_req_http_HTT
- , s_req_http_HTTP
- , s_req_http_major
- , s_req_http_dot
- , s_req_http_minor
- , s_req_http_end
- , s_req_line_almost_done
-
- , s_header_field_start
- , s_header_field
- , s_header_value_discard_ws
- , s_header_value_discard_ws_almost_done
- , s_header_value_discard_lws
- , s_header_value_start
- , s_header_value
- , s_header_value_lws
-
- , s_header_almost_done
-
- , s_chunk_size_start
- , s_chunk_size
- , s_chunk_parameters
- , s_chunk_size_almost_done
-
- , s_headers_almost_done
- , s_headers_done
-
- /* Important: 's_headers_done' must be the last 'header' state. All
- * states beyond this must be 'body' states. It is used for overflow
- * checking. See the PARSING_HEADER() macro.
- */
-
- , s_chunk_data
- , s_chunk_data_almost_done
- , s_chunk_data_done
-
- , s_body_identity
- , s_body_identity_eof
-
- , s_message_done
- };
-
-
-#define PARSING_HEADER(state) (state <= s_headers_done)
-
-
-enum header_states
- { h_general = 0
- , h_C
- , h_CO
- , h_CON
-
- , h_matching_connection
- , h_matching_proxy_connection
- , h_matching_content_length
- , h_matching_transfer_encoding
- , h_matching_upgrade
-
- , h_connection
- , h_content_length
- , h_content_length_num
- , h_content_length_ws
- , h_transfer_encoding
- , h_upgrade
-
- , h_matching_transfer_encoding_chunked
- , h_matching_connection_token_start
- , h_matching_connection_keep_alive
- , h_matching_connection_close
- , h_matching_connection_upgrade
- , h_matching_connection_token
-
- , h_transfer_encoding_chunked
- , h_connection_keep_alive
- , h_connection_close
- , h_connection_upgrade
- };
-
-enum http_host_state
- {
- s_http_host_dead = 1
- , s_http_userinfo_start
- , s_http_userinfo
- , s_http_host_start
- , s_http_host_v6_start
- , s_http_host
- , s_http_host_v6
- , s_http_host_v6_end
- , s_http_host_v6_zone_start
- , s_http_host_v6_zone
- , s_http_host_port_start
- , s_http_host_port
-};
-
-/* Macros for character classes; depends on strict-mode */
-#define CR '\r'
-#define LF '\n'
-#define LOWER(c) (unsigned char)(c | 0x20)
-#define IS_ALPHA(c) (LOWER(c) >= 'a' && LOWER(c) <= 'z')
-#define IS_NUM(c) ((c) >= '0' && (c) <= '9')
-#define IS_ALPHANUM(c) (IS_ALPHA(c) || IS_NUM(c))
-#define IS_HEX(c) (IS_NUM(c) || (LOWER(c) >= 'a' && LOWER(c) <= 'f'))
-#define IS_MARK(c) ((c) == '-' || (c) == '_' || (c) == '.' || \
- (c) == '!' || (c) == '~' || (c) == '*' || (c) == '\'' || (c) == '(' || \
- (c) == ')')
-#define IS_USERINFO_CHAR(c) (IS_ALPHANUM(c) || IS_MARK(c) || (c) == '%' || \
- (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \
- (c) == '$' || (c) == ',')
-
-#define STRICT_TOKEN(c) ((c == ' ') ? 0 : tokens[(unsigned char)c])
-
-#if HTTP_PARSER_STRICT
-#define TOKEN(c) STRICT_TOKEN(c)
-#define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c))
-#define IS_HOST_CHAR(c) (IS_ALPHANUM(c) || (c) == '.' || (c) == '-')
-#else
-#define TOKEN(c) tokens[(unsigned char)c]
-#define IS_URL_CHAR(c) \
- (BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80))
-#define IS_HOST_CHAR(c) \
- (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_')
-#endif
-
-/**
- * Verify that a char is a valid visible (printable) US-ASCII
- * character or %x80-FF
- **/
-#define IS_HEADER_CHAR(ch) \
- (ch == CR || ch == LF || ch == 9 || ((unsigned char)ch > 31 && ch != 127))
-
-#define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res)
-
-
-#if HTTP_PARSER_STRICT
-# define STRICT_CHECK(cond) \
-do { \
- if (cond) { \
- SET_ERRNO(HPE_STRICT); \
- goto error; \
- } \
-} while (0)
-# define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead)
-#else
-# define STRICT_CHECK(cond)
-# define NEW_MESSAGE() start_state
-#endif
-
-
-/* Map errno values to strings for human-readable output */
-#define HTTP_STRERROR_GEN(n, s) { "HPE_" #n, s },
-static struct {
- const char *name;
- const char *description;
-} http_strerror_tab[] = {
- HTTP_ERRNO_MAP(HTTP_STRERROR_GEN)
-};
-#undef HTTP_STRERROR_GEN
-
-int http_message_needs_eof(const http_parser *parser);
-
-/* Our URL parser.
- *
- * This is designed to be shared by http_parser_execute() for URL validation,
- * hence it has a state transition + byte-for-byte interface. In addition, it
- * is meant to be embedded in http_parser_parse_url(), which does the dirty
- * work of turning state transitions URL components for its API.
- *
- * This function should only be invoked with non-space characters. It is
- * assumed that the caller cares about (and can detect) the transition between
- * URL and non-URL states by looking for these.
- */
-static enum state
-parse_url_char(enum state s, const char ch)
-{
- if (ch == ' ' || ch == '\r' || ch == '\n') {
- return s_dead;
- }
-
-#if HTTP_PARSER_STRICT
- if (ch == '\t' || ch == '\f') {
- return s_dead;
- }
-#endif
-
- switch (s) {
- case s_req_spaces_before_url:
- /* Proxied requests are followed by scheme of an absolute URI (alpha).
- * All methods except CONNECT are followed by '/' or '*'.
- */
-
- if (ch == '/' || ch == '*') {
- return s_req_path;
- }
-
- if (IS_ALPHA(ch)) {
- return s_req_schema;
- }
-
- break;
-
- case s_req_schema:
- if (IS_ALPHA(ch)) {
- return s;
- }
-
- if (ch == ':') {
- return s_req_schema_slash;
- }
-
- break;
-
- case s_req_schema_slash:
- if (ch == '/') {
- return s_req_schema_slash_slash;
- }
-
- break;
-
- case s_req_schema_slash_slash:
- if (ch == '/') {
- return s_req_server_start;
- }
-
- break;
-
- case s_req_server_with_at:
- if (ch == '@') {
- return s_dead;
- }
-
- /* fall through */
- case s_req_server_start:
- case s_req_server:
- if (ch == '/') {
- return s_req_path;
- }
-
- if (ch == '?') {
- return s_req_query_string_start;
- }
-
- if (ch == '@') {
- return s_req_server_with_at;
- }
-
- if (IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') {
- return s_req_server;
- }
-
- break;
-
- case s_req_path:
- if (IS_URL_CHAR(ch)) {
- return s;
- }
-
- switch (ch) {
- case '?':
- return s_req_query_string_start;
-
- case '#':
- return s_req_fragment_start;
- }
-
- break;
-
- case s_req_query_string_start:
- case s_req_query_string:
- if (IS_URL_CHAR(ch)) {
- return s_req_query_string;
- }
-
- switch (ch) {
- case '?':
- /* allow extra '?' in query string */
- return s_req_query_string;
-
- case '#':
- return s_req_fragment_start;
- }
-
- break;
-
- case s_req_fragment_start:
- if (IS_URL_CHAR(ch)) {
- return s_req_fragment;
- }
-
- switch (ch) {
- case '?':
- return s_req_fragment;
-
- case '#':
- return s;
- }
-
- break;
-
- case s_req_fragment:
- if (IS_URL_CHAR(ch)) {
- return s;
- }
-
- switch (ch) {
- case '?':
- case '#':
- return s;
- }
-
- break;
-
- default:
- break;
- }
-
- /* We should never fall out of the switch above unless there's an error */
- return s_dead;
-}
-
-size_t http_parser_execute (http_parser *parser,
- const http_parser_settings *settings,
- const char *data,
- size_t len)
-{
- char c, ch;
- int8_t unhex_val;
- const char *p = data;
- const char *header_field_mark = 0;
- const char *header_value_mark = 0;
- const char *url_mark = 0;
- const char *body_mark = 0;
- const char *status_mark = 0;
- enum state p_state = (enum state) parser->state;
- const unsigned int lenient = parser->lenient_http_headers;
- uint32_t nread = parser->nread;
-
- /* We're in an error state. Don't bother doing anything. */
- if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
- return 0;
- }
-
- if (len == 0) {
- switch (CURRENT_STATE()) {
- case s_body_identity_eof:
- /* Use of CALLBACK_NOTIFY() here would erroneously return 1 byte read if
- * we got paused.
- */
- CALLBACK_NOTIFY_NOADVANCE(message_complete);
- return 0;
-
- case s_dead:
- case s_start_req_or_res:
- case s_start_res:
- case s_start_req:
- return 0;
-
- default:
- SET_ERRNO(HPE_INVALID_EOF_STATE);
- return 1;
- }
- }
-
-
- if (CURRENT_STATE() == s_header_field)
- header_field_mark = data;
- if (CURRENT_STATE() == s_header_value)
- header_value_mark = data;
- switch (CURRENT_STATE()) {
- case s_req_path:
- case s_req_schema:
- case s_req_schema_slash:
- case s_req_schema_slash_slash:
- case s_req_server_start:
- case s_req_server:
- case s_req_server_with_at:
- case s_req_query_string_start:
- case s_req_query_string:
- case s_req_fragment_start:
- case s_req_fragment:
- url_mark = data;
- break;
- case s_res_status:
- status_mark = data;
- break;
- default:
- break;
- }
-
- for (p=data; p != data + len; p++) {
- ch = *p;
-
- if (PARSING_HEADER(CURRENT_STATE()))
- COUNT_HEADER_SIZE(1);
-
-reexecute:
- switch (CURRENT_STATE()) {
-
- case s_dead:
- /* this state is used after a 'Connection: close' message
- * the parser will error out if it reads another message
- */
- if (LIKELY(ch == CR || ch == LF))
- break;
-
- SET_ERRNO(HPE_CLOSED_CONNECTION);
- goto error;
-
- case s_start_req_or_res:
- {
- if (ch == CR || ch == LF)
- break;
- parser->flags = 0;
- parser->content_length = ULLONG_MAX;
-
- if (ch == 'H') {
- UPDATE_STATE(s_res_or_resp_H);
-
- CALLBACK_NOTIFY(message_begin);
- } else {
- parser->type = HTTP_REQUEST;
- UPDATE_STATE(s_start_req);
- REEXECUTE();
- }
-
- break;
- }
-
- case s_res_or_resp_H:
- if (ch == 'T') {
- parser->type = HTTP_RESPONSE;
- UPDATE_STATE(s_res_HT);
- } else {
- if (UNLIKELY(ch != 'E')) {
- SET_ERRNO(HPE_INVALID_CONSTANT);
- goto error;
- }
-
- parser->type = HTTP_REQUEST;
- parser->method = HTTP_HEAD;
- parser->index = 2;
- UPDATE_STATE(s_req_method);
- }
- break;
-
- case s_start_res:
- {
- parser->flags = 0;
- parser->content_length = ULLONG_MAX;
-
- switch (ch) {
- case 'H':
- UPDATE_STATE(s_res_H);
- break;
-
- case CR:
- case LF:
- break;
-
- default:
- SET_ERRNO(HPE_INVALID_CONSTANT);
- goto error;
- }
-
- CALLBACK_NOTIFY(message_begin);
- break;
- }
-
- case s_res_H:
- STRICT_CHECK(ch != 'T');
- UPDATE_STATE(s_res_HT);
- break;
-
- case s_res_HT:
- STRICT_CHECK(ch != 'T');
- UPDATE_STATE(s_res_HTT);
- break;
-
- case s_res_HTT:
- STRICT_CHECK(ch != 'P');
- UPDATE_STATE(s_res_HTTP);
- break;
-
- case s_res_HTTP:
- STRICT_CHECK(ch != '/');
- UPDATE_STATE(s_res_http_major);
- break;
-
- case s_res_http_major:
- if (UNLIKELY(!IS_NUM(ch))) {
- SET_ERRNO(HPE_INVALID_VERSION);
- goto error;
- }
-
- parser->http_major = ch - '0';
- UPDATE_STATE(s_res_http_dot);
- break;
-
- case s_res_http_dot:
- {
- if (UNLIKELY(ch != '.')) {
- SET_ERRNO(HPE_INVALID_VERSION);
- goto error;
- }
-
- UPDATE_STATE(s_res_http_minor);
- break;
- }
-
- case s_res_http_minor:
- if (UNLIKELY(!IS_NUM(ch))) {
- SET_ERRNO(HPE_INVALID_VERSION);
- goto error;
- }
-
- parser->http_minor = ch - '0';
- UPDATE_STATE(s_res_http_end);
- break;
-
- case s_res_http_end:
- {
- if (UNLIKELY(ch != ' ')) {
- SET_ERRNO(HPE_INVALID_VERSION);
- goto error;
- }
-
- UPDATE_STATE(s_res_first_status_code);
- break;
- }
-
- case s_res_first_status_code:
- {
- if (!IS_NUM(ch)) {
- if (ch == ' ') {
- break;
- }
-
- SET_ERRNO(HPE_INVALID_STATUS);
- goto error;
- }
- parser->status_code = ch - '0';
- UPDATE_STATE(s_res_status_code);
- break;
- }
-
- case s_res_status_code:
- {
- if (!IS_NUM(ch)) {
- switch (ch) {
- case ' ':
- UPDATE_STATE(s_res_status_start);
- break;
- case CR:
- case LF:
- UPDATE_STATE(s_res_status_start);
- REEXECUTE();
- break;
- default:
- SET_ERRNO(HPE_INVALID_STATUS);
- goto error;
- }
- break;
- }
-
- parser->status_code *= 10;
- parser->status_code += ch - '0';
-
- if (UNLIKELY(parser->status_code > 999)) {
- SET_ERRNO(HPE_INVALID_STATUS);
- goto error;
- }
-
- break;
- }
-
- case s_res_status_start:
- {
- MARK(status);
- UPDATE_STATE(s_res_status);
- parser->index = 0;
-
- if (ch == CR || ch == LF)
- REEXECUTE();
-
- break;
- }
-
- case s_res_status:
- if (ch == CR) {
- UPDATE_STATE(s_res_line_almost_done);
- CALLBACK_DATA(status);
- break;
- }
-
- if (ch == LF) {
- UPDATE_STATE(s_header_field_start);
- CALLBACK_DATA(status);
- break;
- }
-
- break;
-
- case s_res_line_almost_done:
- STRICT_CHECK(ch != LF);
- UPDATE_STATE(s_header_field_start);
- break;
-
- case s_start_req:
- {
- if (ch == CR || ch == LF)
- break;
- parser->flags = 0;
- parser->content_length = ULLONG_MAX;
-
- if (UNLIKELY(!IS_ALPHA(ch))) {
- SET_ERRNO(HPE_INVALID_METHOD);
- goto error;
- }
-
- parser->method = (enum http_method) 0;
- parser->index = 1;
- switch (ch) {
- case 'A': parser->method = HTTP_ACL; break;
- case 'B': parser->method = HTTP_BIND; break;
- case 'C': parser->method = HTTP_CONNECT; /* or COPY, CHECKOUT */ break;
- case 'D': parser->method = HTTP_DELETE; break;
- case 'G': parser->method = HTTP_GET; break;
- case 'H': parser->method = HTTP_HEAD; break;
- case 'L': parser->method = HTTP_LOCK; /* or LINK */ break;
- case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH, MKCALENDAR */ break;
- case 'N': parser->method = HTTP_NOTIFY; break;
- case 'O': parser->method = HTTP_OPTIONS; break;
- case 'P': parser->method = HTTP_POST;
- /* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */
- break;
- case 'R': parser->method = HTTP_REPORT; /* or REBIND */ break;
- case 'S': parser->method = HTTP_SUBSCRIBE; /* or SEARCH, SOURCE */ break;
- case 'T': parser->method = HTTP_TRACE; break;
- case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE, UNBIND, UNLINK */ break;
- default:
- SET_ERRNO(HPE_INVALID_METHOD);
- goto error;
- }
- UPDATE_STATE(s_req_method);
-
- CALLBACK_NOTIFY(message_begin);
-
- break;
- }
-
- case s_req_method:
- {
- const char *matcher;
- if (UNLIKELY(ch == '\0')) {
- SET_ERRNO(HPE_INVALID_METHOD);
- goto error;
- }
-
- matcher = method_strings[parser->method];
- if (ch == ' ' && matcher[parser->index] == '\0') {
- UPDATE_STATE(s_req_spaces_before_url);
- } else if (ch == matcher[parser->index]) {
- ; /* nada */
- } else if ((ch >= 'A' && ch <= 'Z') || ch == '-') {
-
- switch (parser->method << 16 | parser->index << 8 | ch) {
-#define XX(meth, pos, ch, new_meth) \
- case (HTTP_##meth << 16 | pos << 8 | ch): \
- parser->method = HTTP_##new_meth; break;
-
- XX(POST, 1, 'U', PUT)
- XX(POST, 1, 'A', PATCH)
- XX(POST, 1, 'R', PROPFIND)
- XX(PUT, 2, 'R', PURGE)
- XX(CONNECT, 1, 'H', CHECKOUT)
- XX(CONNECT, 2, 'P', COPY)
- XX(MKCOL, 1, 'O', MOVE)
- XX(MKCOL, 1, 'E', MERGE)
- XX(MKCOL, 1, '-', MSEARCH)
- XX(MKCOL, 2, 'A', MKACTIVITY)
- XX(MKCOL, 3, 'A', MKCALENDAR)
- XX(SUBSCRIBE, 1, 'E', SEARCH)
- XX(SUBSCRIBE, 1, 'O', SOURCE)
- XX(REPORT, 2, 'B', REBIND)
- XX(PROPFIND, 4, 'P', PROPPATCH)
- XX(LOCK, 1, 'I', LINK)
- XX(UNLOCK, 2, 'S', UNSUBSCRIBE)
- XX(UNLOCK, 2, 'B', UNBIND)
- XX(UNLOCK, 3, 'I', UNLINK)
-#undef XX
- default:
- SET_ERRNO(HPE_INVALID_METHOD);
- goto error;
- }
- } else {
- SET_ERRNO(HPE_INVALID_METHOD);
- goto error;
- }
-
- ++parser->index;
- break;
- }
-
- case s_req_spaces_before_url:
- {
- if (ch == ' ') break;
-
- MARK(url);
- if (parser->method == HTTP_CONNECT) {
- UPDATE_STATE(s_req_server_start);
- }
-
- UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));
- if (UNLIKELY(CURRENT_STATE() == s_dead)) {
- SET_ERRNO(HPE_INVALID_URL);
- goto error;
- }
-
- break;
- }
-
- case s_req_schema:
- case s_req_schema_slash:
- case s_req_schema_slash_slash:
- case s_req_server_start:
- {
- switch (ch) {
- /* No whitespace allowed here */
- case ' ':
- case CR:
- case LF:
- SET_ERRNO(HPE_INVALID_URL);
- goto error;
- default:
- UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));
- if (UNLIKELY(CURRENT_STATE() == s_dead)) {
- SET_ERRNO(HPE_INVALID_URL);
- goto error;
- }
- }
-
- break;
- }
-
- case s_req_server:
- case s_req_server_with_at:
- case s_req_path:
- case s_req_query_string_start:
- case s_req_query_string:
- case s_req_fragment_start:
- case s_req_fragment:
- {
- switch (ch) {
- case ' ':
- UPDATE_STATE(s_req_http_start);
- CALLBACK_DATA(url);
- break;
- case CR:
- case LF:
- parser->http_major = 0;
- parser->http_minor = 9;
- UPDATE_STATE((ch == CR) ?
- s_req_line_almost_done :
- s_header_field_start);
- CALLBACK_DATA(url);
- break;
- default:
- UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));
- if (UNLIKELY(CURRENT_STATE() == s_dead)) {
- SET_ERRNO(HPE_INVALID_URL);
- goto error;
- }
- }
- break;
- }
-
- case s_req_http_start:
- switch (ch) {
- case 'H':
- UPDATE_STATE(s_req_http_H);
- break;
- case ' ':
- break;
- default:
- SET_ERRNO(HPE_INVALID_CONSTANT);
- goto error;
- }
- break;
-
- case s_req_http_H:
- STRICT_CHECK(ch != 'T');
- UPDATE_STATE(s_req_http_HT);
- break;
-
- case s_req_http_HT:
- STRICT_CHECK(ch != 'T');
- UPDATE_STATE(s_req_http_HTT);
- break;
-
- case s_req_http_HTT:
- STRICT_CHECK(ch != 'P');
- UPDATE_STATE(s_req_http_HTTP);
- break;
-
- case s_req_http_HTTP:
- STRICT_CHECK(ch != '/');
- UPDATE_STATE(s_req_http_major);
- break;
-
- case s_req_http_major:
- if (UNLIKELY(!IS_NUM(ch))) {
- SET_ERRNO(HPE_INVALID_VERSION);
- goto error;
- }
-
- parser->http_major = ch - '0';
- UPDATE_STATE(s_req_http_dot);
- break;
-
- case s_req_http_dot:
- {
- if (UNLIKELY(ch != '.')) {
- SET_ERRNO(HPE_INVALID_VERSION);
- goto error;
- }
-
- UPDATE_STATE(s_req_http_minor);
- break;
- }
-
- case s_req_http_minor:
- if (UNLIKELY(!IS_NUM(ch))) {
- SET_ERRNO(HPE_INVALID_VERSION);
- goto error;
- }
-
- parser->http_minor = ch - '0';
- UPDATE_STATE(s_req_http_end);
- break;
-
- case s_req_http_end:
- {
- if (ch == CR) {
- UPDATE_STATE(s_req_line_almost_done);
- break;
- }
-
- if (ch == LF) {
- UPDATE_STATE(s_header_field_start);
- break;
- }
-
- SET_ERRNO(HPE_INVALID_VERSION);
- goto error;
- break;
- }
-
- /* end of request line */
- case s_req_line_almost_done:
- {
- if (UNLIKELY(ch != LF)) {
- SET_ERRNO(HPE_LF_EXPECTED);
- goto error;
- }
-
- UPDATE_STATE(s_header_field_start);
- break;
- }
-
- case s_header_field_start:
- {
- if (ch == CR) {
- UPDATE_STATE(s_headers_almost_done);
- break;
- }
-
- if (ch == LF) {
- /* they might be just sending \n instead of \r\n so this would be
- * the second \n to denote the end of headers*/
- UPDATE_STATE(s_headers_almost_done);
- REEXECUTE();
- }
-
- c = TOKEN(ch);
-
- if (UNLIKELY(!c)) {
- SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
- goto error;
- }
-
- MARK(header_field);
-
- parser->index = 0;
- UPDATE_STATE(s_header_field);
-
- switch (c) {
- case 'c':
- parser->header_state = h_C;
- break;
-
- case 'p':
- parser->header_state = h_matching_proxy_connection;
- break;
-
- case 't':
- parser->header_state = h_matching_transfer_encoding;
- break;
-
- case 'u':
- parser->header_state = h_matching_upgrade;
- break;
-
- default:
- parser->header_state = h_general;
- break;
- }
- break;
- }
-
- case s_header_field:
- {
- const char* start = p;
- for (; p != data + len; p++) {
- ch = *p;
- c = TOKEN(ch);
-
- if (!c)
- break;
-
- switch (parser->header_state) {
- case h_general: {
- size_t limit = data + len - p;
- limit = MIN(limit, HTTP_MAX_HEADER_SIZE);
- while (p+1 < data + limit && TOKEN(p[1])) {
- p++;
- }
- break;
- }
-
- case h_C:
- parser->index++;
- parser->header_state = (c == 'o' ? h_CO : h_general);
- break;
-
- case h_CO:
- parser->index++;
- parser->header_state = (c == 'n' ? h_CON : h_general);
- break;
-
- case h_CON:
- parser->index++;
- switch (c) {
- case 'n':
- parser->header_state = h_matching_connection;
- break;
- case 't':
- parser->header_state = h_matching_content_length;
- break;
- default:
- parser->header_state = h_general;
- break;
- }
- break;
-
- /* connection */
-
- case h_matching_connection:
- parser->index++;
- if (parser->index > sizeof(CONNECTION)-1
- || c != CONNECTION[parser->index]) {
- parser->header_state = h_general;
- } else if (parser->index == sizeof(CONNECTION)-2) {
- parser->header_state = h_connection;
- }
- break;
-
- /* proxy-connection */
-
- case h_matching_proxy_connection:
- parser->index++;
- if (parser->index > sizeof(PROXY_CONNECTION)-1
- || c != PROXY_CONNECTION[parser->index]) {
- parser->header_state = h_general;
- } else if (parser->index == sizeof(PROXY_CONNECTION)-2) {
- parser->header_state = h_connection;
- }
- break;
-
- /* content-length */
-
- case h_matching_content_length:
- parser->index++;
- if (parser->index > sizeof(CONTENT_LENGTH)-1
- || c != CONTENT_LENGTH[parser->index]) {
- parser->header_state = h_general;
- } else if (parser->index == sizeof(CONTENT_LENGTH)-2) {
- parser->header_state = h_content_length;
- }
- break;
-
- /* transfer-encoding */
-
- case h_matching_transfer_encoding:
- parser->index++;
- if (parser->index > sizeof(TRANSFER_ENCODING)-1
- || c != TRANSFER_ENCODING[parser->index]) {
- parser->header_state = h_general;
- } else if (parser->index == sizeof(TRANSFER_ENCODING)-2) {
- parser->header_state = h_transfer_encoding;
- }
- break;
-
- /* upgrade */
-
- case h_matching_upgrade:
- parser->index++;
- if (parser->index > sizeof(UPGRADE)-1
- || c != UPGRADE[parser->index]) {
- parser->header_state = h_general;
- } else if (parser->index == sizeof(UPGRADE)-2) {
- parser->header_state = h_upgrade;
- }
- break;
-
- case h_connection:
- case h_content_length:
- case h_transfer_encoding:
- case h_upgrade:
- if (ch != ' ') parser->header_state = h_general;
- break;
-
- default:
- assert(0 && "Unknown header_state");
- break;
- }
- }
-
- if (p == data + len) {
- --p;
- COUNT_HEADER_SIZE(p - start);
- break;
- }
-
- COUNT_HEADER_SIZE(p - start);
-
- if (ch == ':') {
- UPDATE_STATE(s_header_value_discard_ws);
- CALLBACK_DATA(header_field);
- break;
- }
-
- SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
- goto error;
- }
-
- case s_header_value_discard_ws:
- if (ch == ' ' || ch == '\t') break;
-
- if (ch == CR) {
- UPDATE_STATE(s_header_value_discard_ws_almost_done);
- break;
- }
-
- if (ch == LF) {
- UPDATE_STATE(s_header_value_discard_lws);
- break;
- }
-
- /* fall through */
-
- case s_header_value_start:
- {
- MARK(header_value);
-
- UPDATE_STATE(s_header_value);
- parser->index = 0;
-
- c = LOWER(ch);
-
- switch (parser->header_state) {
- case h_upgrade:
- parser->flags |= F_UPGRADE;
- parser->header_state = h_general;
- break;
-
- case h_transfer_encoding:
- /* looking for 'Transfer-Encoding: chunked' */
- if ('c' == c) {
- parser->header_state = h_matching_transfer_encoding_chunked;
- } else {
- parser->header_state = h_general;
- }
- break;
-
- case h_content_length:
- if (UNLIKELY(!IS_NUM(ch))) {
- SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
- goto error;
- }
-
- if (parser->flags & F_CONTENTLENGTH) {
- SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);
- goto error;
- }
-
- parser->flags |= F_CONTENTLENGTH;
- parser->content_length = ch - '0';
- parser->header_state = h_content_length_num;
- break;
-
- case h_connection:
- /* looking for 'Connection: keep-alive' */
- if (c == 'k') {
- parser->header_state = h_matching_connection_keep_alive;
- /* looking for 'Connection: close' */
- } else if (c == 'c') {
- parser->header_state = h_matching_connection_close;
- } else if (c == 'u') {
- parser->header_state = h_matching_connection_upgrade;
- } else {
- parser->header_state = h_matching_connection_token;
- }
- break;
-
- /* Multi-value `Connection` header */
- case h_matching_connection_token_start:
- break;
-
- default:
- parser->header_state = h_general;
- break;
- }
- break;
- }
-
- case s_header_value:
- {
- const char* start = p;
- enum header_states h_state = (enum header_states) parser->header_state;
- for (; p != data + len; p++) {
- ch = *p;
- if (ch == CR) {
- UPDATE_STATE(s_header_almost_done);
- parser->header_state = h_state;
- CALLBACK_DATA(header_value);
- break;
- }
-
- if (ch == LF) {
- UPDATE_STATE(s_header_almost_done);
- COUNT_HEADER_SIZE(p - start);
- parser->header_state = h_state;
- CALLBACK_DATA_NOADVANCE(header_value);
- REEXECUTE();
- }
-
- if (!lenient && !IS_HEADER_CHAR(ch)) {
- SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
- goto error;
- }
-
- c = LOWER(ch);
-
- switch (h_state) {
- case h_general:
- {
- const char* p_cr;
- const char* p_lf;
- size_t limit = data + len - p;
-
- limit = MIN(limit, HTTP_MAX_HEADER_SIZE);
-
- p_cr = (const char*) memchr(p, CR, limit);
- p_lf = (const char*) memchr(p, LF, limit);
- if (p_cr != NULL) {
- if (p_lf != NULL && p_cr >= p_lf)
- p = p_lf;
- else
- p = p_cr;
- } else if (UNLIKELY(p_lf != NULL)) {
- p = p_lf;
- } else {
- p = data + len;
- }
- --p;
- break;
- }
-
- case h_connection:
- case h_transfer_encoding:
- assert(0 && "Shouldn't get here.");
- break;
-
- case h_content_length:
- if (ch == ' ') break;
- h_state = h_content_length_num;
- /* fall through */
-
- case h_content_length_num:
- {
- uint64_t t;
-
- if (ch == ' ') {
- h_state = h_content_length_ws;
- break;
- }
-
- if (UNLIKELY(!IS_NUM(ch))) {
- SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
- parser->header_state = h_state;
- goto error;
- }
-
- t = parser->content_length;
- t *= 10;
- t += ch - '0';
-
- /* Overflow? Test against a conservative limit for simplicity. */
- if (UNLIKELY((ULLONG_MAX - 10) / 10 < parser->content_length)) {
- SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
- parser->header_state = h_state;
- goto error;
- }
-
- parser->content_length = t;
- break;
- }
-
- case h_content_length_ws:
- if (ch == ' ') break;
- SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
- parser->header_state = h_state;
- goto error;
-
- /* Transfer-Encoding: chunked */
- case h_matching_transfer_encoding_chunked:
- parser->index++;
- if (parser->index > sizeof(CHUNKED)-1
- || c != CHUNKED[parser->index]) {
- h_state = h_general;
- } else if (parser->index == sizeof(CHUNKED)-2) {
- h_state = h_transfer_encoding_chunked;
- }
- break;
-
- case h_matching_connection_token_start:
- /* looking for 'Connection: keep-alive' */
- if (c == 'k') {
- h_state = h_matching_connection_keep_alive;
- /* looking for 'Connection: close' */
- } else if (c == 'c') {
- h_state = h_matching_connection_close;
- } else if (c == 'u') {
- h_state = h_matching_connection_upgrade;
- } else if (STRICT_TOKEN(c)) {
- h_state = h_matching_connection_token;
- } else if (c == ' ' || c == '\t') {
- /* Skip lws */
- } else {
- h_state = h_general;
- }
- break;
-
- /* looking for 'Connection: keep-alive' */
- case h_matching_connection_keep_alive:
- parser->index++;
- if (parser->index > sizeof(KEEP_ALIVE)-1
- || c != KEEP_ALIVE[parser->index]) {
- h_state = h_matching_connection_token;
- } else if (parser->index == sizeof(KEEP_ALIVE)-2) {
- h_state = h_connection_keep_alive;
- }
- break;
-
- /* looking for 'Connection: close' */
- case h_matching_connection_close:
- parser->index++;
- if (parser->index > sizeof(CLOSE)-1 || c != CLOSE[parser->index]) {
- h_state = h_matching_connection_token;
- } else if (parser->index == sizeof(CLOSE)-2) {
- h_state = h_connection_close;
- }
- break;
-
- /* looking for 'Connection: upgrade' */
- case h_matching_connection_upgrade:
- parser->index++;
- if (parser->index > sizeof(UPGRADE) - 1 ||
- c != UPGRADE[parser->index]) {
- h_state = h_matching_connection_token;
- } else if (parser->index == sizeof(UPGRADE)-2) {
- h_state = h_connection_upgrade;
- }
- break;
-
- case h_matching_connection_token:
- if (ch == ',') {
- h_state = h_matching_connection_token_start;
- parser->index = 0;
- }
- break;
-
- case h_transfer_encoding_chunked:
- if (ch != ' ') h_state = h_general;
- break;
-
- case h_connection_keep_alive:
- case h_connection_close:
- case h_connection_upgrade:
- if (ch == ',') {
- if (h_state == h_connection_keep_alive) {
- parser->flags |= F_CONNECTION_KEEP_ALIVE;
- } else if (h_state == h_connection_close) {
- parser->flags |= F_CONNECTION_CLOSE;
- } else if (h_state == h_connection_upgrade) {
- parser->flags |= F_CONNECTION_UPGRADE;
- }
- h_state = h_matching_connection_token_start;
- parser->index = 0;
- } else if (ch != ' ') {
- h_state = h_matching_connection_token;
- }
- break;
-
- default:
- UPDATE_STATE(s_header_value);
- h_state = h_general;
- break;
- }
- }
- parser->header_state = h_state;
-
- if (p == data + len)
- --p;
-
- COUNT_HEADER_SIZE(p - start);
- break;
- }
-
- case s_header_almost_done:
- {
- if (UNLIKELY(ch != LF)) {
- SET_ERRNO(HPE_LF_EXPECTED);
- goto error;
- }
-
- UPDATE_STATE(s_header_value_lws);
- break;
- }
-
- case s_header_value_lws:
- {
- if (ch == ' ' || ch == '\t') {
- UPDATE_STATE(s_header_value_start);
- REEXECUTE();
- }
-
- /* finished the header */
- switch (parser->header_state) {
- case h_connection_keep_alive:
- parser->flags |= F_CONNECTION_KEEP_ALIVE;
- break;
- case h_connection_close:
- parser->flags |= F_CONNECTION_CLOSE;
- break;
- case h_transfer_encoding_chunked:
- parser->flags |= F_CHUNKED;
- break;
- case h_connection_upgrade:
- parser->flags |= F_CONNECTION_UPGRADE;
- break;
- default:
- break;
- }
-
- UPDATE_STATE(s_header_field_start);
- REEXECUTE();
- }
-
- case s_header_value_discard_ws_almost_done:
- {
- STRICT_CHECK(ch != LF);
- UPDATE_STATE(s_header_value_discard_lws);
- break;
- }
-
- case s_header_value_discard_lws:
- {
- if (ch == ' ' || ch == '\t') {
- UPDATE_STATE(s_header_value_discard_ws);
- break;
- } else {
- switch (parser->header_state) {
- case h_connection_keep_alive:
- parser->flags |= F_CONNECTION_KEEP_ALIVE;
- break;
- case h_connection_close:
- parser->flags |= F_CONNECTION_CLOSE;
- break;
- case h_connection_upgrade:
- parser->flags |= F_CONNECTION_UPGRADE;
- break;
- case h_transfer_encoding_chunked:
- parser->flags |= F_CHUNKED;
- break;
- default:
- break;
- }
-
- /* header value was empty */
- MARK(header_value);
- UPDATE_STATE(s_header_field_start);
- CALLBACK_DATA_NOADVANCE(header_value);
- REEXECUTE();
- }
- }
-
- case s_headers_almost_done:
- {
- STRICT_CHECK(ch != LF);
-
- if (parser->flags & F_TRAILING) {
- /* End of a chunked request */
- UPDATE_STATE(s_message_done);
- CALLBACK_NOTIFY_NOADVANCE(chunk_complete);
- REEXECUTE();
- }
-
- /* Cannot use chunked encoding and a content-length header together
- per the HTTP specification. */
- if ((parser->flags & F_CHUNKED) &&
- (parser->flags & F_CONTENTLENGTH)) {
- SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);
- goto error;
- }
-
- UPDATE_STATE(s_headers_done);
-
- /* Set this here so that on_headers_complete() callbacks can see it */
- if ((parser->flags & F_UPGRADE) &&
- (parser->flags & F_CONNECTION_UPGRADE)) {
- /* For responses, "Upgrade: foo" and "Connection: upgrade" are
- * mandatory only when it is a 101 Switching Protocols response,
- * otherwise it is purely informational, to announce support.
- */
- parser->upgrade =
- (parser->type == HTTP_REQUEST || parser->status_code == 101);
- } else {
- parser->upgrade = (parser->method == HTTP_CONNECT);
- }
-
- /* Here we call the headers_complete callback. This is somewhat
- * different than other callbacks because if the user returns 1, we
- * will interpret that as saying that this message has no body. This
- * is needed for the annoying case of receiving a response to a HEAD
- * request.
- *
- * We'd like to use CALLBACK_NOTIFY_NOADVANCE() here but we cannot, so
- * we have to simulate it by handling a change in errno below.
- */
- if (settings->on_headers_complete) {
- switch (settings->on_headers_complete(parser)) {
- case 0:
- break;
-
- case 2:
- parser->upgrade = 1;
-
- /* fall through */
- case 1:
- parser->flags |= F_SKIPBODY;
- break;
-
- default:
- SET_ERRNO(HPE_CB_headers_complete);
- RETURN(p - data); /* Error */
- }
- }
-
- if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
- RETURN(p - data);
- }
-
- REEXECUTE();
- }
-
- case s_headers_done:
- {
- int hasBody;
- STRICT_CHECK(ch != LF);
-
- parser->nread = 0;
- nread = 0;
-
- hasBody = parser->flags & F_CHUNKED ||
- (parser->content_length > 0 && parser->content_length != ULLONG_MAX);
- if (parser->upgrade && (parser->method == HTTP_CONNECT ||
- (parser->flags & F_SKIPBODY) || !hasBody)) {
- /* Exit, the rest of the message is in a different protocol. */
- UPDATE_STATE(NEW_MESSAGE());
- CALLBACK_NOTIFY(message_complete);
- RETURN((p - data) + 1);
- }
-
- if (parser->flags & F_SKIPBODY) {
- UPDATE_STATE(NEW_MESSAGE());
- CALLBACK_NOTIFY(message_complete);
- } else if (parser->flags & F_CHUNKED) {
- /* chunked encoding - ignore Content-Length header */
- UPDATE_STATE(s_chunk_size_start);
- } else {
- if (parser->content_length == 0) {
- /* Content-Length header given but zero: Content-Length: 0\r\n */
- UPDATE_STATE(NEW_MESSAGE());
- CALLBACK_NOTIFY(message_complete);
- } else if (parser->content_length != ULLONG_MAX) {
- /* Content-Length header given and non-zero */
- UPDATE_STATE(s_body_identity);
- } else {
- if (!http_message_needs_eof(parser)) {
- /* Assume content-length 0 - read the next */
- UPDATE_STATE(NEW_MESSAGE());
- CALLBACK_NOTIFY(message_complete);
- } else {
- /* Read body until EOF */
- UPDATE_STATE(s_body_identity_eof);
- }
- }
- }
-
- break;
- }
-
- case s_body_identity:
- {
- uint64_t to_read = MIN(parser->content_length,
- (uint64_t) ((data + len) - p));
-
- assert(parser->content_length != 0
- && parser->content_length != ULLONG_MAX);
-
- /* The difference between advancing content_length and p is because
- * the latter will automaticaly advance on the next loop iteration.
- * Further, if content_length ends up at 0, we want to see the last
- * byte again for our message complete callback.
- */
- MARK(body);
- parser->content_length -= to_read;
- p += to_read - 1;
-
- if (parser->content_length == 0) {
- UPDATE_STATE(s_message_done);
-
- /* Mimic CALLBACK_DATA_NOADVANCE() but with one extra byte.
- *
- * The alternative to doing this is to wait for the next byte to
- * trigger the data callback, just as in every other case. The
- * problem with this is that this makes it difficult for the test
- * harness to distinguish between complete-on-EOF and
- * complete-on-length. It's not clear that this distinction is
- * important for applications, but let's keep it for now.
- */
- CALLBACK_DATA_(body, p - body_mark + 1, p - data);
- REEXECUTE();
- }
-
- break;
- }
-
- /* read until EOF */
- case s_body_identity_eof:
- MARK(body);
- p = data + len - 1;
-
- break;
-
- case s_message_done:
- UPDATE_STATE(NEW_MESSAGE());
- CALLBACK_NOTIFY(message_complete);
- if (parser->upgrade) {
- /* Exit, the rest of the message is in a different protocol. */
- RETURN((p - data) + 1);
- }
- break;
-
- case s_chunk_size_start:
- {
- assert(nread == 1);
- assert(parser->flags & F_CHUNKED);
-
- unhex_val = unhex[(unsigned char)ch];
- if (UNLIKELY(unhex_val == -1)) {
- SET_ERRNO(HPE_INVALID_CHUNK_SIZE);
- goto error;
- }
-
- parser->content_length = unhex_val;
- UPDATE_STATE(s_chunk_size);
- break;
- }
-
- case s_chunk_size:
- {
- uint64_t t;
-
- assert(parser->flags & F_CHUNKED);
-
- if (ch == CR) {
- UPDATE_STATE(s_chunk_size_almost_done);
- break;
- }
-
- unhex_val = unhex[(unsigned char)ch];
-
- if (unhex_val == -1) {
- if (ch == ';' || ch == ' ') {
- UPDATE_STATE(s_chunk_parameters);
- break;
- }
-
- SET_ERRNO(HPE_INVALID_CHUNK_SIZE);
- goto error;
- }
-
- t = parser->content_length;
- t *= 16;
- t += unhex_val;
-
- /* Overflow? Test against a conservative limit for simplicity. */
- if (UNLIKELY((ULLONG_MAX - 16) / 16 < parser->content_length)) {
- SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
- goto error;
- }
-
- parser->content_length = t;
- break;
- }
-
- case s_chunk_parameters:
- {
- assert(parser->flags & F_CHUNKED);
- /* just ignore this shit. TODO check for overflow */
- if (ch == CR) {
- UPDATE_STATE(s_chunk_size_almost_done);
- break;
- }
- break;
- }
-
- case s_chunk_size_almost_done:
- {
- assert(parser->flags & F_CHUNKED);
- STRICT_CHECK(ch != LF);
-
- parser->nread = 0;
- nread = 0;
-
- if (parser->content_length == 0) {
- parser->flags |= F_TRAILING;
- UPDATE_STATE(s_header_field_start);
- } else {
- UPDATE_STATE(s_chunk_data);
- }
- CALLBACK_NOTIFY(chunk_header);
- break;
- }
-
- case s_chunk_data:
- {
- uint64_t to_read = MIN(parser->content_length,
- (uint64_t) ((data + len) - p));
-
- assert(parser->flags & F_CHUNKED);
- assert(parser->content_length != 0
- && parser->content_length != ULLONG_MAX);
-
- /* See the explanation in s_body_identity for why the content
- * length and data pointers are managed this way.
- */
- MARK(body);
- parser->content_length -= to_read;
- p += to_read - 1;
-
- if (parser->content_length == 0) {
- UPDATE_STATE(s_chunk_data_almost_done);
- }
-
- break;
- }
-
- case s_chunk_data_almost_done:
- assert(parser->flags & F_CHUNKED);
- assert(parser->content_length == 0);
- STRICT_CHECK(ch != CR);
- UPDATE_STATE(s_chunk_data_done);
- CALLBACK_DATA(body);
- break;
-
- case s_chunk_data_done:
- assert(parser->flags & F_CHUNKED);
- STRICT_CHECK(ch != LF);
- parser->nread = 0;
- nread = 0;
- UPDATE_STATE(s_chunk_size_start);
- CALLBACK_NOTIFY(chunk_complete);
- break;
-
- default:
- assert(0 && "unhandled state");
- SET_ERRNO(HPE_INVALID_INTERNAL_STATE);
- goto error;
- }
- }
-
- /* Run callbacks for any marks that we have leftover after we ran our of
- * bytes. There should be at most one of these set, so it's OK to invoke
- * them in series (unset marks will not result in callbacks).
- *
- * We use the NOADVANCE() variety of callbacks here because 'p' has already
- * overflowed 'data' and this allows us to correct for the off-by-one that
- * we'd otherwise have (since CALLBACK_DATA() is meant to be run with a 'p'
- * value that's in-bounds).
- */
-
- assert(((header_field_mark ? 1 : 0) +
- (header_value_mark ? 1 : 0) +
- (url_mark ? 1 : 0) +
- (body_mark ? 1 : 0) +
- (status_mark ? 1 : 0)) <= 1);
-
- CALLBACK_DATA_NOADVANCE(header_field);
- CALLBACK_DATA_NOADVANCE(header_value);
- CALLBACK_DATA_NOADVANCE(url);
- CALLBACK_DATA_NOADVANCE(body);
- CALLBACK_DATA_NOADVANCE(status);
-
- RETURN(len);
-
-error:
- if (HTTP_PARSER_ERRNO(parser) == HPE_OK) {
- SET_ERRNO(HPE_UNKNOWN);
- }
-
- RETURN(p - data);
-}
-
-
-/* Does the parser need to see an EOF to find the end of the message? */
-int
-http_message_needs_eof (const http_parser *parser)
-{
- if (parser->type == HTTP_REQUEST) {
- return 0;
- }
-
- /* See RFC 2616 section 4.4 */
- if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */
- parser->status_code == 204 || /* No Content */
- parser->status_code == 304 || /* Not Modified */
- parser->flags & F_SKIPBODY) { /* response to a HEAD request */
- return 0;
- }
-
- if ((parser->flags & F_CHUNKED) || parser->content_length != ULLONG_MAX) {
- return 0;
- }
-
- return 1;
-}
-
-
-int
-http_should_keep_alive (const http_parser *parser)
-{
- if (parser->http_major > 0 && parser->http_minor > 0) {
- /* HTTP/1.1 */
- if (parser->flags & F_CONNECTION_CLOSE) {
- return 0;
- }
- } else {
- /* HTTP/1.0 or earlier */
- if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) {
- return 0;
- }
- }
-
- return !http_message_needs_eof(parser);
-}
-
-
-const char *
-http_method_str (enum http_method m)
-{
- return ELEM_AT(method_strings, m, "<unknown>");
-}
-
-const char *
-http_status_str (enum http_status s)
-{
- switch (s) {
-#define XX(num, name, string) case HTTP_STATUS_##name: return #string;
- HTTP_STATUS_MAP(XX)
-#undef XX
- default: return "<unknown>";
- }
-}
-
-void
-http_parser_init (http_parser *parser, enum http_parser_type t)
-{
- void *data = parser->data; /* preserve application data */
- memset(parser, 0, sizeof(*parser));
- parser->data = data;
- parser->type = t;
- parser->state = (t == HTTP_REQUEST ? s_start_req : (t == HTTP_RESPONSE ? s_start_res : s_start_req_or_res));
- parser->http_errno = HPE_OK;
-}
-
-void
-http_parser_settings_init(http_parser_settings *settings)
-{
- memset(settings, 0, sizeof(*settings));
-}
-
-const char *
-http_errno_name(enum http_errno err) {
- assert(((size_t) err) < ARRAY_SIZE(http_strerror_tab));
- return http_strerror_tab[err].name;
-}
-
-const char *
-http_errno_description(enum http_errno err) {
- assert(((size_t) err) < ARRAY_SIZE(http_strerror_tab));
- return http_strerror_tab[err].description;
-}
-
-static enum http_host_state
-http_parse_host_char(enum http_host_state s, const char ch) {
- switch(s) {
- case s_http_userinfo:
- case s_http_userinfo_start:
- if (ch == '@') {
- return s_http_host_start;
- }
-
- if (IS_USERINFO_CHAR(ch)) {
- return s_http_userinfo;
- }
- break;
-
- case s_http_host_start:
- if (ch == '[') {
- return s_http_host_v6_start;
- }
-
- if (IS_HOST_CHAR(ch)) {
- return s_http_host;
- }
-
- break;
-
- case s_http_host:
- if (IS_HOST_CHAR(ch)) {
- return s_http_host;
- }
-
- /* fall through */
- case s_http_host_v6_end:
- if (ch == ':') {
- return s_http_host_port_start;
- }
-
- break;
-
- case s_http_host_v6:
- if (ch == ']') {
- return s_http_host_v6_end;
- }
-
- /* fall through */
- case s_http_host_v6_start:
- if (IS_HEX(ch) || ch == ':' || ch == '.') {
- return s_http_host_v6;
- }
-
- if (s == s_http_host_v6 && ch == '%') {
- return s_http_host_v6_zone_start;
- }
- break;
-
- case s_http_host_v6_zone:
- if (ch == ']') {
- return s_http_host_v6_end;
- }
-
- /* fall through */
- case s_http_host_v6_zone_start:
- /* RFC 6874 Zone ID consists of 1*( unreserved / pct-encoded) */
- if (IS_ALPHANUM(ch) || ch == '%' || ch == '.' || ch == '-' || ch == '_' ||
- ch == '~') {
- return s_http_host_v6_zone;
- }
- break;
-
- case s_http_host_port:
- case s_http_host_port_start:
- if (IS_NUM(ch)) {
- return s_http_host_port;
- }
-
- break;
-
- default:
- break;
- }
- return s_http_host_dead;
-}
-
-static int
-http_parse_host(const char * buf, struct http_parser_url *u, int found_at) {
- enum http_host_state s;
-
- const char *p;
- size_t buflen = u->field_data[UF_HOST].off + u->field_data[UF_HOST].len;
-
- assert(u->field_set & (1 << UF_HOST));
-
- u->field_data[UF_HOST].len = 0;
-
- s = found_at ? s_http_userinfo_start : s_http_host_start;
-
- for (p = buf + u->field_data[UF_HOST].off; p < buf + buflen; p++) {
- enum http_host_state new_s = http_parse_host_char(s, *p);
-
- if (new_s == s_http_host_dead) {
- return 1;
- }
-
- switch(new_s) {
- case s_http_host:
- if (s != s_http_host) {
- u->field_data[UF_HOST].off = p - buf;
- }
- u->field_data[UF_HOST].len++;
- break;
-
- case s_http_host_v6:
- if (s != s_http_host_v6) {
- u->field_data[UF_HOST].off = p - buf;
- }
- u->field_data[UF_HOST].len++;
- break;
-
- case s_http_host_v6_zone_start:
- case s_http_host_v6_zone:
- u->field_data[UF_HOST].len++;
- break;
-
- case s_http_host_port:
- if (s != s_http_host_port) {
- u->field_data[UF_PORT].off = p - buf;
- u->field_data[UF_PORT].len = 0;
- u->field_set |= (1 << UF_PORT);
- }
- u->field_data[UF_PORT].len++;
- break;
-
- case s_http_userinfo:
- if (s != s_http_userinfo) {
- u->field_data[UF_USERINFO].off = p - buf ;
- u->field_data[UF_USERINFO].len = 0;
- u->field_set |= (1 << UF_USERINFO);
- }
- u->field_data[UF_USERINFO].len++;
- break;
-
- default:
- break;
- }
- s = new_s;
- }
-
- /* Make sure we don't end somewhere unexpected */
- switch (s) {
- case s_http_host_start:
- case s_http_host_v6_start:
- case s_http_host_v6:
- case s_http_host_v6_zone_start:
- case s_http_host_v6_zone:
- case s_http_host_port_start:
- case s_http_userinfo:
- case s_http_userinfo_start:
- return 1;
- default:
- break;
- }
-
- return 0;
-}
-
-void
-http_parser_url_init(struct http_parser_url *u) {
- memset(u, 0, sizeof(*u));
-}
-
-int
-http_parser_parse_url(const char *buf, size_t buflen, int is_connect,
- struct http_parser_url *u)
-{
- enum state s;
- const char *p;
- enum http_parser_url_fields uf, old_uf;
- int found_at = 0;
-
- if (buflen == 0) {
- return 1;
- }
-
- u->port = u->field_set = 0;
- s = is_connect ? s_req_server_start : s_req_spaces_before_url;
- old_uf = UF_MAX;
-
- for (p = buf; p < buf + buflen; p++) {
- s = parse_url_char(s, *p);
-
- /* Figure out the next field that we're operating on */
- switch (s) {
- case s_dead:
- return 1;
-
- /* Skip delimeters */
- case s_req_schema_slash:
- case s_req_schema_slash_slash:
- case s_req_server_start:
- case s_req_query_string_start:
- case s_req_fragment_start:
- continue;
-
- case s_req_schema:
- uf = UF_SCHEMA;
- break;
-
- case s_req_server_with_at:
- found_at = 1;
-
- /* fall through */
- case s_req_server:
- uf = UF_HOST;
- break;
-
- case s_req_path:
- uf = UF_PATH;
- break;
-
- case s_req_query_string:
- uf = UF_QUERY;
- break;
-
- case s_req_fragment:
- uf = UF_FRAGMENT;
- break;
-
- default:
- assert(!"Unexpected state");
- return 1;
- }
-
- /* Nothing's changed; soldier on */
- if (uf == old_uf) {
- u->field_data[uf].len++;
- continue;
- }
-
- u->field_data[uf].off = p - buf;
- u->field_data[uf].len = 1;
-
- u->field_set |= (1 << uf);
- old_uf = uf;
- }
-
- /* host must be present if there is a schema */
- /* parsing http:///toto will fail */
- if ((u->field_set & (1 << UF_SCHEMA)) &&
- (u->field_set & (1 << UF_HOST)) == 0) {
- return 1;
- }
-
- if (u->field_set & (1 << UF_HOST)) {
- if (http_parse_host(buf, u, found_at) != 0) {
- return 1;
- }
- }
-
- /* CONNECT requests can only contain "hostname:port" */
- if (is_connect && u->field_set != ((1 << UF_HOST)|(1 << UF_PORT))) {
- return 1;
- }
-
- if (u->field_set & (1 << UF_PORT)) {
- uint16_t off;
- uint16_t len;
- const char* p;
- const char* end;
- unsigned long v;
-
- off = u->field_data[UF_PORT].off;
- len = u->field_data[UF_PORT].len;
- end = buf + off + len;
-
- /* NOTE: The characters are already validated and are in the [0-9] range */
- assert(off + len <= buflen && "Port number overflow");
- v = 0;
- for (p = buf + off; p < end; p++) {
- v *= 10;
- v += *p - '0';
-
- /* Ports have a max value of 2^16 */
- if (v > 0xffff) {
- return 1;
- }
- }
-
- u->port = (uint16_t) v;
- }
-
- return 0;
-}
-
-void
-http_parser_pause(http_parser *parser, int paused) {
- /* Users should only be pausing/unpausing a parser that is not in an error
- * state. In non-debug builds, there's not much that we can do about this
- * other than ignore it.
- */
- if (HTTP_PARSER_ERRNO(parser) == HPE_OK ||
- HTTP_PARSER_ERRNO(parser) == HPE_PAUSED) {
- uint32_t nread = parser->nread; /* used by the SET_ERRNO macro */
- SET_ERRNO((paused) ? HPE_PAUSED : HPE_OK);
- } else {
- assert(0 && "Attempting to pause parser in error state");
- }
-}
-
-int
-http_body_is_final(const struct http_parser *parser) {
- return parser->state == s_message_done;
-}
-
-unsigned long
-http_parser_version(void) {
- return HTTP_PARSER_VERSION_MAJOR * 0x10000 |
- HTTP_PARSER_VERSION_MINOR * 0x00100 |
- HTTP_PARSER_VERSION_PATCH * 0x00001;
-}
-
-} // namespace wpi
diff --git a/wpiutil/src/main/native/cpp/jni/DataLogJNI.cpp b/wpiutil/src/main/native/cpp/jni/DataLogJNI.cpp
new file mode 100644
index 0000000..997b90b
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/jni/DataLogJNI.cpp
@@ -0,0 +1,357 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; 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_util_datalog_DataLogJNI.h"
+#include "wpi/DataLog.h"
+#include "wpi/jni_util.h"
+
+using namespace wpi::java;
+using namespace wpi::log;
+
+extern "C" {
+
+/*
+ * Class: edu_wpi_first_util_datalog_DataLogJNI
+ * Method: create
+ * Signature: (Ljava/lang/String;Ljava/lang/String;DLjava/lang/String;)J
+ */
+JNIEXPORT jlong JNICALL
+Java_edu_wpi_first_util_datalog_DataLogJNI_create
+ (JNIEnv* env, jclass, jstring dir, jstring filename, jdouble period,
+ jstring extraHeader)
+{
+ return reinterpret_cast<jlong>(new DataLog{JStringRef{env, dir},
+ JStringRef{env, filename}, period,
+ JStringRef{env, extraHeader}});
+}
+
+/*
+ * Class: edu_wpi_first_util_datalog_DataLogJNI
+ * Method: setFilename
+ * Signature: (JLjava/lang/String;)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_util_datalog_DataLogJNI_setFilename
+ (JNIEnv* env, jclass, jlong impl, jstring filename)
+{
+ if (impl == 0) {
+ return;
+ }
+ reinterpret_cast<DataLog*>(impl)->SetFilename(JStringRef{env, filename});
+}
+
+/*
+ * Class: edu_wpi_first_util_datalog_DataLogJNI
+ * Method: flush
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_util_datalog_DataLogJNI_flush
+ (JNIEnv*, jclass, jlong impl)
+{
+ if (impl == 0) {
+ return;
+ }
+ reinterpret_cast<DataLog*>(impl)->Flush();
+}
+
+/*
+ * Class: edu_wpi_first_util_datalog_DataLogJNI
+ * Method: pause
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_util_datalog_DataLogJNI_pause
+ (JNIEnv*, jclass, jlong impl)
+{
+ if (impl == 0) {
+ return;
+ }
+ reinterpret_cast<DataLog*>(impl)->Pause();
+}
+
+/*
+ * Class: edu_wpi_first_util_datalog_DataLogJNI
+ * Method: resume
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_util_datalog_DataLogJNI_resume
+ (JNIEnv*, jclass, jlong impl)
+{
+ if (impl == 0) {
+ return;
+ }
+ reinterpret_cast<DataLog*>(impl)->Resume();
+}
+
+/*
+ * Class: edu_wpi_first_util_datalog_DataLogJNI
+ * Method: start
+ * Signature: (JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;J)I
+ */
+JNIEXPORT jint JNICALL
+Java_edu_wpi_first_util_datalog_DataLogJNI_start
+ (JNIEnv* env, jclass, jlong impl, jstring name, jstring type,
+ jstring metadata, jlong timestamp)
+{
+ if (impl == 0) {
+ return 0;
+ }
+ return reinterpret_cast<DataLog*>(impl)->Start(
+ JStringRef{env, name}, JStringRef{env, type}, JStringRef{env, metadata},
+ timestamp);
+}
+
+/*
+ * Class: edu_wpi_first_util_datalog_DataLogJNI
+ * Method: finish
+ * Signature: (JIJ)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_util_datalog_DataLogJNI_finish
+ (JNIEnv*, jclass, jlong impl, jint entry, jlong timestamp)
+{
+ if (impl == 0) {
+ return;
+ }
+ reinterpret_cast<DataLog*>(impl)->Finish(entry, timestamp);
+}
+
+/*
+ * Class: edu_wpi_first_util_datalog_DataLogJNI
+ * Method: setMetadata
+ * Signature: (JILjava/lang/String;J)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_util_datalog_DataLogJNI_setMetadata
+ (JNIEnv* env, jclass, jlong impl, jint entry, jstring metadata,
+ jlong timestamp)
+{
+ if (impl == 0) {
+ return;
+ }
+ reinterpret_cast<DataLog*>(impl)->SetMetadata(
+ entry, JStringRef{env, metadata}, timestamp);
+}
+
+/*
+ * Class: edu_wpi_first_util_datalog_DataLogJNI
+ * Method: close
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_util_datalog_DataLogJNI_close
+ (JNIEnv*, jclass, jlong impl)
+{
+ delete reinterpret_cast<DataLog*>(impl);
+}
+
+/*
+ * Class: edu_wpi_first_util_datalog_DataLogJNI
+ * Method: appendRaw
+ * Signature: (JI[BJ)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_util_datalog_DataLogJNI_appendRaw
+ (JNIEnv* env, jclass, jlong impl, jint entry, jbyteArray value,
+ jlong timestamp)
+{
+ if (impl == 0) {
+ return;
+ }
+ JByteArrayRef cvalue{env, value};
+ reinterpret_cast<DataLog*>(impl)->AppendRaw(
+ entry,
+ {reinterpret_cast<const uint8_t*>(cvalue.array().data()), cvalue.size()},
+ timestamp);
+}
+
+/*
+ * Class: edu_wpi_first_util_datalog_DataLogJNI
+ * Method: appendBoolean
+ * Signature: (JIZJ)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_util_datalog_DataLogJNI_appendBoolean
+ (JNIEnv*, jclass, jlong impl, jint entry, jboolean value, jlong timestamp)
+{
+ if (impl == 0) {
+ return;
+ }
+ reinterpret_cast<DataLog*>(impl)->AppendBoolean(entry, value, timestamp);
+}
+
+/*
+ * Class: edu_wpi_first_util_datalog_DataLogJNI
+ * Method: appendInteger
+ * Signature: (JIJJ)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_util_datalog_DataLogJNI_appendInteger
+ (JNIEnv*, jclass, jlong impl, jint entry, jlong value, jlong timestamp)
+{
+ if (impl == 0) {
+ return;
+ }
+ reinterpret_cast<DataLog*>(impl)->AppendInteger(entry, value, timestamp);
+}
+
+/*
+ * Class: edu_wpi_first_util_datalog_DataLogJNI
+ * Method: appendFloat
+ * Signature: (JIFJ)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_util_datalog_DataLogJNI_appendFloat
+ (JNIEnv*, jclass, jlong impl, jint entry, jfloat value, jlong timestamp)
+{
+ if (impl == 0) {
+ return;
+ }
+ reinterpret_cast<DataLog*>(impl)->AppendFloat(entry, value, timestamp);
+}
+
+/*
+ * Class: edu_wpi_first_util_datalog_DataLogJNI
+ * Method: appendDouble
+ * Signature: (JIDJ)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_util_datalog_DataLogJNI_appendDouble
+ (JNIEnv*, jclass, jlong impl, jint entry, jdouble value, jlong timestamp)
+{
+ if (impl == 0) {
+ return;
+ }
+ reinterpret_cast<DataLog*>(impl)->AppendDouble(entry, value, timestamp);
+}
+
+/*
+ * Class: edu_wpi_first_util_datalog_DataLogJNI
+ * Method: appendString
+ * Signature: (JILjava/lang/String;J)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_util_datalog_DataLogJNI_appendString
+ (JNIEnv* env, jclass, jlong impl, jint entry, jstring value, jlong timestamp)
+{
+ if (impl == 0) {
+ return;
+ }
+ reinterpret_cast<DataLog*>(impl)->AppendString(entry, JStringRef{env, value},
+ timestamp);
+}
+
+/*
+ * Class: edu_wpi_first_util_datalog_DataLogJNI
+ * Method: appendBooleanArray
+ * Signature: (JI[ZJ)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_util_datalog_DataLogJNI_appendBooleanArray
+ (JNIEnv* env, jclass, jlong impl, jint entry, jbooleanArray value,
+ jlong timestamp)
+{
+ if (impl == 0) {
+ return;
+ }
+ reinterpret_cast<DataLog*>(impl)->AppendBooleanArray(
+ entry, JBooleanArrayRef{env, value}, timestamp);
+}
+
+/*
+ * Class: edu_wpi_first_util_datalog_DataLogJNI
+ * Method: appendIntegerArray
+ * Signature: (JI[JJ)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_util_datalog_DataLogJNI_appendIntegerArray
+ (JNIEnv* env, jclass, jlong impl, jint entry, jlongArray value,
+ jlong timestamp)
+{
+ if (impl == 0) {
+ return;
+ }
+ JLongArrayRef jarr{env, value};
+ if constexpr (sizeof(jlong) == sizeof(int64_t)) {
+ reinterpret_cast<DataLog*>(impl)->AppendIntegerArray(
+ entry,
+ {reinterpret_cast<const int64_t*>(jarr.array().data()),
+ jarr.array().size()},
+ timestamp);
+ } else {
+ wpi::SmallVector<int64_t, 16> arr;
+ arr.reserve(jarr.size());
+ for (auto v : jarr.array()) {
+ arr.push_back(v);
+ }
+ reinterpret_cast<DataLog*>(impl)->AppendIntegerArray(entry, arr, timestamp);
+ }
+}
+
+/*
+ * Class: edu_wpi_first_util_datalog_DataLogJNI
+ * Method: appendFloatArray
+ * Signature: (JI[FJ)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_util_datalog_DataLogJNI_appendFloatArray
+ (JNIEnv* env, jclass, jlong impl, jint entry, jfloatArray value,
+ jlong timestamp)
+{
+ if (impl == 0) {
+ return;
+ }
+ reinterpret_cast<DataLog*>(impl)->AppendFloatArray(
+ entry, JFloatArrayRef{env, value}, timestamp);
+}
+
+/*
+ * Class: edu_wpi_first_util_datalog_DataLogJNI
+ * Method: appendDoubleArray
+ * Signature: (JI[DJ)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_util_datalog_DataLogJNI_appendDoubleArray
+ (JNIEnv* env, jclass, jlong impl, jint entry, jdoubleArray value,
+ jlong timestamp)
+{
+ if (impl == 0) {
+ return;
+ }
+ reinterpret_cast<DataLog*>(impl)->AppendDoubleArray(
+ entry, JDoubleArrayRef{env, value}, timestamp);
+}
+
+/*
+ * Class: edu_wpi_first_util_datalog_DataLogJNI
+ * Method: appendStringArray
+ * Signature: (JI[Ljava/lang/Object;J)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_util_datalog_DataLogJNI_appendStringArray
+ (JNIEnv* env, jclass, jlong impl, jint entry, jobjectArray value,
+ jlong timestamp)
+{
+ if (impl == 0) {
+ return;
+ }
+ size_t len = env->GetArrayLength(value);
+ std::vector<std::string> arr;
+ arr.reserve(len);
+ for (size_t i = 0; i < len; ++i) {
+ JLocal<jstring> elem{
+ env, static_cast<jstring>(env->GetObjectArrayElement(value, i))};
+ if (!elem) {
+ return;
+ }
+ arr.emplace_back(JStringRef{env, elem}.str());
+ }
+ reinterpret_cast<DataLog*>(impl)->AppendStringArray(entry, arr, timestamp);
+}
+
+} // extern "C"
diff --git a/wpiutil/src/main/native/cpp/jni/WPIUtilJNI.cpp b/wpiutil/src/main/native/cpp/jni/WPIUtilJNI.cpp
index e876d9c..5fa4ddf 100644
--- a/wpiutil/src/main/native/cpp/jni/WPIUtilJNI.cpp
+++ b/wpiutil/src/main/native/cpp/jni/WPIUtilJNI.cpp
@@ -4,14 +4,9 @@
#include <jni.h>
-#include "../MulticastHandleManager.h"
#include "edu_wpi_first_util_WPIUtilJNI.h"
-#include "wpi/DenseMap.h"
-#include "wpi/MulticastServiceAnnouncer.h"
-#include "wpi/MulticastServiceResolver.h"
-#include "wpi/PortForwarder.h"
+#include "fmt/format.h"
#include "wpi/Synchronization.h"
-#include "wpi/UidVector.h"
#include "wpi/jni_util.h"
#include "wpi/timestamp.h"
@@ -21,8 +16,6 @@
static uint64_t mockNow = 0;
static JException interruptedEx;
-static JClass serviceDataCls;
-static JGlobal<jobjectArray> serviceDataEmptyArray;
extern "C" {
@@ -37,17 +30,6 @@
return JNI_ERR;
}
- serviceDataCls = JClass{env, "edu/wpi/first/util/ServiceData"};
- if (!serviceDataCls) {
- return JNI_ERR;
- }
-
- serviceDataEmptyArray = JGlobal<jobjectArray>{
- env, env->NewObjectArray(0, serviceDataCls, nullptr)};
- if (serviceDataEmptyArray == nullptr) {
- return JNI_ERR;
- }
-
return JNI_VERSION_1_6;
}
@@ -57,13 +39,23 @@
return;
}
- serviceDataEmptyArray.free(env);
- serviceDataCls.free(env);
interruptedEx.free(env);
}
/*
* Class: edu_wpi_first_util_WPIUtilJNI
+ * Method: writeStderr
+ * Signature: (Ljava/lang/String;)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_util_WPIUtilJNI_writeStderr
+ (JNIEnv* env, jclass, jstring str)
+{
+ fmt::print(stderr, "{}", JStringRef{env, str});
+}
+
+/*
+ * Class: edu_wpi_first_util_WPIUtilJNI
* Method: enableMockTime
* Signature: ()V
*/
@@ -130,32 +122,6 @@
/*
* Class: edu_wpi_first_util_WPIUtilJNI
- * Method: addPortForwarder
- * Signature: (ILjava/lang/String;I)V
- */
-JNIEXPORT void JNICALL
-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),
- JStringRef{env, remoteHost}.str(),
- static_cast<unsigned int>(remotePort));
-}
-
-/*
- * Class: edu_wpi_first_util_WPIUtilJNI
- * Method: removePortForwarder
- * Signature: (I)V
- */
-JNIEXPORT void JNICALL
-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
*/
@@ -281,7 +247,7 @@
JIntArrayRef handlesArr{env, handles};
wpi::SmallVector<WPI_Handle, 8> signaledBuf;
signaledBuf.resize(handlesArr.size());
- wpi::span<const WPI_Handle> handlesArr2{
+ std::span<const WPI_Handle> handlesArr2{
reinterpret_cast<const WPI_Handle*>(handlesArr.array().data()),
handlesArr.size()};
@@ -305,7 +271,7 @@
JIntArrayRef handlesArr{env, handles};
wpi::SmallVector<WPI_Handle, 8> signaledBuf;
signaledBuf.resize(handlesArr.size());
- wpi::span<const WPI_Handle> handlesArr2{
+ std::span<const WPI_Handle> handlesArr2{
reinterpret_cast<const WPI_Handle*>(handlesArr.array().data()),
handlesArr.size()};
@@ -319,257 +285,4 @@
return MakeJIntArray(env, signaled);
}
-/*
- * Class: edu_wpi_first_util_WPIUtilJNI
- * Method: createMulticastServiceAnnouncer
- * Signature: (Ljava/lang/String;Ljava/lang/String;I[Ljava/lang/Object;[Ljava/lang/Object;)I
- */
-JNIEXPORT jint JNICALL
-Java_edu_wpi_first_util_WPIUtilJNI_createMulticastServiceAnnouncer
- (JNIEnv* env, jclass, jstring serviceName, jstring serviceType, jint port,
- jobjectArray keys, jobjectArray values)
-{
- auto& manager = wpi::GetMulticastManager();
- std::scoped_lock lock{manager.mutex};
-
- JStringRef serviceNameRef{env, serviceName};
- JStringRef serviceTypeRef{env, serviceType};
-
- size_t keysLen = env->GetArrayLength(keys);
- wpi::SmallVector<std::pair<std::string, std::string>, 8> txtVec;
- txtVec.reserve(keysLen);
- for (size_t i = 0; i < keysLen; i++) {
- JLocal<jstring> key{
- env, static_cast<jstring>(env->GetObjectArrayElement(keys, i))};
- JLocal<jstring> value{
- env, static_cast<jstring>(env->GetObjectArrayElement(values, i))};
-
- txtVec.emplace_back(std::pair<std::string, std::string>{
- JStringRef{env, key}.str(), JStringRef{env, value}.str()});
- }
-
- auto announcer = std::make_unique<wpi::MulticastServiceAnnouncer>(
- serviceNameRef.str(), serviceTypeRef.str(), port, txtVec);
-
- size_t index = manager.handleIds.emplace_back(1);
-
- manager.announcers[index] = std::move(announcer);
-
- return static_cast<jint>(index);
-}
-
-/*
- * Class: edu_wpi_first_util_WPIUtilJNI
- * Method: freeMulticastServiceAnnouncer
- * Signature: (I)V
- */
-JNIEXPORT void JNICALL
-Java_edu_wpi_first_util_WPIUtilJNI_freeMulticastServiceAnnouncer
- (JNIEnv* env, jclass, jint handle)
-{
- auto& manager = wpi::GetMulticastManager();
- std::scoped_lock lock{manager.mutex};
- manager.announcers[handle] = nullptr;
- manager.handleIds.erase(handle);
-}
-
-/*
- * Class: edu_wpi_first_util_WPIUtilJNI
- * Method: startMulticastServiceAnnouncer
- * Signature: (I)V
- */
-JNIEXPORT void JNICALL
-Java_edu_wpi_first_util_WPIUtilJNI_startMulticastServiceAnnouncer
- (JNIEnv* env, jclass, jint handle)
-{
- auto& manager = wpi::GetMulticastManager();
- std::scoped_lock lock{manager.mutex};
- auto& announcer = manager.announcers[handle];
- announcer->Start();
-}
-
-/*
- * Class: edu_wpi_first_util_WPIUtilJNI
- * Method: stopMulticastServiceAnnouncer
- * Signature: (I)V
- */
-JNIEXPORT void JNICALL
-Java_edu_wpi_first_util_WPIUtilJNI_stopMulticastServiceAnnouncer
- (JNIEnv* env, jclass, jint handle)
-{
- auto& manager = wpi::GetMulticastManager();
- std::scoped_lock lock{manager.mutex};
- auto& announcer = manager.announcers[handle];
- announcer->Stop();
-}
-
-/*
- * Class: edu_wpi_first_util_WPIUtilJNI
- * Method: getMulticastServiceAnnouncerHasImplementation
- * Signature: (I)Z
- */
-JNIEXPORT jboolean JNICALL
-Java_edu_wpi_first_util_WPIUtilJNI_getMulticastServiceAnnouncerHasImplementation
- (JNIEnv* env, jclass, jint handle)
-{
- auto& manager = wpi::GetMulticastManager();
- std::scoped_lock lock{manager.mutex};
- auto& announcer = manager.announcers[handle];
- return announcer->HasImplementation();
-}
-
-/*
- * Class: edu_wpi_first_util_WPIUtilJNI
- * Method: createMulticastServiceResolver
- * Signature: (Ljava/lang/String;)I
- */
-JNIEXPORT jint JNICALL
-Java_edu_wpi_first_util_WPIUtilJNI_createMulticastServiceResolver
- (JNIEnv* env, jclass, jstring serviceType)
-{
- auto& manager = wpi::GetMulticastManager();
- std::scoped_lock lock{manager.mutex};
- JStringRef serviceTypeRef{env, serviceType};
-
- auto resolver =
- std::make_unique<wpi::MulticastServiceResolver>(serviceTypeRef.str());
-
- size_t index = manager.handleIds.emplace_back(2);
-
- manager.resolvers[index] = std::move(resolver);
-
- return static_cast<jint>(index);
-}
-
-/*
- * Class: edu_wpi_first_util_WPIUtilJNI
- * Method: freeMulticastServiceResolver
- * Signature: (I)V
- */
-JNIEXPORT void JNICALL
-Java_edu_wpi_first_util_WPIUtilJNI_freeMulticastServiceResolver
- (JNIEnv* env, jclass, jint handle)
-{
- auto& manager = wpi::GetMulticastManager();
- std::scoped_lock lock{manager.mutex};
- manager.resolvers[handle] = nullptr;
- manager.handleIds.erase(handle);
-}
-
-/*
- * Class: edu_wpi_first_util_WPIUtilJNI
- * Method: startMulticastServiceResolver
- * Signature: (I)V
- */
-JNIEXPORT void JNICALL
-Java_edu_wpi_first_util_WPIUtilJNI_startMulticastServiceResolver
- (JNIEnv* env, jclass, jint handle)
-{
- auto& manager = wpi::GetMulticastManager();
- std::scoped_lock lock{manager.mutex};
- auto& resolver = manager.resolvers[handle];
- resolver->Start();
-}
-
-/*
- * Class: edu_wpi_first_util_WPIUtilJNI
- * Method: stopMulticastServiceResolver
- * Signature: (I)V
- */
-JNIEXPORT void JNICALL
-Java_edu_wpi_first_util_WPIUtilJNI_stopMulticastServiceResolver
- (JNIEnv* env, jclass, jint handle)
-{
- auto& manager = wpi::GetMulticastManager();
- std::scoped_lock lock{manager.mutex};
- auto& resolver = manager.resolvers[handle];
- resolver->Stop();
-}
-
-/*
- * Class: edu_wpi_first_util_WPIUtilJNI
- * Method: getMulticastServiceResolverHasImplementation
- * Signature: (I)Z
- */
-JNIEXPORT jboolean JNICALL
-Java_edu_wpi_first_util_WPIUtilJNI_getMulticastServiceResolverHasImplementation
- (JNIEnv* env, jclass, jint handle)
-{
- auto& manager = wpi::GetMulticastManager();
- std::scoped_lock lock{manager.mutex};
- auto& resolver = manager.resolvers[handle];
- return resolver->HasImplementation();
-}
-
-/*
- * Class: edu_wpi_first_util_WPIUtilJNI
- * Method: getMulticastServiceResolverEventHandle
- * Signature: (I)I
- */
-JNIEXPORT jint JNICALL
-Java_edu_wpi_first_util_WPIUtilJNI_getMulticastServiceResolverEventHandle
- (JNIEnv* env, jclass, jint handle)
-{
- auto& manager = wpi::GetMulticastManager();
- std::scoped_lock lock{manager.mutex};
- auto& resolver = manager.resolvers[handle];
- return resolver->GetEventHandle();
-}
-
-/*
- * Class: edu_wpi_first_util_WPIUtilJNI
- * Method: getMulticastServiceResolverData
- * Signature: (I)[Ljava/lang/Object;
- */
-JNIEXPORT jobjectArray JNICALL
-Java_edu_wpi_first_util_WPIUtilJNI_getMulticastServiceResolverData
- (JNIEnv* env, jclass, jint handle)
-{
- static jmethodID constructor =
- env->GetMethodID(serviceDataCls, "<init>",
- "(JILjava/lang/String;Ljava/lang/String;[Ljava/lang/"
- "String;[Ljava/lang/String;)V");
- auto& manager = wpi::GetMulticastManager();
- std::vector<wpi::MulticastServiceResolver::ServiceData> allData;
- {
- std::scoped_lock lock{manager.mutex};
- auto& resolver = manager.resolvers[handle];
- allData = resolver->GetData();
- }
- if (allData.empty()) {
- return serviceDataEmptyArray;
- }
-
- JLocal<jobjectArray> returnData{
- env, env->NewObjectArray(allData.size(), serviceDataCls, nullptr)};
-
- for (auto&& data : allData) {
- JLocal<jstring> serviceName{env, MakeJString(env, data.serviceName)};
- JLocal<jstring> hostName{env, MakeJString(env, data.hostName)};
-
- wpi::SmallVector<std::string_view, 8> keysRef;
- wpi::SmallVector<std::string_view, 8> valuesRef;
-
- size_t index = 0;
- for (auto&& txt : data.txt) {
- keysRef.emplace_back(txt.first);
- valuesRef.emplace_back(txt.second);
- }
-
- JLocal<jobjectArray> keys{env, MakeJStringArray(env, keysRef)};
- JLocal<jobjectArray> values{env, MakeJStringArray(env, valuesRef)};
-
- JLocal<jobject> dataItem{
- env, env->NewObject(serviceDataCls, constructor,
- static_cast<jlong>(data.ipv4Address),
- static_cast<jint>(data.port), serviceName.obj(),
- hostName.obj(), keys.obj(), values.obj())};
-
- env->SetObjectArrayElement(returnData, index, dataItem);
- index++;
- }
-
- return returnData;
-}
-
} // extern "C"
diff --git a/wpiutil/src/main/native/cpp/json.cpp b/wpiutil/src/main/native/cpp/json.cpp
deleted file mode 100644
index f1cfb14..0000000
--- a/wpiutil/src/main/native/cpp/json.cpp
+++ /dev/null
@@ -1,1505 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Modifications Copyright (c) 2017-2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
-/*
- __ _____ _____ _____
- __| | __| | | | JSON for Modern C++
-| | |__ | | | | | | version 3.1.2
-|_____|_____|_____|_|___| https://github.com/nlohmann/json
-
-Licensed under the MIT License <http://opensource.org/licenses/MIT>.
-Copyright (c) 2013-2018 Niels Lohmann <http://nlohmann.me>.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
-#define WPI_JSON_IMPLEMENTATION
-#include "wpi/json.h"
-
-#include "fmt/format.h"
-#include "wpi/raw_ostream.h"
-
-namespace wpi {
-namespace detail {
-
-exception::exception(int id_, std::string_view what_arg)
- : id(id_), m(std::string{what_arg}) {}
-
-parse_error parse_error::create(int id_, std::size_t byte_, std::string_view what_arg)
-{
- if (byte_ != 0)
- return parse_error(id_, byte_, fmt::format("[json.exception.parse_error.{}] parse error at {}: {}", id_, byte_, what_arg));
- else
- return parse_error(id_, byte_, fmt::format("[json.exception.parse_error.{}] parse error: {}", id_, what_arg));
-}
-
-invalid_iterator invalid_iterator::create(int id_, std::string_view what_arg)
-{
- return invalid_iterator(id_, fmt::format("[json.exception.invalid_iterator.{}] {}", id_, what_arg));
-}
-
-invalid_iterator invalid_iterator::create(int id_, std::string_view what_arg, std::string_view type_info)
-{
- return invalid_iterator(id_, fmt::format("[json.exception.invalid_iterator.{}] {} {}", id_, what_arg, type_info));
-}
-
-type_error type_error::create(int id_, std::string_view what_arg)
-{
- return type_error(id_, fmt::format("[json.exception.type_error.{}] {}", id_, what_arg));
-}
-
-type_error type_error::create(int id_, std::string_view what_arg, std::string_view type_info)
-{
- return type_error(id_, fmt::format("[json.exception.type_error.{}] {} {}", id_, what_arg, type_info));
-}
-
-out_of_range out_of_range::create(int id_, std::string_view what_arg)
-{
- return out_of_range(id_, fmt::format("[json.exception.out_of_range.{}] {}", id_, what_arg));
-}
-
-other_error other_error::create(int id_, std::string_view what_arg)
-{
- return other_error(id_, fmt::format("[json.exception.other_error.{}] {}", id_, what_arg));
-}
-
-} // namespace detail
-
-json::json_value::json_value(value_t t)
-{
- switch (t)
- {
- case value_t::object:
- {
- object = create<object_t>();
- break;
- }
-
- case value_t::array:
- {
- array = create<array_t>();
- break;
- }
-
- case value_t::string:
- {
- string = create<std::string>("");
- break;
- }
-
- case value_t::boolean:
- {
- boolean = false;
- break;
- }
-
- case value_t::number_integer:
- {
- number_integer = 0;
- break;
- }
-
- case value_t::number_unsigned:
- {
- number_unsigned = 0u;
- break;
- }
-
- case value_t::number_float:
- {
- number_float = 0.0;
- break;
- }
-
- case value_t::null:
- {
- object = nullptr; // silence warning, see #821
- break;
- }
-
- default:
- {
- object = nullptr; // silence warning, see #821
- if (JSON_UNLIKELY(t == value_t::null))
- {
- JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.1.2")); // LCOV_EXCL_LINE
- }
- break;
- }
- }
-}
-
-void json::json_value::destroy(value_t t) noexcept
-{
- switch (t)
- {
- case value_t::object:
- {
- std::allocator<object_t> alloc;
- std::allocator_traits<decltype(alloc)>::destroy(alloc, object);
- std::allocator_traits<decltype(alloc)>::deallocate(alloc, object, 1);
- break;
- }
-
- case value_t::array:
- {
- std::allocator<array_t> alloc;
- std::allocator_traits<decltype(alloc)>::destroy(alloc, array);
- std::allocator_traits<decltype(alloc)>::deallocate(alloc, array, 1);
- break;
- }
-
- case value_t::string:
- {
- std::allocator<std::string> alloc;
- std::allocator_traits<decltype(alloc)>::destroy(alloc, string);
- std::allocator_traits<decltype(alloc)>::deallocate(alloc, string, 1);
- break;
- }
-
- default:
- {
- break;
- }
- }
-}
-
-json::json(initializer_list_t init,
- bool type_deduction,
- value_t manual_type)
-{
- // check if each element is an array with two elements whose first
- // element is a string
- bool is_an_object = std::all_of(init.begin(), init.end(),
- [](const detail::json_ref<json>& element_ref)
- {
- return (element_ref->is_array() and element_ref->size() == 2 and (*element_ref)[0].is_string());
- });
-
- // adjust type if type deduction is not wanted
- if (not type_deduction)
- {
- // if array is wanted, do not create an object though possible
- if (manual_type == value_t::array)
- {
- is_an_object = false;
- }
-
- // if object is wanted but impossible, throw an exception
- if (JSON_UNLIKELY(manual_type == value_t::object and not is_an_object))
- {
- JSON_THROW(type_error::create(301, "cannot create object from initializer list"));
- }
- }
-
- if (is_an_object)
- {
- // the initializer list is a list of pairs -> create object
- m_type = value_t::object;
- m_value = value_t::object;
-
- std::for_each(init.begin(), init.end(), [this](const detail::json_ref<json>& element_ref)
- {
- auto element = element_ref.moved_or_copied();
- m_value.object->try_emplace(
- *((*element.m_value.array)[0].m_value.string),
- std::move((*element.m_value.array)[1]));
- });
- }
- else
- {
- // the initializer list describes an array -> create array
- m_type = value_t::array;
- m_value.array = create<array_t>(init.begin(), init.end());
- }
-
- assert_invariant();
-}
-
-json::json(size_type cnt, const json& val)
- : m_type(value_t::array)
-{
- m_value.array = create<array_t>(cnt, val);
- assert_invariant();
-}
-
-json::json(const json& other)
- : m_type(other.m_type)
-{
- // check of passed value is valid
- other.assert_invariant();
-
- switch (m_type)
- {
- case value_t::object:
- {
- m_value = *other.m_value.object;
- break;
- }
-
- case value_t::array:
- {
- m_value = *other.m_value.array;
- break;
- }
-
- case value_t::string:
- {
- m_value = *other.m_value.string;
- break;
- }
-
- case value_t::boolean:
- {
- m_value = other.m_value.boolean;
- break;
- }
-
- case value_t::number_integer:
- {
- m_value = other.m_value.number_integer;
- break;
- }
-
- case value_t::number_unsigned:
- {
- m_value = other.m_value.number_unsigned;
- break;
- }
-
- case value_t::number_float:
- {
- m_value = other.m_value.number_float;
- break;
- }
-
- default:
- break;
- }
-
- assert_invariant();
-}
-
-json json::meta()
-{
- json result;
-
- result["copyright"] = "(C) 2013-2017 Niels Lohmann, (C) 2017-2018 FIRST";
- result["name"] = "WPI version of JSON for Modern C++";
- result["url"] = "https://github.com/wpilibsuite/allwpilib";
- result["version"]["string"] =
- std::to_string(NLOHMANN_JSON_VERSION_MAJOR) + "." +
- std::to_string(NLOHMANN_JSON_VERSION_MINOR) + "." +
- std::to_string(NLOHMANN_JSON_VERSION_PATCH);
- result["version"]["major"] = NLOHMANN_JSON_VERSION_MAJOR;
- result["version"]["minor"] = NLOHMANN_JSON_VERSION_MINOR;
- result["version"]["patch"] = NLOHMANN_JSON_VERSION_PATCH;
-
-#ifdef _WIN32
- result["platform"] = "win32";
-#elif defined __linux__
- result["platform"] = "linux";
-#elif defined __APPLE__
- result["platform"] = "apple";
-#elif defined __unix__
- result["platform"] = "unix";
-#else
- result["platform"] = "unknown";
-#endif
-
-#if defined(__ICC) || defined(__INTEL_COMPILER)
- result["compiler"] = {{"family", "icc"}, {"version", __INTEL_COMPILER}};
-#elif defined(__clang__)
- result["compiler"] = {{"family", "clang"}, {"version", __clang_version__}};
-#elif defined(__GNUC__) || defined(__GNUG__)
- result["compiler"] = {{"family", "gcc"}, {"version", std::to_string(__GNUC__) + "." + std::to_string(__GNUC_MINOR__) + "." + std::to_string(__GNUC_PATCHLEVEL__)}};
-#elif defined(__HP_cc) || defined(__HP_aCC)
- result["compiler"] = "hp"
-#elif defined(__IBMCPP__)
- result["compiler"] = {{"family", "ilecpp"}, {"version", __IBMCPP__}};
-#elif defined(_MSC_VER)
- result["compiler"] = {{"family", "msvc"}, {"version", _MSC_VER}};
-#elif defined(__PGI)
- result["compiler"] = {{"family", "pgcpp"}, {"version", __PGI}};
-#elif defined(__SUNPRO_CC)
- result["compiler"] = {{"family", "sunpro"}, {"version", __SUNPRO_CC}};
-#else
- result["compiler"] = {{"family", "unknown"}, {"version", "unknown"}};
-#endif
-
-#ifdef __cplusplus
- result["compiler"]["c++"] = std::to_string(__cplusplus);
-#else
- result["compiler"]["c++"] = "unknown";
-#endif
- return result;
-}
-
-json::reference json::at(size_type idx)
-{
- // at only works for arrays
- if (JSON_LIKELY(is_array()))
- {
- JSON_TRY
- {
- return m_value.array->at(idx);
- }
- JSON_CATCH (std::out_of_range&)
- {
- // create better exception explanation
- JSON_THROW(out_of_range::create(401, fmt::format("array index {} is out of range", idx)));
- }
- }
- else
- {
- JSON_THROW(type_error::create(304, "cannot use at() with", type_name()));
- }
-}
-
-json::const_reference json::at(size_type idx) const
-{
- // at only works for arrays
- if (JSON_LIKELY(is_array()))
- {
- JSON_TRY
- {
- return m_value.array->at(idx);
- }
- JSON_CATCH (std::out_of_range&)
- {
- // create better exception explanation
- JSON_THROW(out_of_range::create(401, fmt::format("array index {} is out of range", idx)));
- }
- }
- else
- {
- JSON_THROW(type_error::create(304, "cannot use at() with", type_name()));
- }
-}
-
-json::reference json::at(std::string_view key)
-{
- // at only works for objects
- if (JSON_LIKELY(is_object()))
- {
- auto it = m_value.object->find(key);
- if (it == m_value.object->end())
- {
- // create better exception explanation
- JSON_THROW(out_of_range::create(403, fmt::format("key '{}' not found", key)));
- }
- return it->second;
- }
- else
- {
- JSON_THROW(type_error::create(304, "cannot use at() with", type_name()));
- }
-}
-
-json::const_reference json::at(std::string_view key) const
-{
- // at only works for objects
- if (JSON_LIKELY(is_object()))
- {
- auto it = m_value.object->find(key);
- if (it == m_value.object->end())
- {
- // create better exception explanation
- JSON_THROW(out_of_range::create(403, fmt::format("key '{}' not found", key)));
- }
- return it->second;
- }
- else
- {
- JSON_THROW(type_error::create(304, "cannot use at() with", type_name()));
- }
-}
-
-json::reference json::operator[](size_type idx)
-{
- // implicitly convert null value to an empty array
- if (is_null())
- {
- m_type = value_t::array;
- m_value.array = create<array_t>();
- assert_invariant();
- }
-
- // operator[] only works for arrays
- if (JSON_LIKELY(is_array()))
- {
- // fill up array with null values if given idx is outside range
- if (idx >= m_value.array->size())
- {
- m_value.array->insert(m_value.array->end(),
- idx - m_value.array->size() + 1,
- json());
- }
-
- return m_value.array->operator[](idx);
- }
-
- JSON_THROW(type_error::create(305, "cannot use operator[] with", type_name()));
-}
-
-json::const_reference json::operator[](size_type idx) const
-{
- // const operator[] only works for arrays
- if (JSON_LIKELY(is_array()))
- {
- return m_value.array->operator[](idx);
- }
-
- JSON_THROW(type_error::create(305, "cannot use operator[] with", type_name()));
-}
-
-json::reference json::operator[](std::string_view key)
-{
- // implicitly convert null value to an empty object
- if (is_null())
- {
- m_type = value_t::object;
- m_value.object = create<object_t>();
- assert_invariant();
- }
-
- // operator[] only works for objects
- if (JSON_LIKELY(is_object()))
- {
- return m_value.object->operator[](key);
- }
-
- JSON_THROW(type_error::create(305, "cannot use operator[] with", type_name()));
-}
-
-json::const_reference json::operator[](std::string_view key) const
-{
- // const operator[] only works for objects
- if (JSON_LIKELY(is_object()))
- {
- assert(m_value.object->find(key) != m_value.object->end());
- return m_value.object->find(key)->second;
- }
-
- JSON_THROW(type_error::create(305, "cannot use operator[] with", type_name()));
-}
-
-json::size_type json::erase(std::string_view key)
-{
- // this erase only works for objects
- if (JSON_LIKELY(is_object()))
- {
- return m_value.object->erase(key);
- }
-
- JSON_THROW(type_error::create(307, "cannot use erase() with", type_name()));
-}
-
-void json::erase(const size_type idx)
-{
- // this erase only works for arrays
- if (JSON_LIKELY(is_array()))
- {
- if (JSON_UNLIKELY(idx >= size()))
- {
- JSON_THROW(out_of_range::create(401, fmt::format("array index {} is out of range", idx)));
- }
-
- m_value.array->erase(m_value.array->begin() + static_cast<difference_type>(idx));
- }
- else
- {
- JSON_THROW(type_error::create(307, "cannot use erase() with", type_name()));
- }
-}
-
-json::iterator json::find(std::string_view key)
-{
- auto result = end();
-
- if (is_object())
- {
- result.m_it.object_iterator = m_value.object->find(key);
- }
-
- return result;
-}
-
-json::const_iterator json::find(std::string_view key) const
-{
- auto result = cend();
-
- if (is_object())
- {
- result.m_it.object_iterator = m_value.object->find(key);
- }
-
- return result;
-}
-
-json::size_type json::count(std::string_view key) const
-{
- // return 0 for all nonobject types
- return is_object() ? m_value.object->count(key) : 0;
-}
-
-bool json::empty() const noexcept
-{
- switch (m_type)
- {
- case value_t::null:
- {
- // null values are empty
- return true;
- }
-
- case value_t::array:
- {
- // delegate call to array_t::empty()
- return m_value.array->empty();
- }
-
- case value_t::object:
- {
- // delegate call to object_t::empty()
- return m_value.object->empty();
- }
-
- default:
- {
- // all other types are nonempty
- return false;
- }
- }
-}
-
-json::size_type json::size() const noexcept
-{
- switch (m_type)
- {
- case value_t::null:
- {
- // null values are empty
- return 0;
- }
-
- case value_t::array:
- {
- // delegate call to array_t::size()
- return m_value.array->size();
- }
-
- case value_t::object:
- {
- // delegate call to object_t::size()
- return m_value.object->size();
- }
-
- default:
- {
- // all other types have size 1
- return 1;
- }
- }
-}
-
-json::size_type json::max_size() const noexcept
-{
- switch (m_type)
- {
- case value_t::array:
- {
- // delegate call to array_t::max_size()
- return m_value.array->max_size();
- }
-
- case value_t::object:
- {
- // delegate call to std::allocator<object_t>::max_size()
- return std::allocator_traits<object_t>::max_size(*m_value.object);
- }
-
- default:
- {
- // all other types have max_size() == size()
- return size();
- }
- }
-}
-
-void json::clear() noexcept
-{
- switch (m_type)
- {
- case value_t::number_integer:
- {
- m_value.number_integer = 0;
- break;
- }
-
- case value_t::number_unsigned:
- {
- m_value.number_unsigned = 0;
- break;
- }
-
- case value_t::number_float:
- {
- m_value.number_float = 0.0;
- break;
- }
-
- case value_t::boolean:
- {
- m_value.boolean = false;
- break;
- }
-
- case value_t::string:
- {
- m_value.string->clear();
- break;
- }
-
- case value_t::array:
- {
- m_value.array->clear();
- break;
- }
-
- case value_t::object:
- {
- m_value.object->clear();
- break;
- }
-
- default:
- break;
- }
-}
-
-void json::push_back(json&& val)
-{
- // push_back only works for null objects or arrays
- if (JSON_UNLIKELY(not(is_null() or is_array())))
- {
- JSON_THROW(type_error::create(308, "cannot use push_back() with", type_name()));
- }
-
- // transform null object into an array
- if (is_null())
- {
- m_type = value_t::array;
- m_value = value_t::array;
- assert_invariant();
- }
-
- // add element to array (move semantics)
- m_value.array->push_back(std::move(val));
- // invalidate object
- val.m_type = value_t::null;
-}
-
-void json::push_back(const json& val)
-{
- // push_back only works for null objects or arrays
- if (JSON_UNLIKELY(not(is_null() or is_array())))
- {
- JSON_THROW(type_error::create(308, "cannot use push_back() with", type_name()));
- }
-
- // transform null object into an array
- if (is_null())
- {
- m_type = value_t::array;
- m_value = value_t::array;
- assert_invariant();
- }
-
- // add element to array
- m_value.array->push_back(val);
-}
-
-void json::push_back(initializer_list_t init)
-{
- if (is_object() and init.size() == 2 and (*init.begin())->is_string())
- {
- std::string key = init.begin()->moved_or_copied();
- push_back(std::pair<std::string_view, json>(key, (init.begin() + 1)->moved_or_copied()));
- }
- else
- {
- push_back(json(init));
- }
-}
-
-json::iterator json::insert(const_iterator pos, const json& val)
-{
- // insert only works for arrays
- if (JSON_LIKELY(is_array()))
- {
- // check if iterator pos fits to this JSON value
- if (JSON_UNLIKELY(pos.m_object != this))
- {
- JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
- }
-
- // insert to array and return iterator
- iterator result(this);
- result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, val);
- return result;
- }
-
- JSON_THROW(type_error::create(309, "cannot use insert() with", type_name()));
-}
-
-json::iterator json::insert(const_iterator pos, size_type cnt, const json& val)
-{
- // insert only works for arrays
- if (JSON_LIKELY(is_array()))
- {
- // check if iterator pos fits to this JSON value
- if (JSON_UNLIKELY(pos.m_object != this))
- {
- JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
- }
-
- // insert to array and return iterator
- iterator result(this);
- result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val);
- return result;
- }
-
- JSON_THROW(type_error::create(309, "cannot use insert() with", type_name()));
-}
-
-json::iterator json::insert(const_iterator pos, const_iterator first, const_iterator last)
-{
- // insert only works for arrays
- if (JSON_UNLIKELY(not is_array()))
- {
- JSON_THROW(type_error::create(309, "cannot use insert() with", type_name()));
- }
-
- // check if iterator pos fits to this JSON value
- if (JSON_UNLIKELY(pos.m_object != this))
- {
- JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
- }
-
- // check if range iterators belong to the same JSON object
- if (JSON_UNLIKELY(first.m_object != last.m_object))
- {
- JSON_THROW(invalid_iterator::create(210, "iterators do not fit"));
- }
-
- if (JSON_UNLIKELY(first.m_object == this))
- {
- JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container"));
- }
-
- // insert to array and return iterator
- iterator result(this);
- result.m_it.array_iterator = m_value.array->insert(
- pos.m_it.array_iterator,
- first.m_it.array_iterator,
- last.m_it.array_iterator);
- return result;
-}
-
-json::iterator json::insert(const_iterator pos, initializer_list_t ilist)
-{
- // insert only works for arrays
- if (JSON_UNLIKELY(not is_array()))
- {
- JSON_THROW(type_error::create(309, "cannot use insert() with", type_name()));
- }
-
- // check if iterator pos fits to this JSON value
- if (JSON_UNLIKELY(pos.m_object != this))
- {
- JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
- }
-
- // insert to array and return iterator
- iterator result(this);
- result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, ilist.begin(), ilist.end());
- return result;
-}
-
-void json::insert(const_iterator first, const_iterator last)
-{
- // insert only works for objects
- if (JSON_UNLIKELY(not is_object()))
- {
- JSON_THROW(type_error::create(309, "cannot use insert() with", type_name()));
- }
-
- // check if range iterators belong to the same JSON object
- if (JSON_UNLIKELY(first.m_object != last.m_object))
- {
- JSON_THROW(invalid_iterator::create(210, "iterators do not fit"));
- }
-
- // passed iterators must belong to objects
- if (JSON_UNLIKELY(not first.m_object->is_object()))
- {
- JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects"));
- }
-
- for (auto it = first.m_it.object_iterator, end = last.m_it.object_iterator; it != end; ++it)
- {
- m_value.object->insert(std::make_pair(it->first(), it->second));
- }
-}
-
-void json::update(const_reference j)
-{
- // implicitly convert null value to an empty object
- if (is_null())
- {
- m_type = value_t::object;
- m_value.object = create<object_t>();
- assert_invariant();
- }
-
- if (JSON_UNLIKELY(not is_object()))
- {
- JSON_THROW(type_error::create(312, "cannot use update() with", type_name()));
- }
- if (JSON_UNLIKELY(not j.is_object()))
- {
- JSON_THROW(type_error::create(312, "cannot use update() with", j.type_name()));
- }
-
- for (auto it = j.cbegin(); it != j.cend(); ++it)
- {
- m_value.object->operator[](it.key()) = it.value();
- }
-}
-
-void json::update(const_iterator first, const_iterator last)
-{
- // implicitly convert null value to an empty object
- if (is_null())
- {
- m_type = value_t::object;
- m_value.object = create<object_t>();
- assert_invariant();
- }
-
- if (JSON_UNLIKELY(not is_object()))
- {
- JSON_THROW(type_error::create(312, "cannot use update() with", type_name()));
- }
-
- // check if range iterators belong to the same JSON object
- if (JSON_UNLIKELY(first.m_object != last.m_object))
- {
- JSON_THROW(invalid_iterator::create(210, "iterators do not fit"));
- }
-
- // passed iterators must belong to objects
- if (JSON_UNLIKELY(not first.m_object->is_object()
- or not last.m_object->is_object()))
- {
- JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects"));
- }
-
- for (auto it = first; it != last; ++it)
- {
- m_value.object->operator[](it.key()) = it.value();
- }
-}
-
-bool operator==(json::const_reference lhs, json::const_reference rhs) noexcept
-{
- const auto lhs_type = lhs.type();
- const auto rhs_type = rhs.type();
-
- if (lhs_type == rhs_type)
- {
- switch (lhs_type)
- {
- case json::value_t::array:
- return (*lhs.m_value.array == *rhs.m_value.array);
-
- case json::value_t::object:
- return (*lhs.m_value.object == *rhs.m_value.object);
-
- case json::value_t::null:
- return true;
-
- case json::value_t::string:
- return (*lhs.m_value.string == *rhs.m_value.string);
-
- case json::value_t::boolean:
- return (lhs.m_value.boolean == rhs.m_value.boolean);
-
- case json::value_t::number_integer:
- return (lhs.m_value.number_integer == rhs.m_value.number_integer);
-
- case json::value_t::number_unsigned:
- return (lhs.m_value.number_unsigned == rhs.m_value.number_unsigned);
-
- case json::value_t::number_float:
- return (lhs.m_value.number_float == rhs.m_value.number_float);
-
- default:
- return false;
- }
- }
- else if (lhs_type == json::value_t::number_integer and rhs_type == json::value_t::number_float)
- {
- return (static_cast<double>(lhs.m_value.number_integer) == rhs.m_value.number_float);
- }
- else if (lhs_type == json::value_t::number_float and rhs_type == json::value_t::number_integer)
- {
- return (lhs.m_value.number_float == static_cast<double>(rhs.m_value.number_integer));
- }
- else if (lhs_type == json::value_t::number_unsigned and rhs_type == json::value_t::number_float)
- {
- return (static_cast<double>(lhs.m_value.number_unsigned) == rhs.m_value.number_float);
- }
- else if (lhs_type == json::value_t::number_float and rhs_type == json::value_t::number_unsigned)
- {
- return (lhs.m_value.number_float == static_cast<double>(rhs.m_value.number_unsigned));
- }
- else if (lhs_type == json::value_t::number_unsigned and rhs_type == json::value_t::number_integer)
- {
- return (static_cast<int64_t>(lhs.m_value.number_unsigned) == rhs.m_value.number_integer);
- }
- else if (lhs_type == json::value_t::number_integer and rhs_type == json::value_t::number_unsigned)
- {
- return (lhs.m_value.number_integer == static_cast<int64_t>(rhs.m_value.number_unsigned));
- }
-
- return false;
-}
-
-bool operator<(json::const_reference lhs, json::const_reference rhs) noexcept
-{
- const auto lhs_type = lhs.type();
- const auto rhs_type = rhs.type();
-
- if (lhs_type == rhs_type)
- {
- switch (lhs_type)
- {
- case json::value_t::array:
- return (*lhs.m_value.array) < (*rhs.m_value.array);
-
- case json::value_t::object:
- return *lhs.m_value.object < *rhs.m_value.object;
-
- case json::value_t::null:
- return false;
-
- case json::value_t::string:
- return *lhs.m_value.string < *rhs.m_value.string;
-
- case json::value_t::boolean:
- return lhs.m_value.boolean < rhs.m_value.boolean;
-
- case json::value_t::number_integer:
- return lhs.m_value.number_integer < rhs.m_value.number_integer;
-
- case json::value_t::number_unsigned:
- return lhs.m_value.number_unsigned < rhs.m_value.number_unsigned;
-
- case json::value_t::number_float:
- return lhs.m_value.number_float < rhs.m_value.number_float;
-
- default:
- return false;
- }
- }
- else if (lhs_type == json::value_t::number_integer and rhs_type == json::value_t::number_float)
- {
- return static_cast<double>(lhs.m_value.number_integer) < rhs.m_value.number_float;
- }
- else if (lhs_type == json::value_t::number_float and rhs_type == json::value_t::number_integer)
- {
- return lhs.m_value.number_float < static_cast<double>(rhs.m_value.number_integer);
- }
- else if (lhs_type == json::value_t::number_unsigned and rhs_type == json::value_t::number_float)
- {
- return static_cast<double>(lhs.m_value.number_unsigned) < rhs.m_value.number_float;
- }
- else if (lhs_type == json::value_t::number_float and rhs_type == json::value_t::number_unsigned)
- {
- return lhs.m_value.number_float < static_cast<double>(rhs.m_value.number_unsigned);
- }
- else if (lhs_type == json::value_t::number_integer and rhs_type == json::value_t::number_unsigned)
- {
- return lhs.m_value.number_integer < static_cast<int64_t>(rhs.m_value.number_unsigned);
- }
- else if (lhs_type == json::value_t::number_unsigned and rhs_type == json::value_t::number_integer)
- {
- return static_cast<int64_t>(lhs.m_value.number_unsigned) < rhs.m_value.number_integer;
- }
-
- // We only reach this line if we cannot compare values. In that case,
- // we compare types. Note we have to call the operator explicitly,
- // because MSVC has problems otherwise.
- return operator<(lhs_type, rhs_type);
-}
-
-const char* json::type_name() const noexcept
-{
- {
- switch (m_type)
- {
- case value_t::null:
- return "null";
- case value_t::object:
- return "object";
- case value_t::array:
- return "array";
- case value_t::string:
- return "string";
- case value_t::boolean:
- return "boolean";
- case value_t::discarded:
- return "discarded";
- default:
- return "number";
- }
- }
-}
-
-json json::patch(const json& json_patch) const
-{
- // make a working copy to apply the patch to
- json result = *this;
-
- // the valid JSON Patch operations
- enum class patch_operations {add, remove, replace, move, copy, test, invalid};
-
- const auto get_op = [](const std::string & op)
- {
- if (op == "add")
- {
- return patch_operations::add;
- }
- if (op == "remove")
- {
- return patch_operations::remove;
- }
- if (op == "replace")
- {
- return patch_operations::replace;
- }
- if (op == "move")
- {
- return patch_operations::move;
- }
- if (op == "copy")
- {
- return patch_operations::copy;
- }
- if (op == "test")
- {
- return patch_operations::test;
- }
-
- return patch_operations::invalid;
- };
-
- // wrapper for "add" operation; add value at ptr
- const auto operation_add = [&result](json_pointer & ptr, json val)
- {
- // adding to the root of the target document means replacing it
- if (ptr.is_root())
- {
- result = val;
- }
- else
- {
- // make sure the top element of the pointer exists
- json_pointer top_pointer = ptr.top();
- if (top_pointer != ptr)
- {
- result.at(top_pointer);
- }
-
- // get reference to parent of JSON pointer ptr
- const auto last_path = ptr.pop_back();
- json& parent = result[ptr];
-
- switch (parent.m_type)
- {
- case value_t::null:
- case value_t::object:
- {
- // use operator[] to add value
- parent[last_path] = val;
- break;
- }
-
- case value_t::array:
- {
- if (last_path == "-")
- {
- // special case: append to back
- parent.push_back(val);
- }
- else
- {
- const auto idx = json_pointer::array_index(last_path);
- if (JSON_UNLIKELY(static_cast<size_type>(idx) > parent.size()))
- {
- // avoid undefined behavior
- JSON_THROW(out_of_range::create(401, fmt::format("array index {} is out of range", idx)));
- }
- else
- {
- // default case: insert add offset
- parent.insert(parent.begin() + static_cast<difference_type>(idx), val);
- }
- }
- break;
- }
-
- default:
- {
- // if there exists a parent it cannot be primitive
- assert(false); // LCOV_EXCL_LINE
- }
- }
- }
- };
-
- // wrapper for "remove" operation; remove value at ptr
- const auto operation_remove = [&result](json_pointer & ptr)
- {
- // get reference to parent of JSON pointer ptr
- const auto last_path = ptr.pop_back();
- json& parent = result.at(ptr);
-
- // remove child
- if (parent.is_object())
- {
- // perform range check
- auto it = parent.find(last_path);
- if (JSON_LIKELY(it != parent.end()))
- {
- parent.erase(it);
- }
- else
- {
- JSON_THROW(out_of_range::create(403, fmt::format("key '{}' not found", last_path)));
- }
- }
- else if (parent.is_array())
- {
- // note erase performs range check
- parent.erase(static_cast<size_type>(json_pointer::array_index(last_path)));
- }
- };
-
- // type check: top level value must be an array
- if (JSON_UNLIKELY(not json_patch.is_array()))
- {
- JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects"));
- }
-
- // iterate and apply the operations
- for (const auto& val : json_patch)
- {
- // wrapper to get a value for an operation
- const auto get_value = [&val](const std::string & op,
- const std::string & member,
- bool string_type) -> json &
- {
- // find value
- auto it = val.m_value.object->find(member);
-
- // context-sensitive error message
- const auto error_msg = (op == "op") ? "operation" : "operation '" + op + "'";
-
- // check if desired value is present
- if (JSON_UNLIKELY(it == val.m_value.object->end()))
- {
- JSON_THROW(parse_error::create(105, 0, fmt::format("{} must have member '{}'", error_msg, member)));
- }
-
- // check if result is of type string
- if (JSON_UNLIKELY(string_type and not it->second.is_string()))
- {
- JSON_THROW(parse_error::create(105, 0, fmt::format("{} must have string member '{}'", error_msg, member)));
- }
-
- // no error: return value
- return it->second;
- };
-
- // type check: every element of the array must be an object
- if (JSON_UNLIKELY(not val.is_object()))
- {
- JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects"));
- }
-
- // collect mandatory members
- const std::string op = get_value("op", "op", true);
- const std::string path = get_value(op, "path", true);
- json_pointer ptr(path);
-
- switch (get_op(op))
- {
- case patch_operations::add:
- {
- operation_add(ptr, get_value("add", "value", false));
- break;
- }
-
- case patch_operations::remove:
- {
- operation_remove(ptr);
- break;
- }
-
- case patch_operations::replace:
- {
- // the "path" location must exist - use at()
- result.at(ptr) = get_value("replace", "value", false);
- break;
- }
-
- case patch_operations::move:
- {
- const std::string from_path = get_value("move", "from", true);
- json_pointer from_ptr(from_path);
-
- // the "from" location must exist - use at()
- json v = result.at(from_ptr);
-
- // The move operation is functionally identical to a
- // "remove" operation on the "from" location, followed
- // immediately by an "add" operation at the target
- // location with the value that was just removed.
- operation_remove(from_ptr);
- operation_add(ptr, v);
- break;
- }
-
- case patch_operations::copy:
- {
- const std::string from_path = get_value("copy", "from", true);
- const json_pointer from_ptr(from_path);
-
- // the "from" location must exist - use at()
- json v = result.at(from_ptr);
-
- // The copy is functionally identical to an "add"
- // operation at the target location using the value
- // specified in the "from" member.
- operation_add(ptr, v);
- break;
- }
-
- case patch_operations::test:
- {
- bool success = false;
- JSON_TRY
- {
- // check if "value" matches the one at "path"
- // the "path" location must exist - use at()
- success = (result.at(ptr) == get_value("test", "value", false));
- }
- JSON_CATCH (out_of_range&)
- {
- // ignore out of range errors: success remains false
- }
-
- // throw an exception if test fails
- if (JSON_UNLIKELY(not success))
- {
- JSON_THROW(other_error::create(501, fmt::format("unsuccessful: {}", val.dump())));
- }
-
- break;
- }
-
- case patch_operations::invalid:
- {
- // op must be "add", "remove", "replace", "move", "copy", or
- // "test"
- JSON_THROW(parse_error::create(105, 0, fmt::format("operation value '{}' is invalid", op)));
- }
- }
- }
-
- return result;
-}
-
-json json::diff(const json& source, const json& target,
- const std::string& path)
-{
- // the patch
- json result(value_t::array);
-
- // if the values are the same, return empty patch
- if (source == target)
- {
- return result;
- }
-
- if (source.type() != target.type())
- {
- // different types: replace value
- result.push_back(
- {
- {"op", "replace"}, {"path", path}, {"value", target}
- });
- }
- else
- {
- switch (source.type())
- {
- case value_t::array:
- {
- // first pass: traverse common elements
- std::size_t i = 0;
- while (i < source.size() and i < target.size())
- {
- // recursive call to compare array values at index i
- auto temp_diff = diff(source[i], target[i], path + "/" + std::to_string(i));
- result.insert(result.end(), temp_diff.begin(), temp_diff.end());
- ++i;
- }
-
- // i now reached the end of at least one array
- // in a second pass, traverse the remaining elements
-
- // remove my remaining elements
- const auto end_index = static_cast<difference_type>(result.size());
- while (i < source.size())
- {
- // add operations in reverse order to avoid invalid
- // indices
- result.insert(result.begin() + end_index, object(
- {
- {"op", "remove"},
- {"path", path + "/" + std::to_string(i)}
- }));
- ++i;
- }
-
- // add other remaining elements
- while (i < target.size())
- {
- result.push_back(
- {
- {"op", "add"},
- {"path", path + "/" + std::to_string(i)},
- {"value", target[i]}
- });
- ++i;
- }
-
- break;
- }
-
- case value_t::object:
- {
- // first pass: traverse this object's elements
- for (auto it = source.cbegin(); it != source.cend(); ++it)
- {
- // escape the key name to be used in a JSON patch
- const auto key = json_pointer::escape(std::string{it.key()});
-
- if (target.find(it.key()) != target.end())
- {
- // recursive call to compare object values at key it
- auto temp_diff = diff(it.value(), target[it.key()], path + "/" + key);
- result.insert(result.end(), temp_diff.begin(), temp_diff.end());
- }
- else
- {
- // found a key that is not in o -> remove it
- result.push_back(object(
- {
- {"op", "remove"}, {"path", path + "/" + key}
- }));
- }
- }
-
- // second pass: traverse other object's elements
- for (auto it = target.cbegin(); it != target.cend(); ++it)
- {
- if (source.find(it.key()) == source.end())
- {
- // found a key that is not in this -> add it
- const auto key = json_pointer::escape(std::string{it.key()});
- result.push_back(
- {
- {"op", "add"}, {"path", path + "/" + key},
- {"value", it.value()}
- });
- }
- }
-
- break;
- }
-
- default:
- {
- // both primitive type: replace value
- result.push_back(
- {
- {"op", "replace"}, {"path", path}, {"value", target}
- });
- break;
- }
- }
- }
-
- return result;
-}
-
-void json::merge_patch(const json& patch)
-{
- if (patch.is_object())
- {
- if (not is_object())
- {
- *this = object();
- }
- for (auto it = patch.begin(); it != patch.end(); ++it)
- {
- if (it.value().is_null())
- {
- erase(it.key());
- }
- else
- {
- operator[](it.key()).merge_patch(it.value());
- }
- }
- }
- else
- {
- *this = patch;
- }
-}
-
-} // namespace wpi
diff --git a/wpiutil/src/main/native/cpp/json_binary_reader.cpp b/wpiutil/src/main/native/cpp/json_binary_reader.cpp
deleted file mode 100644
index cee5e39..0000000
--- a/wpiutil/src/main/native/cpp/json_binary_reader.cpp
+++ /dev/null
@@ -1,1415 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Modifications Copyright (c) 2017-2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
-/*
- __ _____ _____ _____
- __| | __| | | | JSON for Modern C++
-| | |__ | | | | | | version 3.1.2
-|_____|_____|_____|_|___| https://github.com/nlohmann/json
-
-Licensed under the MIT License <http://opensource.org/licenses/MIT>.
-Copyright (c) 2013-2018 Niels Lohmann <http://nlohmann.me>.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
-#define WPI_JSON_IMPLEMENTATION
-#include "wpi/json.h"
-
-#include <cmath> // ldexp
-
-#include "fmt/format.h"
-#include "wpi/raw_istream.h"
-
-namespace wpi {
-
-/*!
-@brief deserialization of CBOR and MessagePack values
-*/
-class json::binary_reader
-{
- public:
- /*!
- @brief create a binary reader
-
- @param[in] adapter input adapter to read from
- */
- explicit binary_reader(raw_istream& s) : is(s)
- {
- }
-
- /*!
- @brief create a JSON value from CBOR input
-
- @param[in] strict whether to expect the input to be consumed completed
- @return JSON value created from CBOR input
-
- @throw parse_error.110 if input ended unexpectedly or the end of file was
- not reached when @a strict was set to true
- @throw parse_error.112 if unsupported byte was read
- */
- json parse_cbor(const bool strict)
- {
- const auto res = parse_cbor_internal();
- if (strict)
- {
- get();
- expect_eof();
- }
- return res;
- }
-
- /*!
- @brief create a JSON value from MessagePack input
-
- @param[in] strict whether to expect the input to be consumed completed
- @return JSON value created from MessagePack input
-
- @throw parse_error.110 if input ended unexpectedly or the end of file was
- not reached when @a strict was set to true
- @throw parse_error.112 if unsupported byte was read
- */
- json parse_msgpack(const bool strict)
- {
- const auto res = parse_msgpack_internal();
- if (strict)
- {
- get();
- expect_eof();
- }
- return res;
- }
-
- /*!
- @brief create a JSON value from UBJSON input
-
- @param[in] strict whether to expect the input to be consumed completed
- @return JSON value created from UBJSON input
-
- @throw parse_error.110 if input ended unexpectedly or the end of file was
- not reached when @a strict was set to true
- @throw parse_error.112 if unsupported byte was read
- */
- json parse_ubjson(const bool strict)
- {
- const auto res = parse_ubjson_internal();
- if (strict)
- {
- get_ignore_noop();
- expect_eof();
- }
- return res;
- }
-
- /*!
- @brief determine system byte order
-
- @return true if and only if system's byte order is little endian
-
- @note from http://stackoverflow.com/a/1001328/266378
- */
- static bool little_endianess(int num = 1) noexcept
- {
- return (*reinterpret_cast<char*>(&num) == 1);
- }
-
- private:
- /*!
- @param[in] get_char whether a new character should be retrieved from the
- input (true, default) or whether the last read
- character should be considered instead
- */
- json parse_cbor_internal(const bool get_char = true);
-
- json parse_msgpack_internal();
-
- /*!
- @param[in] get_char whether a new character should be retrieved from the
- input (true, default) or whether the last read
- character should be considered instead
- */
- json parse_ubjson_internal(const bool get_char = true)
- {
- return get_ubjson_value(get_char ? get_ignore_noop() : current);
- }
-
- /*!
- @brief get next character from the input
-
- This function provides the interface to the used input adapter. It does
- not throw in case the input reached EOF, but returns a -'ve valued
- `std::char_traits<char>::eof()` in that case.
-
- @return character read from the input
- */
- int get()
- {
- ++chars_read;
- unsigned char c;
- is.read(c);
- if (is.has_error())
- {
- current = std::char_traits<char>::eof();
- }
- else
- {
- current = c;
- }
- return current;
- }
-
- /*!
- @return character read from the input after ignoring all 'N' entries
- */
- int get_ignore_noop()
- {
- do
- {
- get();
- }
- while (current == 'N');
-
- return current;
- }
-
- /*
- @brief read a number from the input
-
- @tparam NumberType the type of the number
-
- @return number of type @a NumberType
-
- @note This function needs to respect the system's endianess, because
- bytes in CBOR and MessagePack are stored in network order (big
- endian) and therefore need reordering on little endian systems.
-
- @throw parse_error.110 if input has less than `sizeof(NumberType)` bytes
- */
- template<typename NumberType> NumberType get_number()
- {
- // step 1: read input into array with system's byte order
- std::array<uint8_t, sizeof(NumberType)> vec;
- for (std::size_t i = 0; i < sizeof(NumberType); ++i)
- {
- get();
- unexpect_eof();
-
- // reverse byte order prior to conversion if necessary
- if (is_little_endian)
- {
- vec[sizeof(NumberType) - i - 1] = static_cast<uint8_t>(current);
- }
- else
- {
- vec[i] = static_cast<uint8_t>(current); // LCOV_EXCL_LINE
- }
- }
-
- // step 2: convert array into number of type T and return
- NumberType result;
- std::memcpy(&result, vec.data(), sizeof(NumberType));
- return result;
- }
-
- /*!
- @brief create a string by reading characters from the input
-
- @param[in] len number of bytes to read
-
- @note We can not reserve @a len bytes for the result, because @a len
- may be too large. Usually, @ref unexpect_eof() detects the end of
- the input before we run out of string memory.
-
- @return string created by reading @a len bytes
-
- @throw parse_error.110 if input has less than @a len bytes
- */
- template<typename NumberType>
- std::string get_string(const NumberType len)
- {
- std::string result;
- std::generate_n(std::back_inserter(result), len, [this]()
- {
- get();
- unexpect_eof();
- return static_cast<char>(current);
- });
- return result;
- }
-
- /*!
- @brief reads a CBOR string
-
- This function first reads starting bytes to determine the expected
- string length and then copies this number of bytes into a string.
- Additionally, CBOR's strings with indefinite lengths are supported.
-
- @return string
-
- @throw parse_error.110 if input ended
- @throw parse_error.113 if an unexpected byte is read
- */
- std::string get_cbor_string();
-
- template<typename NumberType>
- json get_cbor_array(const NumberType len)
- {
- json result = value_t::array;
- std::generate_n(std::back_inserter(*result.m_value.array), len, [this]()
- {
- return parse_cbor_internal();
- });
- return result;
- }
-
- template<typename NumberType>
- json get_cbor_object(const NumberType len)
- {
- json result = value_t::object;
- for (NumberType i = 0; i < len; ++i)
- {
- get();
- auto key = get_cbor_string();
- (*result.m_value.object)[key] = parse_cbor_internal();
- }
- return result;
- }
-
- /*!
- @brief reads a MessagePack string
-
- This function first reads starting bytes to determine the expected
- string length and then copies this number of bytes into a string.
-
- @return string
-
- @throw parse_error.110 if input ended
- @throw parse_error.113 if an unexpected byte is read
- */
- std::string get_msgpack_string();
-
- template<typename NumberType>
- json get_msgpack_array(const NumberType len)
- {
- json result = value_t::array;
- std::generate_n(std::back_inserter(*result.m_value.array), len, [this]()
- {
- return parse_msgpack_internal();
- });
- return result;
- }
-
- template<typename NumberType>
- json get_msgpack_object(const NumberType len)
- {
- json result = value_t::object;
- for (NumberType i = 0; i < len; ++i)
- {
- get();
- auto key = get_msgpack_string();
- (*result.m_value.object)[key] = parse_msgpack_internal();
- }
- return result;
- }
-
- /*!
- @brief reads a UBJSON string
-
- This function is either called after reading the 'S' byte explicitly
- indicating a string, or in case of an object key where the 'S' byte can be
- left out.
-
- @param[in] get_char whether a new character should be retrieved from the
- input (true, default) or whether the last read
- character should be considered instead
-
- @return string
-
- @throw parse_error.110 if input ended
- @throw parse_error.113 if an unexpected byte is read
- */
- std::string get_ubjson_string(const bool get_char = true);
-
- /*!
- @brief determine the type and size for a container
-
- In the optimized UBJSON format, a type and a size can be provided to allow
- for a more compact representation.
-
- @return pair of the size and the type
- */
- std::pair<std::size_t, int> get_ubjson_size_type();
-
- json get_ubjson_value(const int prefix);
-
- json get_ubjson_array();
-
- json get_ubjson_object();
-
- /*!
- @brief throw if end of input is not reached
- @throw parse_error.110 if input not ended
- */
- void expect_eof() const
- {
- if (JSON_UNLIKELY(current != std::char_traits<char>::eof()))
- {
- JSON_THROW(parse_error::create(110, chars_read, "expected end of input"));
- }
- }
-
- /*!
- @briefthrow if end of input is reached
- @throw parse_error.110 if input ended
- */
- void unexpect_eof() const
- {
- if (JSON_UNLIKELY(current == std::char_traits<char>::eof()))
- {
- JSON_THROW(parse_error::create(110, chars_read, "unexpected end of input"));
- }
- }
-
- private:
- /// input adapter
- raw_istream& is;
-
- /// the current character
- int current = std::char_traits<char>::eof();
-
- /// the number of characters read
- std::size_t chars_read = 0;
-
- /// whether we can assume little endianess
- const bool is_little_endian = little_endianess();
-};
-
-json json::binary_reader::parse_cbor_internal(const bool get_char)
-{
- switch (get_char ? get() : current)
- {
- // EOF
- case std::char_traits<char>::eof():
- JSON_THROW(parse_error::create(110, chars_read, "unexpected end of input"));
-
- // Integer 0x00..0x17 (0..23)
- case 0x00:
- case 0x01:
- case 0x02:
- case 0x03:
- case 0x04:
- case 0x05:
- case 0x06:
- case 0x07:
- case 0x08:
- case 0x09:
- case 0x0A:
- case 0x0B:
- case 0x0C:
- case 0x0D:
- case 0x0E:
- case 0x0F:
- case 0x10:
- case 0x11:
- case 0x12:
- case 0x13:
- case 0x14:
- case 0x15:
- case 0x16:
- case 0x17:
- return static_cast<uint64_t>(current);
-
- case 0x18: // Unsigned integer (one-byte uint8_t follows)
- return get_number<uint8_t>();
-
- case 0x19: // Unsigned integer (two-byte uint16_t follows)
- return get_number<uint16_t>();
-
- case 0x1A: // Unsigned integer (four-byte uint32_t follows)
- return get_number<uint32_t>();
-
- case 0x1B: // Unsigned integer (eight-byte uint64_t follows)
- return get_number<uint64_t>();
-
- // Negative integer -1-0x00..-1-0x17 (-1..-24)
- case 0x20:
- case 0x21:
- case 0x22:
- case 0x23:
- case 0x24:
- case 0x25:
- case 0x26:
- case 0x27:
- case 0x28:
- case 0x29:
- case 0x2A:
- case 0x2B:
- case 0x2C:
- case 0x2D:
- case 0x2E:
- case 0x2F:
- case 0x30:
- case 0x31:
- case 0x32:
- case 0x33:
- case 0x34:
- case 0x35:
- case 0x36:
- case 0x37:
- return static_cast<int8_t>(0x20 - 1 - current);
-
- case 0x38: // Negative integer (one-byte uint8_t follows)
- {
- return static_cast<int64_t>(-1) - get_number<uint8_t>();
- }
-
- case 0x39: // Negative integer -1-n (two-byte uint16_t follows)
- {
- return static_cast<int64_t>(-1) - get_number<uint16_t>();
- }
-
- case 0x3A: // Negative integer -1-n (four-byte uint32_t follows)
- {
- return static_cast<int64_t>(-1) - get_number<uint32_t>();
- }
-
- case 0x3B: // Negative integer -1-n (eight-byte uint64_t follows)
- {
- return static_cast<int64_t>(-1) -
- static_cast<int64_t>(get_number<uint64_t>());
- }
-
- // UTF-8 string (0x00..0x17 bytes follow)
- case 0x60:
- case 0x61:
- case 0x62:
- case 0x63:
- case 0x64:
- case 0x65:
- case 0x66:
- case 0x67:
- case 0x68:
- case 0x69:
- case 0x6A:
- case 0x6B:
- case 0x6C:
- case 0x6D:
- case 0x6E:
- case 0x6F:
- case 0x70:
- case 0x71:
- case 0x72:
- case 0x73:
- case 0x74:
- case 0x75:
- case 0x76:
- case 0x77:
- case 0x78: // UTF-8 string (one-byte uint8_t for n follows)
- case 0x79: // UTF-8 string (two-byte uint16_t for n follow)
- case 0x7A: // UTF-8 string (four-byte uint32_t for n follow)
- case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow)
- case 0x7F: // UTF-8 string (indefinite length)
- {
- return get_cbor_string();
- }
-
- // array (0x00..0x17 data items follow)
- case 0x80:
- case 0x81:
- case 0x82:
- case 0x83:
- case 0x84:
- case 0x85:
- case 0x86:
- case 0x87:
- case 0x88:
- case 0x89:
- case 0x8A:
- case 0x8B:
- case 0x8C:
- case 0x8D:
- case 0x8E:
- case 0x8F:
- case 0x90:
- case 0x91:
- case 0x92:
- case 0x93:
- case 0x94:
- case 0x95:
- case 0x96:
- case 0x97:
- {
- return get_cbor_array(current & 0x1F);
- }
-
- case 0x98: // array (one-byte uint8_t for n follows)
- {
- return get_cbor_array(get_number<uint8_t>());
- }
-
- case 0x99: // array (two-byte uint16_t for n follow)
- {
- return get_cbor_array(get_number<uint16_t>());
- }
-
- case 0x9A: // array (four-byte uint32_t for n follow)
- {
- return get_cbor_array(get_number<uint32_t>());
- }
-
- case 0x9B: // array (eight-byte uint64_t for n follow)
- {
- return get_cbor_array(get_number<uint64_t>());
- }
-
- case 0x9F: // array (indefinite length)
- {
- json result = value_t::array;
- while (get() != 0xFF)
- {
- result.push_back(parse_cbor_internal(false));
- }
- return result;
- }
-
- // map (0x00..0x17 pairs of data items follow)
- case 0xA0:
- case 0xA1:
- case 0xA2:
- case 0xA3:
- case 0xA4:
- case 0xA5:
- case 0xA6:
- case 0xA7:
- case 0xA8:
- case 0xA9:
- case 0xAA:
- case 0xAB:
- case 0xAC:
- case 0xAD:
- case 0xAE:
- case 0xAF:
- case 0xB0:
- case 0xB1:
- case 0xB2:
- case 0xB3:
- case 0xB4:
- case 0xB5:
- case 0xB6:
- case 0xB7:
- {
- return get_cbor_object(current & 0x1F);
- }
-
- case 0xB8: // map (one-byte uint8_t for n follows)
- {
- return get_cbor_object(get_number<uint8_t>());
- }
-
- case 0xB9: // map (two-byte uint16_t for n follow)
- {
- return get_cbor_object(get_number<uint16_t>());
- }
-
- case 0xBA: // map (four-byte uint32_t for n follow)
- {
- return get_cbor_object(get_number<uint32_t>());
- }
-
- case 0xBB: // map (eight-byte uint64_t for n follow)
- {
- return get_cbor_object(get_number<uint64_t>());
- }
-
- case 0xBF: // map (indefinite length)
- {
- json result = value_t::object;
- while (get() != 0xFF)
- {
- auto key = get_cbor_string();
- result[key] = parse_cbor_internal();
- }
- return result;
- }
-
- case 0xF4: // false
- {
- return false;
- }
-
- case 0xF5: // true
- {
- return true;
- }
-
- case 0xF6: // null
- {
- return value_t::null;
- }
-
- case 0xF9: // Half-Precision Float (two-byte IEEE 754)
- {
- const int byte1 = get();
- unexpect_eof();
- const int byte2 = get();
- unexpect_eof();
-
- // code from RFC 7049, Appendix D, Figure 3:
- // As half-precision floating-point numbers were only added
- // to IEEE 754 in 2008, today's programming platforms often
- // still only have limited support for them. It is very
- // easy to include at least decoding support for them even
- // without such support. An example of a small decoder for
- // half-precision floating-point numbers in the C language
- // is shown in Fig. 3.
- const int half = (byte1 << 8) + byte2;
- const int exp = (half >> 10) & 0x1F;
- const int mant = half & 0x3FF;
- double val;
- if (exp == 0)
- {
- val = std::ldexp(mant, -24);
- }
- else if (exp != 31)
- {
- val = std::ldexp(mant + 1024, exp - 25);
- }
- else
- {
- val = (mant == 0) ? std::numeric_limits<double>::infinity()
- : std::numeric_limits<double>::quiet_NaN();
- }
- return (half & 0x8000) != 0 ? -val : val;
- }
-
- case 0xFA: // Single-Precision Float (four-byte IEEE 754)
- {
- return get_number<float>();
- }
-
- case 0xFB: // Double-Precision Float (eight-byte IEEE 754)
- {
- return get_number<double>();
- }
-
- default: // anything else (0xFF is handled inside the other types)
- {
- JSON_THROW(parse_error::create(112, chars_read, fmt::format("error reading CBOR; last byte: {:#02x}", current)));
- }
- }
-}
-
-json json::binary_reader::parse_msgpack_internal()
-{
- switch (get())
- {
- // EOF
- case std::char_traits<char>::eof():
- JSON_THROW(parse_error::create(110, chars_read, "unexpected end of input"));
-
- // positive fixint
- case 0x00:
- case 0x01:
- case 0x02:
- case 0x03:
- case 0x04:
- case 0x05:
- case 0x06:
- case 0x07:
- case 0x08:
- case 0x09:
- case 0x0A:
- case 0x0B:
- case 0x0C:
- case 0x0D:
- case 0x0E:
- case 0x0F:
- case 0x10:
- case 0x11:
- case 0x12:
- case 0x13:
- case 0x14:
- case 0x15:
- case 0x16:
- case 0x17:
- case 0x18:
- case 0x19:
- case 0x1A:
- case 0x1B:
- case 0x1C:
- case 0x1D:
- case 0x1E:
- case 0x1F:
- case 0x20:
- case 0x21:
- case 0x22:
- case 0x23:
- case 0x24:
- case 0x25:
- case 0x26:
- case 0x27:
- case 0x28:
- case 0x29:
- case 0x2A:
- case 0x2B:
- case 0x2C:
- case 0x2D:
- case 0x2E:
- case 0x2F:
- case 0x30:
- case 0x31:
- case 0x32:
- case 0x33:
- case 0x34:
- case 0x35:
- case 0x36:
- case 0x37:
- case 0x38:
- case 0x39:
- case 0x3A:
- case 0x3B:
- case 0x3C:
- case 0x3D:
- case 0x3E:
- case 0x3F:
- case 0x40:
- case 0x41:
- case 0x42:
- case 0x43:
- case 0x44:
- case 0x45:
- case 0x46:
- case 0x47:
- case 0x48:
- case 0x49:
- case 0x4A:
- case 0x4B:
- case 0x4C:
- case 0x4D:
- case 0x4E:
- case 0x4F:
- case 0x50:
- case 0x51:
- case 0x52:
- case 0x53:
- case 0x54:
- case 0x55:
- case 0x56:
- case 0x57:
- case 0x58:
- case 0x59:
- case 0x5A:
- case 0x5B:
- case 0x5C:
- case 0x5D:
- case 0x5E:
- case 0x5F:
- case 0x60:
- case 0x61:
- case 0x62:
- case 0x63:
- case 0x64:
- case 0x65:
- case 0x66:
- case 0x67:
- case 0x68:
- case 0x69:
- case 0x6A:
- case 0x6B:
- case 0x6C:
- case 0x6D:
- case 0x6E:
- case 0x6F:
- case 0x70:
- case 0x71:
- case 0x72:
- case 0x73:
- case 0x74:
- case 0x75:
- case 0x76:
- case 0x77:
- case 0x78:
- case 0x79:
- case 0x7A:
- case 0x7B:
- case 0x7C:
- case 0x7D:
- case 0x7E:
- case 0x7F:
- return static_cast<uint64_t>(current);
-
- // fixmap
- case 0x80:
- case 0x81:
- case 0x82:
- case 0x83:
- case 0x84:
- case 0x85:
- case 0x86:
- case 0x87:
- case 0x88:
- case 0x89:
- case 0x8A:
- case 0x8B:
- case 0x8C:
- case 0x8D:
- case 0x8E:
- case 0x8F:
- {
- return get_msgpack_object(current & 0x0F);
- }
-
- // fixarray
- case 0x90:
- case 0x91:
- case 0x92:
- case 0x93:
- case 0x94:
- case 0x95:
- case 0x96:
- case 0x97:
- case 0x98:
- case 0x99:
- case 0x9A:
- case 0x9B:
- case 0x9C:
- case 0x9D:
- case 0x9E:
- case 0x9F:
- {
- return get_msgpack_array(current & 0x0F);
- }
-
- // fixstr
- case 0xA0:
- case 0xA1:
- case 0xA2:
- case 0xA3:
- case 0xA4:
- case 0xA5:
- case 0xA6:
- case 0xA7:
- case 0xA8:
- case 0xA9:
- case 0xAA:
- case 0xAB:
- case 0xAC:
- case 0xAD:
- case 0xAE:
- case 0xAF:
- case 0xB0:
- case 0xB1:
- case 0xB2:
- case 0xB3:
- case 0xB4:
- case 0xB5:
- case 0xB6:
- case 0xB7:
- case 0xB8:
- case 0xB9:
- case 0xBA:
- case 0xBB:
- case 0xBC:
- case 0xBD:
- case 0xBE:
- case 0xBF:
- return get_msgpack_string();
-
- case 0xC0: // nil
- return value_t::null;
-
- case 0xC2: // false
- return false;
-
- case 0xC3: // true
- return true;
-
- case 0xCA: // float 32
- return get_number<float>();
-
- case 0xCB: // float 64
- return get_number<double>();
-
- case 0xCC: // uint 8
- return get_number<uint8_t>();
-
- case 0xCD: // uint 16
- return get_number<uint16_t>();
-
- case 0xCE: // uint 32
- return get_number<uint32_t>();
-
- case 0xCF: // uint 64
- return get_number<uint64_t>();
-
- case 0xD0: // int 8
- return get_number<int8_t>();
-
- case 0xD1: // int 16
- return get_number<int16_t>();
-
- case 0xD2: // int 32
- return get_number<int32_t>();
-
- case 0xD3: // int 64
- return get_number<int64_t>();
-
- case 0xD9: // str 8
- case 0xDA: // str 16
- case 0xDB: // str 32
- return get_msgpack_string();
-
- case 0xDC: // array 16
- {
- return get_msgpack_array(get_number<uint16_t>());
- }
-
- case 0xDD: // array 32
- {
- return get_msgpack_array(get_number<uint32_t>());
- }
-
- case 0xDE: // map 16
- {
- return get_msgpack_object(get_number<uint16_t>());
- }
-
- case 0xDF: // map 32
- {
- return get_msgpack_object(get_number<uint32_t>());
- }
-
- // positive fixint
- case 0xE0:
- case 0xE1:
- case 0xE2:
- case 0xE3:
- case 0xE4:
- case 0xE5:
- case 0xE6:
- case 0xE7:
- case 0xE8:
- case 0xE9:
- case 0xEA:
- case 0xEB:
- case 0xEC:
- case 0xED:
- case 0xEE:
- case 0xEF:
- case 0xF0:
- case 0xF1:
- case 0xF2:
- case 0xF3:
- case 0xF4:
- case 0xF5:
- case 0xF6:
- case 0xF7:
- case 0xF8:
- case 0xF9:
- case 0xFA:
- case 0xFB:
- case 0xFC:
- case 0xFD:
- case 0xFE:
- case 0xFF:
- return static_cast<int8_t>(current);
-
- default: // anything else
- {
- JSON_THROW(parse_error::create(112, chars_read,
- fmt::format("error reading MessagePack; last byte: {:#02x}", current)));
- }
- }
-}
-
-std::string json::binary_reader::get_cbor_string()
-{
- unexpect_eof();
-
- switch (current)
- {
- // UTF-8 string (0x00..0x17 bytes follow)
- case 0x60:
- case 0x61:
- case 0x62:
- case 0x63:
- case 0x64:
- case 0x65:
- case 0x66:
- case 0x67:
- case 0x68:
- case 0x69:
- case 0x6A:
- case 0x6B:
- case 0x6C:
- case 0x6D:
- case 0x6E:
- case 0x6F:
- case 0x70:
- case 0x71:
- case 0x72:
- case 0x73:
- case 0x74:
- case 0x75:
- case 0x76:
- case 0x77:
- {
- return get_string(current & 0x1F);
- }
-
- case 0x78: // UTF-8 string (one-byte uint8_t for n follows)
- {
- return get_string(get_number<uint8_t>());
- }
-
- case 0x79: // UTF-8 string (two-byte uint16_t for n follow)
- {
- return get_string(get_number<uint16_t>());
- }
-
- case 0x7A: // UTF-8 string (four-byte uint32_t for n follow)
- {
- return get_string(get_number<uint32_t>());
- }
-
- case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow)
- {
- return get_string(get_number<uint64_t>());
- }
-
- case 0x7F: // UTF-8 string (indefinite length)
- {
- std::string result;
- while (get() != 0xFF)
- {
- result.append(get_cbor_string());
- }
- return result;
- }
-
- default:
- {
- JSON_THROW(parse_error::create(113, chars_read,
- fmt::format("expected a CBOR string; last byte: {:#02x}", current)));
- }
- }
-}
-
-std::string json::binary_reader::get_msgpack_string()
-{
- unexpect_eof();
-
- switch (current)
- {
- // fixstr
- case 0xA0:
- case 0xA1:
- case 0xA2:
- case 0xA3:
- case 0xA4:
- case 0xA5:
- case 0xA6:
- case 0xA7:
- case 0xA8:
- case 0xA9:
- case 0xAA:
- case 0xAB:
- case 0xAC:
- case 0xAD:
- case 0xAE:
- case 0xAF:
- case 0xB0:
- case 0xB1:
- case 0xB2:
- case 0xB3:
- case 0xB4:
- case 0xB5:
- case 0xB6:
- case 0xB7:
- case 0xB8:
- case 0xB9:
- case 0xBA:
- case 0xBB:
- case 0xBC:
- case 0xBD:
- case 0xBE:
- case 0xBF:
- {
- return get_string(current & 0x1F);
- }
-
- case 0xD9: // str 8
- {
- return get_string(get_number<uint8_t>());
- }
-
- case 0xDA: // str 16
- {
- return get_string(get_number<uint16_t>());
- }
-
- case 0xDB: // str 32
- {
- return get_string(get_number<uint32_t>());
- }
-
- default:
- {
- JSON_THROW(parse_error::create(113, chars_read,
- fmt::format("expected a MessagePack string; last byte: {:#02x}", current)));
- }
- }
-}
-
-std::string json::binary_reader::get_ubjson_string(const bool get_char)
-{
- if (get_char)
- {
- get(); // TODO: may we ignore N here?
- }
-
- unexpect_eof();
-
- switch (current)
- {
- case 'U':
- return get_string(get_number<uint8_t>());
- case 'i':
- return get_string(get_number<int8_t>());
- case 'I':
- return get_string(get_number<int16_t>());
- case 'l':
- return get_string(get_number<int32_t>());
- case 'L':
- return get_string(get_number<int64_t>());
- default:
- JSON_THROW(parse_error::create(113, chars_read,
- fmt::format("expected a UBJSON string; last byte: {:#02x}", current)));
- }
-}
-
-std::pair<std::size_t, int> json::binary_reader::get_ubjson_size_type()
-{
- std::size_t sz = std::string::npos;
- int tc = 0;
-
- get_ignore_noop();
-
- if (current == '$')
- {
- tc = get(); // must not ignore 'N', because 'N' maybe the type
- unexpect_eof();
-
- get_ignore_noop();
- if (current != '#')
- {
- JSON_THROW(parse_error::create(112, chars_read,
- fmt::format("expected '#' after UBJSON type information; last byte: {:#02x}", current)));
- }
- sz = parse_ubjson_internal();
- }
- else if (current == '#')
- {
- sz = parse_ubjson_internal();
- }
-
- return std::make_pair(sz, tc);
-}
-
-json json::binary_reader::get_ubjson_value(const int prefix)
-{
- switch (prefix)
- {
- case std::char_traits<char>::eof(): // EOF
- JSON_THROW(parse_error::create(110, chars_read, "unexpected end of input"));
-
- case 'T': // true
- return true;
- case 'F': // false
- return false;
-
- case 'Z': // null
- return nullptr;
-
- case 'U':
- return get_number<uint8_t>();
- case 'i':
- return get_number<int8_t>();
- case 'I':
- return get_number<int16_t>();
- case 'l':
- return get_number<int32_t>();
- case 'L':
- return get_number<int64_t>();
- case 'd':
- return get_number<float>();
- case 'D':
- return get_number<double>();
-
- case 'C': // char
- {
- get();
- unexpect_eof();
- if (JSON_UNLIKELY(current > 127))
- {
- JSON_THROW(parse_error::create(113, chars_read,
- fmt::format("byte after 'C' must be in range 0x00..0x7F; last byte: {:#02x}", current)));
- }
- return std::string(1, static_cast<char>(current));
- }
-
- case 'S': // string
- return get_ubjson_string();
-
- case '[': // array
- return get_ubjson_array();
-
- case '{': // object
- return get_ubjson_object();
-
- default: // anything else
- JSON_THROW(parse_error::create(112, chars_read,
- fmt::format("error reading UBJSON; last byte: {:#02x}", current)));
- }
-}
-
-json json::binary_reader::get_ubjson_array()
-{
- json result = value_t::array;
- const auto size_and_type = get_ubjson_size_type();
-
- if (size_and_type.first != std::string::npos)
- {
- if (JSON_UNLIKELY(size_and_type.first > result.max_size()))
- {
- JSON_THROW(out_of_range::create(408,
- fmt::format("excessive array size: {}", size_and_type.first)));
- }
-
- if (size_and_type.second != 0)
- {
- if (size_and_type.second != 'N')
- {
- std::generate_n(std::back_inserter(*result.m_value.array),
- size_and_type.first, [this, size_and_type]()
- {
- return get_ubjson_value(size_and_type.second);
- });
- }
- }
- else
- {
- std::generate_n(std::back_inserter(*result.m_value.array),
- size_and_type.first, [this]()
- {
- return parse_ubjson_internal();
- });
- }
- }
- else
- {
- while (current != ']')
- {
- result.push_back(parse_ubjson_internal(false));
- get_ignore_noop();
- }
- }
-
- return result;
-}
-
-json json::binary_reader::get_ubjson_object()
-{
- json result = value_t::object;
- const auto size_and_type = get_ubjson_size_type();
-
- if (size_and_type.first != std::string::npos)
- {
- if (JSON_UNLIKELY(size_and_type.first > result.max_size()))
- {
- JSON_THROW(out_of_range::create(408,
- fmt::format("excessive object size: {}", size_and_type.first)));
- }
-
- if (size_and_type.second != 0)
- {
- for (size_t i = 0; i < size_and_type.first; ++i)
- {
- auto key = get_ubjson_string();
- (*result.m_value.object)[key] = get_ubjson_value(size_and_type.second);
- }
- }
- else
- {
- for (size_t i = 0; i < size_and_type.first; ++i)
- {
- auto key = get_ubjson_string();
- (*result.m_value.object)[key] = parse_ubjson_internal();
- }
- }
- }
- else
- {
- while (current != '}')
- {
- auto key = get_ubjson_string(false);
- result[std::move(key)] = parse_ubjson_internal();
- get_ignore_noop();
- }
- }
-
- return result;
-}
-
-json json::from_cbor(raw_istream& is, const bool strict)
-{
- return binary_reader(is).parse_cbor(strict);
-}
-
-json json::from_cbor(span<const uint8_t> arr, const bool strict)
-{
- raw_mem_istream is(arr);
- return from_cbor(is, strict);
-}
-
-json json::from_msgpack(raw_istream& is, const bool strict)
-{
- return binary_reader(is).parse_msgpack(strict);
-}
-
-json json::from_msgpack(span<const uint8_t> arr, const bool strict)
-{
- raw_mem_istream is(arr);
- return from_msgpack(is, strict);
-}
-
-json json::from_ubjson(raw_istream& is, const bool strict)
-{
- return binary_reader(is).parse_ubjson(strict);
-}
-
-json json::from_ubjson(span<const uint8_t> arr, const bool strict)
-{
- raw_mem_istream is(arr);
- return from_ubjson(is, strict);
-}
-
-} // namespace wpi
diff --git a/wpiutil/src/main/native/cpp/json_binary_writer.cpp b/wpiutil/src/main/native/cpp/json_binary_writer.cpp
deleted file mode 100644
index db23f8d..0000000
--- a/wpiutil/src/main/native/cpp/json_binary_writer.cpp
+++ /dev/null
@@ -1,1091 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Modifications Copyright (c) 2017-2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
-/*
- __ _____ _____ _____
- __| | __| | | | JSON for Modern C++
-| | |__ | | | | | | version 3.1.2
-|_____|_____|_____|_|___| https://github.com/nlohmann/json
-
-Licensed under the MIT License <http://opensource.org/licenses/MIT>.
-Copyright (c) 2013-2018 Niels Lohmann <http://nlohmann.me>.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
-#define WPI_JSON_IMPLEMENTATION
-#include "wpi/json.h"
-
-#include "fmt/format.h"
-#include "wpi/raw_ostream.h"
-
-namespace wpi {
-
-/*!
-@brief serialization to CBOR and MessagePack values
-*/
-class json::binary_writer
-{
- using CharType = unsigned char;
-
- public:
- /*!
- @brief create a binary writer
-
- @param[in] adapter output adapter to write to
- */
- explicit binary_writer(raw_ostream& s) : o(s)
- {
- }
-
- /*!
- @brief[in] j JSON value to serialize
- */
- void write_cbor(const json& j);
-
- /*!
- @brief[in] j JSON value to serialize
- */
- void write_msgpack(const json& j);
-
- /*!
- @param[in] j JSON value to serialize
- @param[in] use_count whether to use '#' prefixes (optimized format)
- @param[in] use_type whether to use '$' prefixes (optimized format)
- @param[in] add_prefix whether prefixes need to be used for this value
- */
- void write_ubjson(const json& j, const bool use_count,
- const bool use_type, const bool add_prefix = true);
-
- private:
- void write_cbor_string(std::string_view str);
-
- void write_msgpack_string(std::string_view str);
-
- /*
- @brief write a number to output input
-
- @param[in] n number of type @a NumberType
- @tparam NumberType the type of the number
-
- @note This function needs to respect the system's endianess, because bytes
- in CBOR, MessagePack, and UBJSON are stored in network order (big
- endian) and therefore need reordering on little endian systems.
- */
- template<typename NumberType>
- void write_number(const NumberType n);
-
- // UBJSON: write number (floating point)
- template<typename NumberType, typename std::enable_if<
- std::is_floating_point<NumberType>::value, int>::type = 0>
- void write_number_with_ubjson_prefix(const NumberType n,
- const bool add_prefix)
- {
- if (add_prefix)
- {
- o << get_ubjson_float_prefix(n);
- }
- write_number(n);
- }
-
- // UBJSON: write number (unsigned integer)
- template<typename NumberType, typename std::enable_if<
- std::is_unsigned<NumberType>::value, int>::type = 0>
- void write_number_with_ubjson_prefix(const NumberType n,
- const bool add_prefix);
-
- // UBJSON: write number (signed integer)
- template<typename NumberType, typename std::enable_if<
- std::is_signed<NumberType>::value and
- not std::is_floating_point<NumberType>::value, int>::type = 0>
- void write_number_with_ubjson_prefix(const NumberType n,
- const bool add_prefix);
-
- /*!
- @brief determine the type prefix of container values
-
- @note This function does not need to be 100% accurate when it comes to
- integer limits. In case a number exceeds the limits of int64_t,
- this will be detected by a later call to function
- write_number_with_ubjson_prefix. Therefore, we return 'L' for any
- value that does not fit the previous limits.
- */
- CharType ubjson_prefix(const json& j) const noexcept;
-
- static constexpr CharType get_cbor_float_prefix(float)
- {
- return static_cast<CharType>(0xFA); // Single-Precision Float
- }
-
- static constexpr CharType get_cbor_float_prefix(double)
- {
- return static_cast<CharType>(0xFB); // Double-Precision Float
- }
-
- static constexpr CharType get_msgpack_float_prefix(float)
- {
- return static_cast<CharType>(0xCA); // float 32
- }
-
- static constexpr CharType get_msgpack_float_prefix(double)
- {
- return static_cast<CharType>(0xCB); // float 64
- }
-
- static constexpr CharType get_ubjson_float_prefix(float)
- {
- return 'd'; // float 32
- }
-
- static constexpr CharType get_ubjson_float_prefix(double)
- {
- return 'D'; // float 64
- }
-
- private:
- static bool little_endianess(int num = 1) noexcept
- {
- return (*reinterpret_cast<char*>(&num) == 1);
- }
-
- /// whether we can assume little endianess
- const bool is_little_endian = little_endianess();
-
- /// the output
- raw_ostream& o;
-};
-
-void json::binary_writer::write_cbor(const json& j)
-{
- switch (j.type())
- {
- case value_t::null:
- {
- o << static_cast<CharType>(0xF6);
- break;
- }
-
- case value_t::boolean:
- {
- o << static_cast<CharType>(j.m_value.boolean ? 0xF5 : 0xF4);
- break;
- }
-
- case value_t::number_integer:
- {
- if (j.m_value.number_integer >= 0)
- {
- // CBOR does not differentiate between positive signed
- // integers and unsigned integers. Therefore, we used the
- // code from the value_t::number_unsigned case here.
- if (j.m_value.number_integer <= 0x17)
- {
- write_number(static_cast<uint8_t>(j.m_value.number_integer));
- }
- else if (j.m_value.number_integer <= (std::numeric_limits<uint8_t>::max)())
- {
- o << static_cast<CharType>(0x18);
- write_number(static_cast<uint8_t>(j.m_value.number_integer));
- }
- else if (j.m_value.number_integer <= (std::numeric_limits<uint16_t>::max)())
- {
- o << static_cast<CharType>(0x19);
- write_number(static_cast<uint16_t>(j.m_value.number_integer));
- }
- else if (j.m_value.number_integer <= (std::numeric_limits<uint32_t>::max)())
- {
- o << static_cast<CharType>(0x1A);
- write_number(static_cast<uint32_t>(j.m_value.number_integer));
- }
- else
- {
- o << static_cast<CharType>(0x1B);
- write_number(static_cast<uint64_t>(j.m_value.number_integer));
- }
- }
- else
- {
- // The conversions below encode the sign in the first
- // byte, and the value is converted to a positive number.
- const auto positive_number = -1 - j.m_value.number_integer;
- if (j.m_value.number_integer >= -24)
- {
- write_number(static_cast<uint8_t>(0x20 + positive_number));
- }
- else if (positive_number <= (std::numeric_limits<uint8_t>::max)())
- {
- o << static_cast<CharType>(0x38);
- write_number(static_cast<uint8_t>(positive_number));
- }
- else if (positive_number <= (std::numeric_limits<uint16_t>::max)())
- {
- o << static_cast<CharType>(0x39);
- write_number(static_cast<uint16_t>(positive_number));
- }
- else if (positive_number <= (std::numeric_limits<uint32_t>::max)())
- {
- o << static_cast<CharType>(0x3A);
- write_number(static_cast<uint32_t>(positive_number));
- }
- else
- {
- o << static_cast<CharType>(0x3B);
- write_number(static_cast<uint64_t>(positive_number));
- }
- }
- break;
- }
-
- case value_t::number_unsigned:
- {
- if (j.m_value.number_unsigned <= 0x17)
- {
- write_number(static_cast<uint8_t>(j.m_value.number_unsigned));
- }
- else if (j.m_value.number_unsigned <= (std::numeric_limits<uint8_t>::max)())
- {
- o << static_cast<CharType>(0x18);
- write_number(static_cast<uint8_t>(j.m_value.number_unsigned));
- }
- else if (j.m_value.number_unsigned <= (std::numeric_limits<uint16_t>::max)())
- {
- o << static_cast<CharType>(0x19);
- write_number(static_cast<uint16_t>(j.m_value.number_unsigned));
- }
- else if (j.m_value.number_unsigned <= (std::numeric_limits<uint32_t>::max)())
- {
- o << static_cast<CharType>(0x1A);
- write_number(static_cast<uint32_t>(j.m_value.number_unsigned));
- }
- else
- {
- o << static_cast<CharType>(0x1B);
- write_number(static_cast<uint64_t>(j.m_value.number_unsigned));
- }
- break;
- }
-
- case value_t::number_float:
- {
- o << get_cbor_float_prefix(j.m_value.number_float);
- write_number(j.m_value.number_float);
- break;
- }
-
- case value_t::string:
- {
- write_cbor_string(*j.m_value.string);
- break;
- }
-
- case value_t::array:
- {
- // step 1: write control byte and the array size
- const auto N = j.m_value.array->size();
- if (N <= 0x17)
- {
- write_number(static_cast<uint8_t>(0x80 + N));
- }
- else if (N <= (std::numeric_limits<uint8_t>::max)())
- {
- o << static_cast<CharType>(0x98);
- write_number(static_cast<uint8_t>(N));
- }
- else if (N <= (std::numeric_limits<uint16_t>::max)())
- {
- o << static_cast<CharType>(0x99);
- write_number(static_cast<uint16_t>(N));
- }
- else if (N <= (std::numeric_limits<uint32_t>::max)())
- {
- o << static_cast<CharType>(0x9A);
- write_number(static_cast<uint32_t>(N));
- }
- // LCOV_EXCL_START
- else if (N <= (std::numeric_limits<uint64_t>::max)())
- {
- o << static_cast<CharType>(0x9B);
- write_number(static_cast<uint64_t>(N));
- }
- // LCOV_EXCL_STOP
-
- // step 2: write each element
- for (const auto& el : *j.m_value.array)
- {
- write_cbor(el);
- }
- break;
- }
-
- case value_t::object:
- {
- // step 1: write control byte and the object size
- const auto N = j.m_value.object->size();
- if (N <= 0x17)
- {
- write_number(static_cast<uint8_t>(0xA0 + N));
- }
- else if (N <= (std::numeric_limits<uint8_t>::max)())
- {
- o << static_cast<CharType>(0xB8);
- write_number(static_cast<uint8_t>(N));
- }
- else if (N <= (std::numeric_limits<uint16_t>::max)())
- {
- o << static_cast<CharType>(0xB9);
- write_number(static_cast<uint16_t>(N));
- }
- else if (N <= (std::numeric_limits<uint32_t>::max)())
- {
- o << static_cast<CharType>(0xBA);
- write_number(static_cast<uint32_t>(N));
- }
- // LCOV_EXCL_START
- else /*if (N <= (std::numeric_limits<uint64_t>::max)())*/
- {
- o << static_cast<CharType>(0xBB);
- write_number(static_cast<uint64_t>(N));
- }
- // LCOV_EXCL_STOP
-
- // step 2: write each element
- for (const auto& el : *j.m_value.object)
- {
- write_cbor_string(el.first());
- write_cbor(el.second);
- }
- break;
- }
-
- default:
- break;
- }
-}
-
-void json::binary_writer::write_msgpack(const json& j)
-{
- switch (j.type())
- {
- case value_t::null: // nil
- {
- o << static_cast<CharType>(0xC0);
- break;
- }
-
- case value_t::boolean: // true and false
- {
- o << static_cast<CharType>(j.m_value.boolean ? 0xC3 : 0xC2);
- break;
- }
-
- case value_t::number_integer:
- {
- if (j.m_value.number_integer >= 0)
- {
- // MessagePack does not differentiate between positive
- // signed integers and unsigned integers. Therefore, we used
- // the code from the value_t::number_unsigned case here.
- if (j.m_value.number_unsigned < 128)
- {
- // positive fixnum
- write_number(static_cast<uint8_t>(j.m_value.number_integer));
- }
- else if (j.m_value.number_unsigned <= (std::numeric_limits<uint8_t>::max)())
- {
- // uint 8
- o << static_cast<CharType>(0xCC);
- write_number(static_cast<uint8_t>(j.m_value.number_integer));
- }
- else if (j.m_value.number_unsigned <= (std::numeric_limits<uint16_t>::max)())
- {
- // uint 16
- o << static_cast<CharType>(0xCD);
- write_number(static_cast<uint16_t>(j.m_value.number_integer));
- }
- else if (j.m_value.number_unsigned <= (std::numeric_limits<uint32_t>::max)())
- {
- // uint 32
- o << static_cast<CharType>(0xCE);
- write_number(static_cast<uint32_t>(j.m_value.number_integer));
- }
- else if (j.m_value.number_unsigned <= (std::numeric_limits<uint64_t>::max)())
- {
- // uint 64
- o << static_cast<CharType>(0xCF);
- write_number(static_cast<uint64_t>(j.m_value.number_integer));
- }
- }
- else
- {
- if (j.m_value.number_integer >= -32)
- {
- // negative fixnum
- write_number(static_cast<int8_t>(j.m_value.number_integer));
- }
- else if (j.m_value.number_integer >= (std::numeric_limits<int8_t>::min)() and
- j.m_value.number_integer <= (std::numeric_limits<int8_t>::max)())
- {
- // int 8
- o << static_cast<CharType>(0xD0);
- write_number(static_cast<int8_t>(j.m_value.number_integer));
- }
- else if (j.m_value.number_integer >= (std::numeric_limits<int16_t>::min)() and
- j.m_value.number_integer <= (std::numeric_limits<int16_t>::max)())
- {
- // int 16
- o << static_cast<CharType>(0xD1);
- write_number(static_cast<int16_t>(j.m_value.number_integer));
- }
- else if (j.m_value.number_integer >= (std::numeric_limits<int32_t>::min)() and
- j.m_value.number_integer <= (std::numeric_limits<int32_t>::max)())
- {
- // int 32
- o << static_cast<CharType>(0xD2);
- write_number(static_cast<int32_t>(j.m_value.number_integer));
- }
- else if (j.m_value.number_integer >= (std::numeric_limits<int64_t>::min)() and
- j.m_value.number_integer <= (std::numeric_limits<int64_t>::max)())
- {
- // int 64
- o << static_cast<CharType>(0xD3);
- write_number(static_cast<int64_t>(j.m_value.number_integer));
- }
- }
- break;
- }
-
- case value_t::number_unsigned:
- {
- if (j.m_value.number_unsigned < 128)
- {
- // positive fixnum
- write_number(static_cast<uint8_t>(j.m_value.number_integer));
- }
- else if (j.m_value.number_unsigned <= (std::numeric_limits<uint8_t>::max)())
- {
- // uint 8
- o << static_cast<CharType>(0xCC);
- write_number(static_cast<uint8_t>(j.m_value.number_integer));
- }
- else if (j.m_value.number_unsigned <= (std::numeric_limits<uint16_t>::max)())
- {
- // uint 16
- o << static_cast<CharType>(0xCD);
- write_number(static_cast<uint16_t>(j.m_value.number_integer));
- }
- else if (j.m_value.number_unsigned <= (std::numeric_limits<uint32_t>::max)())
- {
- // uint 32
- o << static_cast<CharType>(0xCE);
- write_number(static_cast<uint32_t>(j.m_value.number_integer));
- }
- else if (j.m_value.number_unsigned <= (std::numeric_limits<uint64_t>::max)())
- {
- // uint 64
- o << static_cast<CharType>(0xCF);
- write_number(static_cast<uint64_t>(j.m_value.number_integer));
- }
- break;
- }
-
- case value_t::number_float:
- {
- o << get_msgpack_float_prefix(j.m_value.number_float);
- write_number(j.m_value.number_float);
- break;
- }
-
- case value_t::string:
- {
- write_msgpack_string(*j.m_value.string);
- break;
- }
-
- case value_t::array:
- {
- // step 1: write control byte and the array size
- const auto N = j.m_value.array->size();
- if (N <= 15)
- {
- // fixarray
- write_number(static_cast<uint8_t>(0x90 | N));
- }
- else if (N <= (std::numeric_limits<uint16_t>::max)())
- {
- // array 16
- o << static_cast<CharType>(0xDC);
- write_number(static_cast<uint16_t>(N));
- }
- else if (N <= (std::numeric_limits<uint32_t>::max)())
- {
- // array 32
- o << static_cast<CharType>(0xDD);
- write_number(static_cast<uint32_t>(N));
- }
-
- // step 2: write each element
- for (const auto& el : *j.m_value.array)
- {
- write_msgpack(el);
- }
- break;
- }
-
- case value_t::object:
- {
- // step 1: write control byte and the object size
- const auto N = j.m_value.object->size();
- if (N <= 15)
- {
- // fixmap
- write_number(static_cast<uint8_t>(0x80 | (N & 0xF)));
- }
- else if (N <= (std::numeric_limits<uint16_t>::max)())
- {
- // map 16
- o << static_cast<CharType>(0xDE);
- write_number(static_cast<uint16_t>(N));
- }
- else if (N <= (std::numeric_limits<uint32_t>::max)())
- {
- // map 32
- o << static_cast<CharType>(0xDF);
- write_number(static_cast<uint32_t>(N));
- }
-
- // step 2: write each element
- for (const auto& el : *j.m_value.object)
- {
- write_msgpack_string(el.first());
- write_msgpack(el.second);
- }
- break;
- }
-
- default:
- break;
- }
-}
-
-void json::binary_writer::write_ubjson(const json& j, const bool use_count,
- const bool use_type, const bool add_prefix)
-{
- switch (j.type())
- {
- case value_t::null:
- {
- if (add_prefix)
- {
- o << static_cast<CharType>('Z');
- }
- break;
- }
-
- case value_t::boolean:
- {
- if (add_prefix)
- o << static_cast<CharType>(j.m_value.boolean ? 'T' : 'F');
- break;
- }
-
- case value_t::number_integer:
- {
- write_number_with_ubjson_prefix(j.m_value.number_integer, add_prefix);
- break;
- }
-
- case value_t::number_unsigned:
- {
- write_number_with_ubjson_prefix(j.m_value.number_unsigned, add_prefix);
- break;
- }
-
- case value_t::number_float:
- {
- write_number_with_ubjson_prefix(j.m_value.number_float, add_prefix);
- break;
- }
-
- case value_t::string:
- {
- if (add_prefix)
- {
- o << static_cast<CharType>('S');
- }
- write_number_with_ubjson_prefix(j.m_value.string->size(), true);
- o << *j.m_value.string;
- break;
- }
-
- case value_t::array:
- {
- if (add_prefix)
- {
- o << static_cast<CharType>('[');
- }
-
- bool prefix_required = true;
- if (use_type and not j.m_value.array->empty())
- {
- assert(use_count);
- const CharType first_prefix = ubjson_prefix(j.front());
- const bool same_prefix = std::all_of(j.begin() + 1, j.end(),
- [this, first_prefix](const json & v)
- {
- return ubjson_prefix(v) == first_prefix;
- });
-
- if (same_prefix)
- {
- prefix_required = false;
- o << static_cast<CharType>('$');
- o << first_prefix;
- }
- }
-
- if (use_count)
- {
- o << static_cast<CharType>('#');
- write_number_with_ubjson_prefix(j.m_value.array->size(), true);
- }
-
- for (const auto& el : *j.m_value.array)
- {
- write_ubjson(el, use_count, use_type, prefix_required);
- }
-
- if (not use_count)
- {
- o << static_cast<CharType>(']');
- }
-
- break;
- }
-
- case value_t::object:
- {
- if (add_prefix)
- {
- o << static_cast<CharType>('{');
- }
-
- bool prefix_required = true;
- if (use_type and not j.m_value.object->empty())
- {
- assert(use_count);
- const CharType first_prefix = ubjson_prefix(j.front());
- const bool same_prefix = std::all_of(j.begin(), j.end(),
- [this, first_prefix](const json & v)
- {
- return ubjson_prefix(v) == first_prefix;
- });
-
- if (same_prefix)
- {
- prefix_required = false;
- o << static_cast<CharType>('$');
- o << first_prefix;
- }
- }
-
- if (use_count)
- {
- o << static_cast<CharType>('#');
- write_number_with_ubjson_prefix(j.m_value.object->size(), true);
- }
-
- for (const auto& el : *j.m_value.object)
- {
- write_number_with_ubjson_prefix(el.first().size(), true);
- o << el.first();
- write_ubjson(el.second, use_count, use_type, prefix_required);
- }
-
- if (not use_count)
- {
- o << static_cast<CharType>('}');
- }
-
- break;
- }
-
- default:
- break;
- }
-}
-
-void json::binary_writer::write_cbor_string(std::string_view str)
-{
- // step 1: write control byte and the string length
- const auto N = str.size();
- if (N <= 0x17)
- {
- write_number(static_cast<uint8_t>(0x60 + N));
- }
- else if (N <= (std::numeric_limits<uint8_t>::max)())
- {
- o << static_cast<CharType>(0x78);
- write_number(static_cast<uint8_t>(N));
- }
- else if (N <= (std::numeric_limits<uint16_t>::max)())
- {
- o << static_cast<CharType>(0x79);
- write_number(static_cast<uint16_t>(N));
- }
- else if (N <= (std::numeric_limits<uint32_t>::max)())
- {
- o << static_cast<CharType>(0x7A);
- write_number(static_cast<uint32_t>(N));
- }
- // LCOV_EXCL_START
- else if (N <= (std::numeric_limits<uint64_t>::max)())
- {
- o << static_cast<CharType>(0x7B);
- write_number(static_cast<uint64_t>(N));
- }
- // LCOV_EXCL_STOP
-
- // step 2: write the string
- o << str;
-}
-
-void json::binary_writer::write_msgpack_string(std::string_view str)
-{
- // step 1: write control byte and the string length
- const auto N = str.size();
- if (N <= 31)
- {
- // fixstr
- write_number(static_cast<uint8_t>(0xA0 | N));
- }
- else if (N <= (std::numeric_limits<uint8_t>::max)())
- {
- // str 8
- o << static_cast<CharType>(0xD9);
- write_number(static_cast<uint8_t>(N));
- }
- else if (N <= (std::numeric_limits<uint16_t>::max)())
- {
- // str 16
- o << static_cast<CharType>(0xDA);
- write_number(static_cast<uint16_t>(N));
- }
- else if (N <= (std::numeric_limits<uint32_t>::max)())
- {
- // str 32
- o << static_cast<CharType>(0xDB);
- write_number(static_cast<uint32_t>(N));
- }
-
- // step 2: write the string
- o << str;
-}
-
-template<typename NumberType>
-void json::binary_writer::write_number(const NumberType n)
-{
- // step 1: write number to array of length NumberType
- std::array<uint8_t, sizeof(NumberType)> vec;
- std::memcpy(vec.data(), &n, sizeof(NumberType));
-
- // step 2: write array to output (with possible reordering)
- if (is_little_endian)
- {
- // reverse byte order prior to conversion if necessary
- std::reverse(vec.begin(), vec.end());
- }
-
- o << span{vec.data(), sizeof(NumberType)};
-}
-
-template<typename NumberType, typename std::enable_if<
- std::is_unsigned<NumberType>::value, int>::type>
-void json::binary_writer::write_number_with_ubjson_prefix(const NumberType n,
- const bool add_prefix)
-{
- if (n <= static_cast<uint64_t>((std::numeric_limits<int8_t>::max)()))
- {
- if (add_prefix)
- {
- o << static_cast<CharType>('i'); // int8
- }
- write_number(static_cast<uint8_t>(n));
- }
- else if (n <= (std::numeric_limits<uint8_t>::max)())
- {
- if (add_prefix)
- {
- o << static_cast<CharType>('U'); // uint8
- }
- write_number(static_cast<uint8_t>(n));
- }
- else if (n <= static_cast<uint64_t>((std::numeric_limits<int16_t>::max)()))
- {
- if (add_prefix)
- {
- o << static_cast<CharType>('I'); // int16
- }
- write_number(static_cast<int16_t>(n));
- }
- else if (n <= static_cast<uint64_t>((std::numeric_limits<int32_t>::max)()))
- {
- if (add_prefix)
- {
- o << static_cast<CharType>('l'); // int32
- }
- write_number(static_cast<int32_t>(n));
- }
- else if (n <= static_cast<uint64_t>((std::numeric_limits<int64_t>::max)()))
- {
- if (add_prefix)
- {
- o << static_cast<CharType>('L'); // int64
- }
- write_number(static_cast<int64_t>(n));
- }
- else
- {
- JSON_THROW(out_of_range::create(407, fmt::format("number overflow serializing {}", n)));
- }
-}
-
-template<typename NumberType, typename std::enable_if<
- std::is_signed<NumberType>::value and
- not std::is_floating_point<NumberType>::value, int>::type>
-void json::binary_writer::write_number_with_ubjson_prefix(const NumberType n,
- const bool add_prefix)
-{
- if ((std::numeric_limits<int8_t>::min)() <= n and n <= (std::numeric_limits<int8_t>::max)())
- {
- if (add_prefix)
- {
- o << static_cast<CharType>('i'); // int8
- }
- write_number(static_cast<int8_t>(n));
- }
- else if (static_cast<int64_t>((std::numeric_limits<uint8_t>::min)()) <= n and n <= static_cast<int64_t>((std::numeric_limits<uint8_t>::max)()))
- {
- if (add_prefix)
- {
- o << static_cast<CharType>('U'); // uint8
- }
- write_number(static_cast<uint8_t>(n));
- }
- else if ((std::numeric_limits<int16_t>::min)() <= n and n <= (std::numeric_limits<int16_t>::max)())
- {
- if (add_prefix)
- {
- o << static_cast<CharType>('I'); // int16
- }
- write_number(static_cast<int16_t>(n));
- }
- else if ((std::numeric_limits<int32_t>::min)() <= n and n <= (std::numeric_limits<int32_t>::max)())
- {
- if (add_prefix)
- {
- o << static_cast<CharType>('l'); // int32
- }
- write_number(static_cast<int32_t>(n));
- }
- else if ((std::numeric_limits<int64_t>::min)() <= n and n <= (std::numeric_limits<int64_t>::max)())
- {
- if (add_prefix)
- {
- o << static_cast<CharType>('L'); // int64
- }
- write_number(static_cast<int64_t>(n));
- }
- // LCOV_EXCL_START
- else
- {
- JSON_THROW(out_of_range::create(407, fmt::format("number overflow serializing {}", n)));
- }
- // LCOV_EXCL_STOP
-}
-
-json::binary_writer::CharType json::binary_writer::ubjson_prefix(const json& j) const noexcept
-{
- switch (j.type())
- {
- case value_t::null:
- return 'Z';
-
- case value_t::boolean:
- return j.m_value.boolean ? 'T' : 'F';
-
- case value_t::number_integer:
- {
- if ((std::numeric_limits<int8_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<int8_t>::max)())
- {
- return 'i';
- }
- else if ((std::numeric_limits<uint8_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<uint8_t>::max)())
- {
- return 'U';
- }
- else if ((std::numeric_limits<int16_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<int16_t>::max)())
- {
- return 'I';
- }
- else if ((std::numeric_limits<int32_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<int32_t>::max)())
- {
- return 'l';
- }
- else // no check and assume int64_t (see note above)
- {
- return 'L';
- }
- }
-
- case value_t::number_unsigned:
- {
- if (j.m_value.number_unsigned <= static_cast<uint64_t>((std::numeric_limits<int8_t>::max)()))
- {
- return 'i';
- }
- else if (j.m_value.number_unsigned <= (std::numeric_limits<uint8_t>::max)())
- {
- return 'U';
- }
- else if (j.m_value.number_unsigned <= static_cast<uint64_t>((std::numeric_limits<int16_t>::max)()))
- {
- return 'I';
- }
- else if (j.m_value.number_unsigned <= static_cast<uint64_t>((std::numeric_limits<int32_t>::max)()))
- {
- return 'l';
- }
- else // no check and assume int64_t (see note above)
- {
- return 'L';
- }
- }
-
- case value_t::number_float:
- return get_ubjson_float_prefix(j.m_value.number_float);
-
- case value_t::string:
- return 'S';
-
- case value_t::array:
- return '[';
-
- case value_t::object:
- return '{';
-
- default: // discarded values
- return 'N';
- }
-}
-
-std::vector<uint8_t> json::to_cbor(const json& j)
-{
- std::vector<uint8_t> result;
- raw_uvector_ostream os(result);
- to_cbor(os, j);
- return result;
-}
-
-span<uint8_t> json::to_cbor(const json& j, std::vector<uint8_t>& buf)
-{
- buf.clear();
- raw_uvector_ostream os(buf);
- to_cbor(os, j);
- return os.array();
-}
-
-span<uint8_t> json::to_cbor(const json& j, SmallVectorImpl<uint8_t>& buf)
-{
- buf.clear();
- raw_usvector_ostream os(buf);
- to_cbor(os, j);
- return os.array();
-}
-
-void json::to_cbor(raw_ostream& os, const json& j)
-{
- binary_writer(os).write_cbor(j);
-}
-
-std::vector<uint8_t> json::to_msgpack(const json& j)
-{
- std::vector<uint8_t> result;
- raw_uvector_ostream os(result);
- to_msgpack(os, j);
- return result;
-}
-
-span<uint8_t> json::to_msgpack(const json& j, std::vector<uint8_t>& buf)
-{
- buf.clear();
- raw_uvector_ostream os(buf);
- to_msgpack(os, j);
- return os.array();
-}
-
-span<uint8_t> json::to_msgpack(const json& j, SmallVectorImpl<uint8_t>& buf)
-{
- buf.clear();
- raw_usvector_ostream os(buf);
- to_msgpack(os, j);
- return os.array();
-}
-
-void json::to_msgpack(raw_ostream& os, const json& j)
-{
- binary_writer(os).write_msgpack(j);
-}
-
-std::vector<uint8_t> json::to_ubjson(const json& j,
- const bool use_size,
- const bool use_type)
-{
- std::vector<uint8_t> result;
- raw_uvector_ostream os(result);
- to_ubjson(os, j, use_size, use_type);
- return result;
-}
-
-span<uint8_t> json::to_ubjson(const json& j, std::vector<uint8_t>& buf,
- const bool use_size, const bool use_type)
-{
- buf.clear();
- raw_uvector_ostream os(buf);
- to_ubjson(os, j, use_size, use_type);
- return os.array();
-}
-
-span<uint8_t> json::to_ubjson(const json& j, SmallVectorImpl<uint8_t>& buf,
- const bool use_size, const bool use_type)
-{
- buf.clear();
- raw_usvector_ostream os(buf);
- to_ubjson(os, j, use_size, use_type);
- return os.array();
-}
-
-void json::to_ubjson(raw_ostream& os, const json& j,
- const bool use_size, const bool use_type)
-{
- binary_writer(os).write_ubjson(j, use_size, use_type);
-}
-
-} // namespace wpi
diff --git a/wpiutil/src/main/native/cpp/json_parser.cpp b/wpiutil/src/main/native/cpp/json_parser.cpp
deleted file mode 100644
index db1ed11..0000000
--- a/wpiutil/src/main/native/cpp/json_parser.cpp
+++ /dev/null
@@ -1,1968 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Modifications Copyright (c) 2017-2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
-/*
- __ _____ _____ _____
- __| | __| | | | JSON for Modern C++
-| | |__ | | | | | | version 3.1.2
-|_____|_____|_____|_|___| https://github.com/nlohmann/json
-
-Licensed under the MIT License <http://opensource.org/licenses/MIT>.
-Copyright (c) 2013-2018 Niels Lohmann <http://nlohmann.me>.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
-#define WPI_JSON_IMPLEMENTATION
-#include "wpi/json.h"
-
-#include <clocale>
-#include <cmath>
-#include <cstdlib>
-
-#include "fmt/format.h"
-#include "wpi/SmallString.h"
-#include "wpi/raw_istream.h"
-#include "wpi/raw_ostream.h"
-
-namespace wpi {
-
-/*!
-@brief lexical analysis
-
-This class organizes the lexical analysis during JSON deserialization.
-*/
-class json::lexer
-{
- public:
- /// token types for the parser
- enum class token_type
- {
- uninitialized, ///< indicating the scanner is uninitialized
- literal_true, ///< the `true` literal
- literal_false, ///< the `false` literal
- literal_null, ///< the `null` literal
- value_string, ///< a string -- use get_string() for actual value
- value_unsigned, ///< an unsigned integer -- use get_number_unsigned() for actual value
- value_integer, ///< a signed integer -- use get_number_integer() for actual value
- value_float, ///< an floating point number -- use get_number_float() for actual value
- begin_array, ///< the character for array begin `[`
- begin_object, ///< the character for object begin `{`
- end_array, ///< the character for array end `]`
- end_object, ///< the character for object end `}`
- name_separator, ///< the name separator `:`
- value_separator, ///< the value separator `,`
- parse_error, ///< indicating a parse error
- end_of_input, ///< indicating the end of the input buffer
- literal_or_value ///< a literal or the begin of a value (only for diagnostics)
- };
-
- /// return name of values of type token_type (only used for errors)
- static const char* token_type_name(const token_type t) noexcept;
-
- explicit lexer(raw_istream& s);
-
- // delete because of pointer members
- lexer(const lexer&) = delete;
- lexer& operator=(lexer&) = delete;
-
- private:
- /////////////////////
- // locales
- /////////////////////
-
- /// return the locale-dependent decimal point
- static char get_decimal_point() noexcept
- {
- const auto loc = localeconv();
- assert(loc != nullptr);
- return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point);
- }
-
- /////////////////////
- // scan functions
- /////////////////////
-
- /*!
- @brief get codepoint from 4 hex characters following `\u`
-
- For input "\u c1 c2 c3 c4" the codepoint is:
- (c1 * 0x1000) + (c2 * 0x0100) + (c3 * 0x0010) + c4
- = (c1 << 12) + (c2 << 8) + (c3 << 4) + (c4 << 0)
-
- Furthermore, the possible characters '0'..'9', 'A'..'F', and 'a'..'f'
- must be converted to the integers 0x0..0x9, 0xA..0xF, 0xA..0xF, resp. The
- conversion is done by subtracting the offset (0x30, 0x37, and 0x57)
- between the ASCII value of the character and the desired integer value.
-
- @return codepoint (0x0000..0xFFFF) or -1 in case of an error (e.g. EOF or
- non-hex character)
- */
- int get_codepoint();
-
- /*!
- @brief check if the next byte(s) are inside a given range
-
- Adds the current byte and, for each passed range, reads a new byte and
- checks if it is inside the range. If a violation was detected, set up an
- error message and return false. Otherwise, return true.
-
- @param[in] ranges list of integers; interpreted as list of pairs of
- inclusive lower and upper bound, respectively
-
- @pre The passed list @a ranges must have 2, 4, or 6 elements; that is,
- 1, 2, or 3 pairs. This precondition is enforced by an assertion.
-
- @return true if and only if no range violation was detected
- */
- bool next_byte_in_range(std::initializer_list<int> ranges)
- {
- assert(ranges.size() == 2 or ranges.size() == 4 or ranges.size() == 6);
- add(current);
-
- for (auto range = ranges.begin(); range != ranges.end(); ++range)
- {
- get();
- if (JSON_LIKELY(*range <= current and current <= *(++range)))
- {
- add(current);
- }
- else
- {
- error_message = "invalid string: ill-formed UTF-8 byte";
- return false;
- }
- }
-
- return true;
- }
-
- /*!
- @brief scan a string literal
-
- This function scans a string according to Sect. 7 of RFC 7159. While
- scanning, bytes are escaped and copied into buffer token_buffer. Then the
- function returns successfully, token_buffer is *not* null-terminated (as it
- may contain \0 bytes), and token_buffer.size() is the number of bytes in the
- string.
-
- @return token_type::value_string if string could be successfully scanned,
- token_type::parse_error otherwise
-
- @note In case of errors, variable error_message contains a textual
- description.
- */
- token_type scan_string();
-
- static void strtof(float& f, const char* str, char** endptr) noexcept
- {
- f = std::strtof(str, endptr);
- }
-
- static void strtof(double& f, const char* str, char** endptr) noexcept
- {
- f = std::strtod(str, endptr);
- }
-
- static void strtof(long double& f, const char* str, char** endptr) noexcept
- {
- f = std::strtold(str, endptr);
- }
-
- /*!
- @brief scan a number literal
-
- This function scans a string according to Sect. 6 of RFC 7159.
-
- The function is realized with a deterministic finite state machine derived
- from the grammar described in RFC 7159. Starting in state "init", the
- input is read and used to determined the next state. Only state "done"
- accepts the number. State "error" is a trap state to model errors. In the
- table below, "anything" means any character but the ones listed before.
-
- state | 0 | 1-9 | e E | + | - | . | anything
- ---------|----------|----------|----------|---------|---------|----------|-----------
- init | zero | any1 | [error] | [error] | minus | [error] | [error]
- minus | zero | any1 | [error] | [error] | [error] | [error] | [error]
- zero | done | done | exponent | done | done | decimal1 | done
- any1 | any1 | any1 | exponent | done | done | decimal1 | done
- decimal1 | decimal2 | [error] | [error] | [error] | [error] | [error] | [error]
- decimal2 | decimal2 | decimal2 | exponent | done | done | done | done
- exponent | any2 | any2 | [error] | sign | sign | [error] | [error]
- sign | any2 | any2 | [error] | [error] | [error] | [error] | [error]
- any2 | any2 | any2 | done | done | done | done | done
-
- The state machine is realized with one label per state (prefixed with
- "scan_number_") and `goto` statements between them. The state machine
- contains cycles, but any cycle can be left when EOF is read. Therefore,
- the function is guaranteed to terminate.
-
- During scanning, the read bytes are stored in token_buffer. This string is
- then converted to a signed integer, an unsigned integer, or a
- floating-point number.
-
- @return token_type::value_unsigned, token_type::value_integer, or
- token_type::value_float if number could be successfully scanned,
- token_type::parse_error otherwise
-
- @note The scanner is independent of the current locale. Internally, the
- locale's decimal point is used instead of `.` to work with the
- locale-dependent converters.
- */
- token_type scan_number();
-
- /*!
- @param[in] literal_text the literal text to expect
- @param[in] length the length of the passed literal text
- @param[in] return_type the token type to return on success
- */
- token_type scan_literal(const char* literal_text, const std::size_t length,
- token_type return_type);
-
- /////////////////////
- // input management
- /////////////////////
-
- /// reset token_buffer; current character is beginning of token
- void reset() noexcept
- {
- token_buffer.clear();
- token_string.clear();
- token_string.push_back(std::char_traits<char>::to_char_type(current));
- }
-
- /*
- @brief get next character from the input
-
- This function provides the interface to the used input adapter. It does
- not throw in case the input reached EOF, but returns a
- `std::char_traits<char>::eof()` in that case. Stores the scanned characters
- for use in error messages.
-
- @return character read from the input
- */
- std::char_traits<char>::int_type get()
- {
- ++chars_read;
- if (JSON_UNLIKELY(!unget_chars.empty()))
- {
- current = unget_chars.back();
- unget_chars.pop_back();
- token_string.push_back(current);
- return current;
- }
- char c;
- is.read(c);
- if (JSON_UNLIKELY(is.has_error()))
- {
- current = std::char_traits<char>::eof();
- }
- else
- {
- current = std::char_traits<char>::to_int_type(c);
- token_string.push_back(c);
- }
- return current;
- }
-
- /// unget current character (return it again on next get)
- void unget()
- {
- --chars_read;
- if (JSON_LIKELY(current != std::char_traits<char>::eof()))
- {
- unget_chars.emplace_back(current);
- assert(token_string.size() != 0);
- token_string.pop_back();
- if (!token_string.empty())
- {
- current = token_string.back();
- }
- }
- }
-
- /// put back character (returned on next get)
- void putback(std::char_traits<char>::int_type c)
- {
- --chars_read;
- unget_chars.emplace_back(c);
- }
-
- /// add a character to token_buffer
- void add(int c)
- {
- token_buffer.push_back(std::char_traits<char>::to_char_type(c));
- }
-
- public:
- /////////////////////
- // value getters
- /////////////////////
-
- /// return integer value
- int64_t get_number_integer() const noexcept
- {
- return value_integer;
- }
-
- /// return unsigned integer value
- uint64_t get_number_unsigned() const noexcept
- {
- return value_unsigned;
- }
-
- /// return floating-point value
- double get_number_float() const noexcept
- {
- return value_float;
- }
-
- /// return current string value
- std::string_view get_string()
- {
- return token_buffer;
- }
-
- /////////////////////
- // diagnostics
- /////////////////////
-
- /// return position of last read token
- std::size_t get_position() const noexcept
- {
- return chars_read;
- }
-
- /// return the last read token (for errors only). Will never contain EOF
- /// (an arbitrary value that is not a valid char value, often -1), because
- /// 255 may legitimately occur. May contain NUL, which should be escaped.
- std::string get_token_string() const;
-
- /// return syntax error message
- const char* get_error_message() const noexcept
- {
- return error_message;
- }
-
- /////////////////////
- // actual scanner
- /////////////////////
-
- token_type scan();
-
- private:
- /// input adapter
- raw_istream& is;
-
- /// the current character
- std::char_traits<char>::int_type current = std::char_traits<char>::eof();
-
- /// unget characters
- SmallVector<std::char_traits<char>::int_type, 4> unget_chars;
-
- /// the number of characters read
- std::size_t chars_read = 0;
-
- /// raw input token string (for error messages)
- SmallString<128> token_string {};
-
- /// buffer for variable-length tokens (numbers, strings)
- SmallString<128> token_buffer {};
-
- /// a description of occurred lexer errors
- const char* error_message = "";
-
- // number values
- int64_t value_integer = 0;
- uint64_t value_unsigned = 0;
- double value_float = 0;
-
- /// the decimal point
- const char decimal_point_char = '.';
-};
-
-////////////
-// parser //
-////////////
-
-/*!
-@brief syntax analysis
-
-This class implements a recursive decent parser.
-*/
-class json::parser
-{
- using lexer_t = json::lexer;
- using token_type = typename lexer_t::token_type;
-
- public:
- /// a parser reading from an input adapter
- explicit parser(raw_istream& s,
- const parser_callback_t cb = nullptr,
- const bool allow_exceptions_ = true)
- : callback(cb), m_lexer(s), allow_exceptions(allow_exceptions_)
- {}
-
- /*!
- @brief public parser interface
-
- @param[in] strict whether to expect the last token to be EOF
- @param[in,out] result parsed JSON value
-
- @throw parse_error.101 in case of an unexpected token
- @throw parse_error.102 if to_unicode fails or surrogate error
- @throw parse_error.103 if to_unicode fails
- */
- void parse(const bool strict, json& result);
-
- /*!
- @brief public accept interface
-
- @param[in] strict whether to expect the last token to be EOF
- @return whether the input is a proper JSON text
- */
- bool accept(const bool strict = true)
- {
- // read first token
- get_token();
-
- if (not accept_internal())
- {
- return false;
- }
-
- // strict => last token must be EOF
- return not strict or (get_token() == token_type::end_of_input);
- }
-
- private:
- /*!
- @brief the actual parser
- @throw parse_error.101 in case of an unexpected token
- @throw parse_error.102 if to_unicode fails or surrogate error
- @throw parse_error.103 if to_unicode fails
- */
- void parse_internal(bool keep, json& result);
-
- /*!
- @brief the actual acceptor
-
- @invariant 1. The last token is not yet processed. Therefore, the caller
- of this function must make sure a token has been read.
- 2. When this function returns, the last token is processed.
- That is, the last read character was already considered.
-
- This invariant makes sure that no token needs to be "unput".
- */
- bool accept_internal();
-
- /// get next token from lexer
- token_type get_token()
- {
- return (last_token = m_lexer.scan());
- }
-
- /*!
- @throw parse_error.101 if expected token did not occur
- */
- bool expect(token_type t)
- {
- if (JSON_UNLIKELY(t != last_token))
- {
- errored = true;
- expected = t;
- if (allow_exceptions)
- {
- throw_exception();
- }
- else
- {
- return false;
- }
- }
-
- return true;
- }
-
- [[noreturn]] void throw_exception() const;
-
- private:
- /// current level of recursion
- int depth = 0;
- /// callback function
- const parser_callback_t callback = nullptr;
- /// the type of the last read token
- token_type last_token = token_type::uninitialized;
- /// the lexer
- lexer_t m_lexer;
- /// whether a syntax error occurred
- bool errored = false;
- /// possible reason for the syntax error
- token_type expected = token_type::uninitialized;
- /// whether to throw exceptions in case of errors
- const bool allow_exceptions = true;
-};
-
-const char* json::lexer::token_type_name(const token_type t) noexcept
-{
- switch (t)
- {
- case token_type::uninitialized:
- return "<uninitialized>";
- case token_type::literal_true:
- return "true literal";
- case token_type::literal_false:
- return "false literal";
- case token_type::literal_null:
- return "null literal";
- case token_type::value_string:
- return "string literal";
- case lexer::token_type::value_unsigned:
- case lexer::token_type::value_integer:
- case lexer::token_type::value_float:
- return "number literal";
- case token_type::begin_array:
- return "'['";
- case token_type::begin_object:
- return "'{'";
- case token_type::end_array:
- return "']'";
- case token_type::end_object:
- return "'}'";
- case token_type::name_separator:
- return "':'";
- case token_type::value_separator:
- return "','";
- case token_type::parse_error:
- return "<parse error>";
- case token_type::end_of_input:
- return "end of input";
- case token_type::literal_or_value:
- return "'[', '{', or a literal";
- default: // catch non-enum values
- return "unknown token"; // LCOV_EXCL_LINE
- }
-}
-
-json::lexer::lexer(raw_istream& s)
- : is(s), decimal_point_char(get_decimal_point())
-{
- // skip byte order mark
- std::char_traits<char>::int_type c;
- if ((c = get()) == 0xEF)
- {
- if ((c = get()) == 0xBB)
- {
- if ((c = get()) == 0xBF)
- {
- chars_read = 0;
- return; // Ignore BOM
- }
- else if (c != std::char_traits<char>::eof())
- {
- unget();
- }
- putback('\xBB');
- }
- else if (c != std::char_traits<char>::eof())
- {
- unget();
- }
- putback('\xEF');
- }
- unget(); // no byte order mark; process as usual
-}
-
-int json::lexer::get_codepoint()
-{
- // this function only makes sense after reading `\u`
- assert(current == 'u');
- int codepoint = 0;
-
- const auto factors = { 12, 8, 4, 0 };
- for (const auto factor : factors)
- {
- get();
-
- if (current >= '0' and current <= '9')
- {
- codepoint += ((current - 0x30) << factor);
- }
- else if (current >= 'A' and current <= 'F')
- {
- codepoint += ((current - 0x37) << factor);
- }
- else if (current >= 'a' and current <= 'f')
- {
- codepoint += ((current - 0x57) << factor);
- }
- else
- {
- return -1;
- }
- }
-
- assert(0x0000 <= codepoint and codepoint <= 0xFFFF);
- return codepoint;
-}
-
-json::lexer::token_type json::lexer::scan_string()
-{
- // reset token_buffer (ignore opening quote)
- reset();
-
- // we entered the function by reading an open quote
- assert(current == '\"');
-
- while (true)
- {
- // get next character
- switch (get())
- {
- // end of file while parsing string
- case std::char_traits<char>::eof():
- {
- error_message = "invalid string: missing closing quote";
- return token_type::parse_error;
- }
-
- // closing quote
- case '\"':
- {
- return token_type::value_string;
- }
-
- // escapes
- case '\\':
- {
- switch (get())
- {
- // quotation mark
- case '\"':
- add('\"');
- break;
- // reverse solidus
- case '\\':
- add('\\');
- break;
- // solidus
- case '/':
- add('/');
- break;
- // backspace
- case 'b':
- add('\b');
- break;
- // form feed
- case 'f':
- add('\f');
- break;
- // line feed
- case 'n':
- add('\n');
- break;
- // carriage return
- case 'r':
- add('\r');
- break;
- // tab
- case 't':
- add('\t');
- break;
-
- // unicode escapes
- case 'u':
- {
- const int codepoint1 = get_codepoint();
- int codepoint = codepoint1; // start with codepoint1
-
- if (JSON_UNLIKELY(codepoint1 == -1))
- {
- error_message = "invalid string: '\\u' must be followed by 4 hex digits";
- return token_type::parse_error;
- }
-
- // check if code point is a high surrogate
- if (0xD800 <= codepoint1 and codepoint1 <= 0xDBFF)
- {
- // expect next \uxxxx entry
- if (JSON_LIKELY(get() == '\\' and get() == 'u'))
- {
- const int codepoint2 = get_codepoint();
-
- if (JSON_UNLIKELY(codepoint2 == -1))
- {
- error_message = "invalid string: '\\u' must be followed by 4 hex digits";
- return token_type::parse_error;
- }
-
- // check if codepoint2 is a low surrogate
- if (JSON_LIKELY(0xDC00 <= codepoint2 and codepoint2 <= 0xDFFF))
- {
- // overwrite codepoint
- codepoint =
- // high surrogate occupies the most significant 22 bits
- (codepoint1 << 10)
- // low surrogate occupies the least significant 15 bits
- + codepoint2
- // there is still the 0xD800, 0xDC00 and 0x10000 noise
- // in the result so we have to subtract with:
- // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00
- - 0x35FDC00;
- }
- else
- {
- error_message = "invalid string: surrogate U+DC00..U+DFFF must be followed by U+DC00..U+DFFF";
- return token_type::parse_error;
- }
- }
- else
- {
- error_message = "invalid string: surrogate U+DC00..U+DFFF must be followed by U+DC00..U+DFFF";
- return token_type::parse_error;
- }
- }
- else
- {
- if (JSON_UNLIKELY(0xDC00 <= codepoint1 and codepoint1 <= 0xDFFF))
- {
- error_message = "invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF";
- return token_type::parse_error;
- }
- }
-
- // result of the above calculation yields a proper codepoint
- assert(0x00 <= codepoint and codepoint <= 0x10FFFF);
-
- // translate codepoint into bytes
- if (codepoint < 0x80)
- {
- // 1-byte characters: 0xxxxxxx (ASCII)
- add(codepoint);
- }
- else if (codepoint <= 0x7FF)
- {
- // 2-byte characters: 110xxxxx 10xxxxxx
- add(0xC0 | (codepoint >> 6));
- add(0x80 | (codepoint & 0x3F));
- }
- else if (codepoint <= 0xFFFF)
- {
- // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx
- add(0xE0 | (codepoint >> 12));
- add(0x80 | ((codepoint >> 6) & 0x3F));
- add(0x80 | (codepoint & 0x3F));
- }
- else
- {
- // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
- add(0xF0 | (codepoint >> 18));
- add(0x80 | ((codepoint >> 12) & 0x3F));
- add(0x80 | ((codepoint >> 6) & 0x3F));
- add(0x80 | (codepoint & 0x3F));
- }
-
- break;
- }
-
- // other characters after escape
- default:
- error_message = "invalid string: forbidden character after backslash";
- return token_type::parse_error;
- }
-
- break;
- }
-
- // invalid control characters
- case 0x00:
- case 0x01:
- case 0x02:
- case 0x03:
- case 0x04:
- case 0x05:
- case 0x06:
- case 0x07:
- case 0x08:
- case 0x09:
- case 0x0A:
- case 0x0B:
- case 0x0C:
- case 0x0D:
- case 0x0E:
- case 0x0F:
- case 0x10:
- case 0x11:
- case 0x12:
- case 0x13:
- case 0x14:
- case 0x15:
- case 0x16:
- case 0x17:
- case 0x18:
- case 0x19:
- case 0x1A:
- case 0x1B:
- case 0x1C:
- case 0x1D:
- case 0x1E:
- case 0x1F:
- {
- error_message = "invalid string: control character must be escaped";
- return token_type::parse_error;
- }
-
- // U+0020..U+007F (except U+0022 (quote) and U+005C (backspace))
- case 0x20:
- case 0x21:
- case 0x23:
- case 0x24:
- case 0x25:
- case 0x26:
- case 0x27:
- case 0x28:
- case 0x29:
- case 0x2A:
- case 0x2B:
- case 0x2C:
- case 0x2D:
- case 0x2E:
- case 0x2F:
- case 0x30:
- case 0x31:
- case 0x32:
- case 0x33:
- case 0x34:
- case 0x35:
- case 0x36:
- case 0x37:
- case 0x38:
- case 0x39:
- case 0x3A:
- case 0x3B:
- case 0x3C:
- case 0x3D:
- case 0x3E:
- case 0x3F:
- case 0x40:
- case 0x41:
- case 0x42:
- case 0x43:
- case 0x44:
- case 0x45:
- case 0x46:
- case 0x47:
- case 0x48:
- case 0x49:
- case 0x4A:
- case 0x4B:
- case 0x4C:
- case 0x4D:
- case 0x4E:
- case 0x4F:
- case 0x50:
- case 0x51:
- case 0x52:
- case 0x53:
- case 0x54:
- case 0x55:
- case 0x56:
- case 0x57:
- case 0x58:
- case 0x59:
- case 0x5A:
- case 0x5B:
- case 0x5D:
- case 0x5E:
- case 0x5F:
- case 0x60:
- case 0x61:
- case 0x62:
- case 0x63:
- case 0x64:
- case 0x65:
- case 0x66:
- case 0x67:
- case 0x68:
- case 0x69:
- case 0x6A:
- case 0x6B:
- case 0x6C:
- case 0x6D:
- case 0x6E:
- case 0x6F:
- case 0x70:
- case 0x71:
- case 0x72:
- case 0x73:
- case 0x74:
- case 0x75:
- case 0x76:
- case 0x77:
- case 0x78:
- case 0x79:
- case 0x7A:
- case 0x7B:
- case 0x7C:
- case 0x7D:
- case 0x7E:
- case 0x7F:
- {
- add(current);
- break;
- }
-
- // U+0080..U+07FF: bytes C2..DF 80..BF
- case 0xC2:
- case 0xC3:
- case 0xC4:
- case 0xC5:
- case 0xC6:
- case 0xC7:
- case 0xC8:
- case 0xC9:
- case 0xCA:
- case 0xCB:
- case 0xCC:
- case 0xCD:
- case 0xCE:
- case 0xCF:
- case 0xD0:
- case 0xD1:
- case 0xD2:
- case 0xD3:
- case 0xD4:
- case 0xD5:
- case 0xD6:
- case 0xD7:
- case 0xD8:
- case 0xD9:
- case 0xDA:
- case 0xDB:
- case 0xDC:
- case 0xDD:
- case 0xDE:
- case 0xDF:
- {
- if (JSON_UNLIKELY(not next_byte_in_range({0x80, 0xBF})))
- {
- return token_type::parse_error;
- }
- break;
- }
-
- // U+0800..U+0FFF: bytes E0 A0..BF 80..BF
- case 0xE0:
- {
- if (JSON_UNLIKELY(not (next_byte_in_range({0xA0, 0xBF, 0x80, 0xBF}))))
- {
- return token_type::parse_error;
- }
- break;
- }
-
- // U+1000..U+CFFF: bytes E1..EC 80..BF 80..BF
- // U+E000..U+FFFF: bytes EE..EF 80..BF 80..BF
- case 0xE1:
- case 0xE2:
- case 0xE3:
- case 0xE4:
- case 0xE5:
- case 0xE6:
- case 0xE7:
- case 0xE8:
- case 0xE9:
- case 0xEA:
- case 0xEB:
- case 0xEC:
- case 0xEE:
- case 0xEF:
- {
- if (JSON_UNLIKELY(not (next_byte_in_range({0x80, 0xBF, 0x80, 0xBF}))))
- {
- return token_type::parse_error;
- }
- break;
- }
-
- // U+D000..U+D7FF: bytes ED 80..9F 80..BF
- case 0xED:
- {
- if (JSON_UNLIKELY(not (next_byte_in_range({0x80, 0x9F, 0x80, 0xBF}))))
- {
- return token_type::parse_error;
- }
- break;
- }
-
- // U+10000..U+3FFFF F0 90..BF 80..BF 80..BF
- case 0xF0:
- {
- if (JSON_UNLIKELY(not (next_byte_in_range({0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF}))))
- {
- return token_type::parse_error;
- }
- break;
- }
-
- // U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF
- case 0xF1:
- case 0xF2:
- case 0xF3:
- {
- if (JSON_UNLIKELY(not (next_byte_in_range({0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF}))))
- {
- return token_type::parse_error;
- }
- break;
- }
-
- // U+100000..U+10FFFF F4 80..8F 80..BF 80..BF
- case 0xF4:
- {
- if (JSON_UNLIKELY(not (next_byte_in_range({0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF}))))
- {
- return token_type::parse_error;
- }
- break;
- }
-
- // remaining bytes (80..C1 and F5..FF) are ill-formed
- default:
- {
- error_message = "invalid string: ill-formed UTF-8 byte";
- return token_type::parse_error;
- }
- }
- }
-}
-
-json::lexer::token_type json::lexer::scan_number()
-{
- // reset token_buffer to store the number's bytes
- reset();
-
- // the type of the parsed number; initially set to unsigned; will be
- // changed if minus sign, decimal point or exponent is read
- token_type number_type = token_type::value_unsigned;
-
- // state (init): we just found out we need to scan a number
- switch (current)
- {
- case '-':
- {
- add(current);
- goto scan_number_minus;
- }
-
- case '0':
- {
- add(current);
- goto scan_number_zero;
- }
-
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- {
- add(current);
- goto scan_number_any1;
- }
-
- default:
- {
- // all other characters are rejected outside scan_number()
- assert(false); // LCOV_EXCL_LINE
- }
- }
-
-scan_number_minus:
- // state: we just parsed a leading minus sign
- number_type = token_type::value_integer;
- switch (get())
- {
- case '0':
- {
- add(current);
- goto scan_number_zero;
- }
-
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- {
- add(current);
- goto scan_number_any1;
- }
-
- default:
- {
- error_message = "invalid number; expected digit after '-'";
- return token_type::parse_error;
- }
- }
-
-scan_number_zero:
- // state: we just parse a zero (maybe with a leading minus sign)
- switch (get())
- {
- case '.':
- {
- add(decimal_point_char);
- goto scan_number_decimal1;
- }
-
- case 'e':
- case 'E':
- {
- add(current);
- goto scan_number_exponent;
- }
-
- default:
- goto scan_number_done;
- }
-
-scan_number_any1:
- // state: we just parsed a number 0-9 (maybe with a leading minus sign)
- switch (get())
- {
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- {
- add(current);
- goto scan_number_any1;
- }
-
- case '.':
- {
- add(decimal_point_char);
- goto scan_number_decimal1;
- }
-
- case 'e':
- case 'E':
- {
- add(current);
- goto scan_number_exponent;
- }
-
- default:
- goto scan_number_done;
- }
-
-scan_number_decimal1:
- // state: we just parsed a decimal point
- number_type = token_type::value_float;
- switch (get())
- {
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- {
- add(current);
- goto scan_number_decimal2;
- }
-
- default:
- {
- error_message = "invalid number; expected digit after '.'";
- return token_type::parse_error;
- }
- }
-
-scan_number_decimal2:
- // we just parsed at least one number after a decimal point
- switch (get())
- {
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- {
- add(current);
- goto scan_number_decimal2;
- }
-
- case 'e':
- case 'E':
- {
- add(current);
- goto scan_number_exponent;
- }
-
- default:
- goto scan_number_done;
- }
-
-scan_number_exponent:
- // we just parsed an exponent
- number_type = token_type::value_float;
- switch (get())
- {
- case '+':
- case '-':
- {
- add(current);
- goto scan_number_sign;
- }
-
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- {
- add(current);
- goto scan_number_any2;
- }
-
- default:
- {
- error_message =
- "invalid number; expected '+', '-', or digit after exponent";
- return token_type::parse_error;
- }
- }
-
-scan_number_sign:
- // we just parsed an exponent sign
- switch (get())
- {
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- {
- add(current);
- goto scan_number_any2;
- }
-
- default:
- {
- error_message = "invalid number; expected digit after exponent sign";
- return token_type::parse_error;
- }
- }
-
-scan_number_any2:
- // we just parsed a number after the exponent or exponent sign
- switch (get())
- {
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- {
- add(current);
- goto scan_number_any2;
- }
-
- default:
- goto scan_number_done;
- }
-
-scan_number_done:
- // unget the character after the number (we only read it to know that
- // we are done scanning a number)
- unget();
-
- char* endptr = nullptr;
- errno = 0;
-
- // try to parse integers first and fall back to floats
- if (number_type == token_type::value_unsigned)
- {
- const auto x = std::strtoull(token_buffer.c_str(), &endptr, 10);
-
- // we checked the number format before
- assert(endptr == token_buffer.data() + token_buffer.size());
-
- if (errno == 0)
- {
- value_unsigned = static_cast<uint64_t>(x);
- if (value_unsigned == x)
- {
- return token_type::value_unsigned;
- }
- }
- }
- else if (number_type == token_type::value_integer)
- {
- const auto x = std::strtoll(token_buffer.c_str(), &endptr, 10);
-
- // we checked the number format before
- assert(endptr == token_buffer.data() + token_buffer.size());
-
- if (errno == 0)
- {
- value_integer = static_cast<int64_t>(x);
- if (value_integer == x)
- {
- return token_type::value_integer;
- }
- }
- }
-
- // this code is reached if we parse a floating-point number or if an
- // integer conversion above failed
- strtof(value_float, token_buffer.c_str(), &endptr);
-
- // we checked the number format before
- assert(endptr == token_buffer.data() + token_buffer.size());
-
- return token_type::value_float;
-}
-
-json::lexer::token_type json::lexer::scan_literal(const char* literal_text, const std::size_t length,
- token_type return_type)
-{
- assert(current == literal_text[0]);
- for (std::size_t i = 1; i < length; ++i)
- {
- if (JSON_UNLIKELY(get() != literal_text[i]))
- {
- error_message = "invalid literal";
- return token_type::parse_error;
- }
- }
- return return_type;
-}
-
-std::string json::lexer::get_token_string() const
-{
- // escape control characters
- std::string result;
- raw_string_ostream ss(result);
- for (const unsigned char c : token_string)
- {
- if (c <= '\x1F')
- {
- // escape control characters
- ss << fmt::format("<U+{:04X}>", c);
- }
- else
- {
- // add character as is
- ss << c;
- }
- }
-
- ss.flush();
- return result;
-}
-
-json::lexer::token_type json::lexer::scan()
-{
- // read next character and ignore whitespace
- do
- {
- get();
- }
- while (current == ' ' or current == '\t' or current == '\n' or current == '\r');
-
- switch (current)
- {
- // structural characters
- case '[':
- return token_type::begin_array;
- case ']':
- return token_type::end_array;
- case '{':
- return token_type::begin_object;
- case '}':
- return token_type::end_object;
- case ':':
- return token_type::name_separator;
- case ',':
- return token_type::value_separator;
-
- // literals
- case 't':
- return scan_literal("true", 4, token_type::literal_true);
- case 'f':
- return scan_literal("false", 5, token_type::literal_false);
- case 'n':
- return scan_literal("null", 4, token_type::literal_null);
-
- // string
- case '\"':
- return scan_string();
-
- // number
- case '-':
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- return scan_number();
-
- // end of input (the null byte is needed when parsing from
- // string literals)
- case '\0':
- case std::char_traits<char>::eof():
- return token_type::end_of_input;
-
- // error
- default:
- error_message = "invalid literal";
- return token_type::parse_error;
- }
-}
-
-void json::parser::parse(const bool strict, json& result)
-{
- // read first token
- get_token();
-
- parse_internal(true, result);
- result.assert_invariant();
-
- // in strict mode, input must be completely read
- if (strict)
- {
- get_token();
- expect(token_type::end_of_input);
- }
-
- // in case of an error, return discarded value
- if (errored)
- {
- result = value_t::discarded;
- return;
- }
-
- // set top-level value to null if it was discarded by the callback
- // function
- if (result.is_discarded())
- {
- result = nullptr;
- }
-}
-
-void json::parser::parse_internal(bool keep, json& result)
-{
- // never parse after a parse error was detected
- assert(not errored);
-
- // start with a discarded value
- if (not result.is_discarded())
- {
- result.m_value.destroy(result.m_type);
- result.m_type = value_t::discarded;
- }
-
- switch (last_token)
- {
- case token_type::begin_object:
- {
- if (keep)
- {
- if (callback)
- {
- keep = callback(depth++, parse_event_t::object_start, result);
- }
-
- if (not callback or keep)
- {
- // explicitly set result to object to cope with {}
- result.m_type = value_t::object;
- result.m_value = value_t::object;
- }
- }
-
- // read next token
- get_token();
-
- // closing } -> we are done
- if (last_token == token_type::end_object)
- {
- if (keep and callback and not callback(--depth, parse_event_t::object_end, result))
- {
- result.m_value.destroy(result.m_type);
- result.m_type = value_t::discarded;
- }
- break;
- }
-
- // parse values
- SmallString<128> key;
- json value;
- while (true)
- {
- // store key
- if (not expect(token_type::value_string))
- {
- return;
- }
- key = m_lexer.get_string();
-
- bool keep_tag = false;
- if (keep)
- {
- if (callback)
- {
- json k(key);
- keep_tag = callback(depth, parse_event_t::key, k);
- }
- else
- {
- keep_tag = true;
- }
- }
-
- // parse separator (:)
- get_token();
- if (not expect(token_type::name_separator))
- {
- return;
- }
-
- // parse and add value
- get_token();
- value.m_value.destroy(value.m_type);
- value.m_type = value_t::discarded;
- parse_internal(keep, value);
-
- if (JSON_UNLIKELY(errored))
- {
- return;
- }
-
- if (keep and keep_tag and not value.is_discarded())
- {
- result.m_value.object->try_emplace(std::string_view(key.data(), key.size()), std::move(value));
- }
-
- // comma -> next value
- get_token();
- if (last_token == token_type::value_separator)
- {
- get_token();
- continue;
- }
-
- // closing }
- if (not expect(token_type::end_object))
- {
- return;
- }
- break;
- }
-
- if (keep and callback and not callback(--depth, parse_event_t::object_end, result))
- {
- result.m_value.destroy(result.m_type);
- result.m_type = value_t::discarded;
- }
- break;
- }
-
- case token_type::begin_array:
- {
- if (keep)
- {
- if (callback)
- {
- keep = callback(depth++, parse_event_t::array_start, result);
- }
-
- if (not callback or keep)
- {
- // explicitly set result to array to cope with []
- result.m_type = value_t::array;
- result.m_value = value_t::array;
- }
- }
-
- // read next token
- get_token();
-
- // closing ] -> we are done
- if (last_token == token_type::end_array)
- {
- if (callback and not callback(--depth, parse_event_t::array_end, result))
- {
- result.m_value.destroy(result.m_type);
- result.m_type = value_t::discarded;
- }
- break;
- }
-
- // parse values
- json value;
- while (true)
- {
- // parse value
- value.m_value.destroy(value.m_type);
- value.m_type = value_t::discarded;
- parse_internal(keep, value);
-
- if (JSON_UNLIKELY(errored))
- {
- return;
- }
-
- if (keep and not value.is_discarded())
- {
- result.m_value.array->push_back(std::move(value));
- }
-
- // comma -> next value
- get_token();
- if (last_token == token_type::value_separator)
- {
- get_token();
- continue;
- }
-
- // closing ]
- if (not expect(token_type::end_array))
- {
- return;
- }
- break;
- }
-
- if (keep and callback and not callback(--depth, parse_event_t::array_end, result))
- {
- result.m_value.destroy(result.m_type);
- result.m_type = value_t::discarded;
- }
- break;
- }
-
- case token_type::literal_null:
- {
- result.m_type = value_t::null;
- break;
- }
-
- case token_type::value_string:
- {
- result.m_type = value_t::string;
- result.m_value = m_lexer.get_string();
- break;
- }
-
- case token_type::literal_true:
- {
- result.m_type = value_t::boolean;
- result.m_value = true;
- break;
- }
-
- case token_type::literal_false:
- {
- result.m_type = value_t::boolean;
- result.m_value = false;
- break;
- }
-
- case token_type::value_unsigned:
- {
- result.m_type = value_t::number_unsigned;
- result.m_value = m_lexer.get_number_unsigned();
- break;
- }
-
- case token_type::value_integer:
- {
- result.m_type = value_t::number_integer;
- result.m_value = m_lexer.get_number_integer();
- break;
- }
-
- case token_type::value_float:
- {
- result.m_type = value_t::number_float;
- result.m_value = m_lexer.get_number_float();
-
- // throw in case of infinity or NAN
- if (JSON_UNLIKELY(not std::isfinite(result.m_value.number_float)))
- {
- if (allow_exceptions)
- {
- JSON_THROW(out_of_range::create(406,
- fmt::format("number overflow parsing '{}'", m_lexer.get_token_string())));
- }
- expect(token_type::uninitialized);
- }
- break;
- }
-
- case token_type::parse_error:
- {
- // using "uninitialized" to avoid "expected" message
- if (not expect(token_type::uninitialized))
- {
- return;
- }
- break; // LCOV_EXCL_LINE
- }
-
- default:
- {
- // the last token was unexpected; we expected a value
- if (not expect(token_type::literal_or_value))
- {
- return;
- }
- break; // LCOV_EXCL_LINE
- }
- }
-
- if (keep and callback and not callback(depth, parse_event_t::value, result))
- {
- result.m_value.destroy(result.m_type);
- result.m_type = value_t::discarded;
- }
-}
-
-bool json::parser::accept_internal()
-{
- switch (last_token)
- {
- case token_type::begin_object:
- {
- // read next token
- get_token();
-
- // closing } -> we are done
- if (last_token == token_type::end_object)
- {
- return true;
- }
-
- // parse values
- while (true)
- {
- // parse key
- if (last_token != token_type::value_string)
- {
- return false;
- }
-
- // parse separator (:)
- get_token();
- if (last_token != token_type::name_separator)
- {
- return false;
- }
-
- // parse value
- get_token();
- if (not accept_internal())
- {
- return false;
- }
-
- // comma -> next value
- get_token();
- if (last_token == token_type::value_separator)
- {
- get_token();
- continue;
- }
-
- // closing }
- return (last_token == token_type::end_object);
- }
- }
-
- case token_type::begin_array:
- {
- // read next token
- get_token();
-
- // closing ] -> we are done
- if (last_token == token_type::end_array)
- {
- return true;
- }
-
- // parse values
- while (true)
- {
- // parse value
- if (not accept_internal())
- {
- return false;
- }
-
- // comma -> next value
- get_token();
- if (last_token == token_type::value_separator)
- {
- get_token();
- continue;
- }
-
- // closing ]
- return (last_token == token_type::end_array);
- }
- }
-
- case token_type::value_float:
- {
- // reject infinity or NAN
- return std::isfinite(m_lexer.get_number_float());
- }
-
- case token_type::literal_false:
- case token_type::literal_null:
- case token_type::literal_true:
- case token_type::value_integer:
- case token_type::value_string:
- case token_type::value_unsigned:
- return true;
-
- default: // the last token was unexpected
- return false;
- }
-}
-
-void json::parser::throw_exception() const
-{
- std::string error_msg = "syntax error - ";
- if (last_token == token_type::parse_error)
- {
- error_msg += std::string(m_lexer.get_error_message()) + "; last read: '" +
- m_lexer.get_token_string() + "'";
- }
- else
- {
- error_msg += "unexpected " + std::string(lexer_t::token_type_name(last_token));
- }
-
- if (expected != token_type::uninitialized)
- {
- error_msg += "; expected " + std::string(lexer_t::token_type_name(expected));
- }
-
- JSON_THROW(parse_error::create(101, m_lexer.get_position(), error_msg));
-}
-
-json json::parse(std::string_view s,
- const parser_callback_t cb,
- const bool allow_exceptions)
-{
- raw_mem_istream is(span<const char>(s.data(), s.size()));
- return parse(is, cb, allow_exceptions);
-}
-
-json json::parse(span<const uint8_t> arr,
- const parser_callback_t cb,
- const bool allow_exceptions)
-{
- raw_mem_istream is(arr);
- return parse(is, cb, allow_exceptions);
-}
-
-json json::parse(raw_istream& i,
- const parser_callback_t cb,
- const bool allow_exceptions)
-{
- json result;
- parser(i, cb, allow_exceptions).parse(true, result);
- return result;
-}
-
-bool json::accept(std::string_view s)
-{
- raw_mem_istream is(span<const char>(s.data(), s.size()));
- return parser(is).accept(true);
-}
-
-bool json::accept(span<const uint8_t> arr)
-{
- raw_mem_istream is(arr);
- return parser(is).accept(true);
-}
-
-bool json::accept(raw_istream& i)
-{
- return parser(i).accept(true);
-}
-
-raw_istream& operator>>(raw_istream& i, json& j)
-{
- json::parser(i).parse(false, j);
- return i;
-}
-
-} // namespace wpi
diff --git a/wpiutil/src/main/native/cpp/json_pointer.cpp b/wpiutil/src/main/native/cpp/json_pointer.cpp
deleted file mode 100644
index 51548b3..0000000
--- a/wpiutil/src/main/native/cpp/json_pointer.cpp
+++ /dev/null
@@ -1,540 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Modifications Copyright (c) 2017-2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
-/*
- __ _____ _____ _____
- __| | __| | | | JSON for Modern C++
-| | |__ | | | | | | version 3.1.2
-|_____|_____|_____|_|___| https://github.com/nlohmann/json
-
-Licensed under the MIT License <http://opensource.org/licenses/MIT>.
-Copyright (c) 2013-2018 Niels Lohmann <http://nlohmann.me>.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
-#define WPI_JSON_IMPLEMENTATION
-#include "wpi/json.h"
-
-#include <numeric> // accumulate
-
-#include "fmt/format.h"
-#include "wpi/SmallString.h"
-#include "wpi/StringExtras.h"
-
-namespace wpi {
-
-std::string json_pointer::to_string() const noexcept
-{
- return std::accumulate(reference_tokens.begin(), reference_tokens.end(),
- std::string{},
- [](const std::string & a, const std::string & b)
- {
- return a + "/" + escape(b);
- });
-}
-
-int json_pointer::array_index(std::string_view s)
-{
- SmallString<128> str{s};
- std::size_t processed_chars = 0;
- const int res = std::stoi(str.c_str(), &processed_chars);
-
- // check if the string was completely read
- if (JSON_UNLIKELY(processed_chars != str.size()))
- {
- JSON_THROW(detail::out_of_range::create(404, fmt::format("unresolved reference token '{}'", s)));
- }
-
- return res;
-}
-
-json& json_pointer::get_and_create(json& j) const
-{
- using size_type = typename json::size_type;
- auto result = &j;
-
- // in case no reference tokens exist, return a reference to the JSON value
- // j which will be overwritten by a primitive value
- for (const auto& reference_token : reference_tokens)
- {
- switch (result->m_type)
- {
- case detail::value_t::null:
- {
- if (reference_token == "0")
- {
- // start a new array if reference token is 0
- result = &result->operator[](0);
- }
- else
- {
- // start a new object otherwise
- result = &result->operator[](reference_token);
- }
- break;
- }
-
- case detail::value_t::object:
- {
- // create an entry in the object
- result = &result->operator[](reference_token);
- break;
- }
-
- case detail::value_t::array:
- {
- // create an entry in the array
- JSON_TRY
- {
- result = &result->operator[](static_cast<size_type>(array_index(reference_token)));
- }
- JSON_CATCH(std::invalid_argument&)
- {
- JSON_THROW(detail::parse_error::create(109, 0, fmt::format("array index '{}' is not a number", reference_token)));
- }
- break;
- }
-
- /*
- The following code is only reached if there exists a reference
- token _and_ the current value is primitive. In this case, we have
- an error situation, because primitive values may only occur as
- single value; that is, with an empty list of reference tokens.
- */
- default:
- JSON_THROW(detail::type_error::create(313, "invalid value to unflatten"));
- }
- }
-
- return *result;
-}
-
-json& json_pointer::get_unchecked(json* ptr) const
-{
- using size_type = typename json::size_type;
- for (const auto& reference_token : reference_tokens)
- {
- // convert null values to arrays or objects before continuing
- if (ptr->m_type == detail::value_t::null)
- {
- // check if reference token is a number
- const bool nums =
- std::all_of(reference_token.begin(), reference_token.end(),
- [](const char x)
- {
- return (x >= '0' and x <= '9');
- });
-
- // change value to array for numbers or "-" or to object otherwise
- *ptr = (nums or reference_token == "-")
- ? detail::value_t::array
- : detail::value_t::object;
- }
-
- switch (ptr->m_type)
- {
- case detail::value_t::object:
- {
- // use unchecked object access
- ptr = &ptr->operator[](reference_token);
- break;
- }
-
- case detail::value_t::array:
- {
- // error condition (cf. RFC 6901, Sect. 4)
- if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))
- {
- JSON_THROW(detail::parse_error::create(106, 0,
- fmt::format("array index '{}' must not begin with '0'", reference_token)));
- }
-
- if (reference_token == "-")
- {
- // explicitly treat "-" as index beyond the end
- ptr = &ptr->operator[](ptr->m_value.array->size());
- }
- else
- {
- // convert array index to number; unchecked access
- JSON_TRY
- {
- ptr = &ptr->operator[](
- static_cast<size_type>(array_index(reference_token)));
- }
- JSON_CATCH(std::invalid_argument&)
- {
- JSON_THROW(detail::parse_error::create(109, 0,
- fmt::format("array index '{}' is not a number", reference_token)));
- }
- }
- break;
- }
-
- default:
- JSON_THROW(detail::out_of_range::create(404,
- fmt::format("unresolved reference token '{}'", reference_token)));
- }
- }
-
- return *ptr;
-}
-
-json& json_pointer::get_checked(json* ptr) const
-{
- using size_type = typename json::size_type;
- for (const auto& reference_token : reference_tokens)
- {
- switch (ptr->m_type)
- {
- case detail::value_t::object:
- {
- // note: at performs range check
- ptr = &ptr->at(reference_token);
- break;
- }
-
- case detail::value_t::array:
- {
- if (JSON_UNLIKELY(reference_token == "-"))
- {
- // "-" always fails the range check
- JSON_THROW(detail::out_of_range::create(402,
- fmt::format("array index '-' ({}) is out of range", ptr->m_value.array->size())));
- }
-
- // error condition (cf. RFC 6901, Sect. 4)
- if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))
- {
- JSON_THROW(detail::parse_error::create(106, 0,
- fmt::format("array index '{}' must not begin with '0'", reference_token)));
- }
-
- // note: at performs range check
- JSON_TRY
- {
- ptr = &ptr->at(static_cast<size_type>(array_index(reference_token)));
- }
- JSON_CATCH(std::invalid_argument&)
- {
- JSON_THROW(detail::parse_error::create(109, 0,
- fmt::format("array index '{}' is not a number", reference_token)));
- }
- break;
- }
-
- default:
- JSON_THROW(detail::out_of_range::create(404,
- fmt::format("unresolved reference token '{}'", reference_token)));
- }
- }
-
- return *ptr;
-}
-
-const json& json_pointer::get_unchecked(const json* ptr) const
-{
- using size_type = typename json::size_type;
- for (const auto& reference_token : reference_tokens)
- {
- switch (ptr->m_type)
- {
- case detail::value_t::object:
- {
- // use unchecked object access
- ptr = &ptr->operator[](reference_token);
- break;
- }
-
- case detail::value_t::array:
- {
- if (JSON_UNLIKELY(reference_token == "-"))
- {
- // "-" cannot be used for const access
- JSON_THROW(detail::out_of_range::create(402,
- fmt::format("array index '-' ({}) is out of range", ptr->m_value.array->size())));
- }
-
- // error condition (cf. RFC 6901, Sect. 4)
- if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))
- {
- JSON_THROW(detail::parse_error::create(106, 0,
- fmt::format("array index '{}' must not begin with '0'", reference_token)));
- }
-
- // use unchecked array access
- JSON_TRY
- {
- ptr = &ptr->operator[](
- static_cast<size_type>(array_index(reference_token)));
- }
- JSON_CATCH(std::invalid_argument&)
- {
- JSON_THROW(detail::parse_error::create(109, 0,
- fmt::format("array index '{}' is not a number", reference_token)));
- }
- break;
- }
-
- default:
- JSON_THROW(detail::out_of_range::create(404,
- fmt::format("unresolved reference token '{}'", reference_token)));
- }
- }
-
- return *ptr;
-}
-
-const json& json_pointer::get_checked(const json* ptr) const
-{
- using size_type = typename json::size_type;
- for (const auto& reference_token : reference_tokens)
- {
- switch (ptr->m_type)
- {
- case detail::value_t::object:
- {
- // note: at performs range check
- ptr = &ptr->at(reference_token);
- break;
- }
-
- case detail::value_t::array:
- {
- if (JSON_UNLIKELY(reference_token == "-"))
- {
- // "-" always fails the range check
- JSON_THROW(detail::out_of_range::create(402,
- fmt::format("array index '-' ({}) is out of range", ptr->m_value.array->size())));
- }
-
- // error condition (cf. RFC 6901, Sect. 4)
- if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))
- {
- JSON_THROW(detail::parse_error::create(106, 0,
- fmt::format("array index '{}' must not begin with '0'", reference_token)));
- }
-
- // note: at performs range check
- JSON_TRY
- {
- ptr = &ptr->at(static_cast<size_type>(array_index(reference_token)));
- }
- JSON_CATCH(std::invalid_argument&)
- {
- JSON_THROW(detail::parse_error::create(109, 0,
- fmt::format("array index '{}' is not a number", reference_token)));
- }
- break;
- }
-
- default:
- JSON_THROW(detail::out_of_range::create(404,
- fmt::format("unresolved reference token '{}'", reference_token)));
- }
- }
-
- return *ptr;
-}
-
-std::vector<std::string> json_pointer::split(std::string_view ref_str)
-{
- std::vector<std::string> result;
-
- // special case: empty reference string -> no reference tokens
- if (ref_str.empty())
- {
- return result;
- }
-
- // check if nonempty reference string begins with slash
- if (JSON_UNLIKELY(ref_str[0] != '/'))
- {
- JSON_THROW(detail::parse_error::create(107, 1,
- fmt::format("JSON pointer must be empty or begin with '/' - was: '{}'", ref_str)));
- }
-
- // extract the reference tokens:
- // - slash: position of the last read slash (or end of string)
- // - start: position after the previous slash
- for (
- // search for the first slash after the first character
- std::size_t slash = ref_str.find_first_of('/', 1),
- // set the beginning of the first reference token
- start = 1;
- // we can stop if start == string::npos+1 = 0
- start != 0;
- // set the beginning of the next reference token
- // (will eventually be 0 if slash == std::string::npos)
- start = slash + 1,
- // find next slash
- slash = ref_str.find_first_of('/', start))
- {
- // use the text between the beginning of the reference token
- // (start) and the last slash (slash).
- auto reference_token = slice(ref_str, start, slash);
-
- // check reference tokens are properly escaped
- for (std::size_t pos = reference_token.find_first_of('~');
- pos != std::string_view::npos;
- pos = reference_token.find_first_of('~', pos + 1))
- {
- assert(reference_token[pos] == '~');
-
- // ~ must be followed by 0 or 1
- if (JSON_UNLIKELY(pos == reference_token.size() - 1 or
- (reference_token[pos + 1] != '0' and
- reference_token[pos + 1] != '1')))
- {
- JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'"));
- }
- }
-
- // finally, store the reference token
- std::string ref_tok{reference_token};
- unescape(ref_tok);
- result.emplace_back(std::move(ref_tok));
- }
-
- return result;
-}
-
-void json_pointer::replace_substring(std::string& s, const std::string& f,
- const std::string& t)
-{
- assert(not f.empty());
- for (auto pos = s.find(f); // find first occurrence of f
- pos != std::string::npos; // make sure f was found
- s.replace(pos, f.size(), t), // replace with t, and
- pos = s.find(f, pos + t.size())) // find next occurrence of f
- {}
-}
-
-std::string json_pointer::escape(std::string s)
-{
- replace_substring(s, "~", "~0");
- replace_substring(s, "/", "~1");
- return s;
-}
-
-/// unescape "~1" to tilde and "~0" to slash (order is important!)
-void json_pointer::unescape(std::string& s)
-{
- replace_substring(s, "~1", "/");
- replace_substring(s, "~0", "~");
-}
-
-void json_pointer::flatten(std::string_view reference_string,
- const json& value,
- json& result)
-{
- switch (value.m_type)
- {
- case detail::value_t::array:
- {
- if (value.m_value.array->empty())
- {
- // flatten empty array as null
- result[reference_string] = nullptr;
- }
- else
- {
- // iterate array and use index as reference string
- for (std::size_t i = 0; i < value.m_value.array->size(); ++i)
- {
- flatten(fmt::format("{}/{}", reference_string, i),
- value.m_value.array->operator[](i), result);
- }
- }
- break;
- }
-
- case detail::value_t::object:
- {
- if (value.m_value.object->empty())
- {
- // flatten empty object as null
- result[reference_string] = nullptr;
- }
- else
- {
- // iterate object and use keys as reference string
- for (const auto& element : *value.m_value.object)
- {
- flatten(fmt::format("{}/{}", reference_string, escape(std::string{element.first()})), element.second, result);
- }
- }
- break;
- }
-
- default:
- {
- // add primitive value with its reference string
- result[reference_string] = value;
- break;
- }
- }
-}
-
-json
-json_pointer::unflatten(const json& value)
-{
- if (JSON_UNLIKELY(not value.is_object()))
- {
- JSON_THROW(detail::type_error::create(314, "only objects can be unflattened"));
- }
-
- // we need to iterate over the object values in sorted key order
- SmallVector<StringMapConstIterator<json>, 64> sorted;
- for (auto i = value.m_value.object->begin(),
- end = value.m_value.object->end(); i != end; ++i)
- {
- if (!i->second.is_primitive())
- {
- JSON_THROW(detail::type_error::create(315, "values in object must be primitive"));
- }
- sorted.push_back(i);
- }
- std::sort(sorted.begin(), sorted.end(),
- [](const StringMapConstIterator<json>& a,
- const StringMapConstIterator<json>& b) {
- return a->getKey() < b->getKey();
- });
-
- json result;
-
- // iterate the sorted JSON object values
- for (const auto& element : sorted)
- {
-
- // assign value to reference pointed to by JSON pointer; Note
- // that if the JSON pointer is "" (i.e., points to the whole
- // value), function get_and_create returns a reference to
- // result itself. An assignment will then create a primitive
- // value.
- json_pointer(element->first()).get_and_create(result) = element->second;
- }
-
- return result;
-}
-
-} // namespace wpi
diff --git a/wpiutil/src/main/native/cpp/json_serializer.cpp b/wpiutil/src/main/native/cpp/json_serializer.cpp
deleted file mode 100644
index 1101f66..0000000
--- a/wpiutil/src/main/native/cpp/json_serializer.cpp
+++ /dev/null
@@ -1,1531 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Modifications Copyright (c) 2017-2018 FIRST. All Rights Reserved. */
-/* Open Source Software - may be modified and shared by FRC teams. The code */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project. */
-/*----------------------------------------------------------------------------*/
-/*
- __ _____ _____ _____
- __| | __| | | | JSON for Modern C++
-| | |__ | | | | | | version 3.1.2
-|_____|_____|_____|_|___| https://github.com/nlohmann/json
-
-Licensed under the MIT License <http://opensource.org/licenses/MIT>.
-Copyright (c) 2013-2018 Niels Lohmann <http://nlohmann.me>.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
-#define WPI_JSON_IMPLEMENTATION
-#include "wpi/json.h"
-
-#include "fmt/format.h"
-#include "wpi/SmallString.h"
-#include "wpi/raw_os_ostream.h"
-
-#include "wpi/json_serializer.h"
-
-namespace wpi {
-
-namespace {
-
-/*!
-@brief implements the Grisu2 algorithm for binary to decimal floating-point
-conversion.
-
-This implementation is a slightly modified version of the reference
-implementation which may be obtained from
-http://florian.loitsch.com/publications (bench.tar.gz).
-
-The code is distributed under the MIT license, Copyright (c) 2009 Florian Loitsch.
-
-For a detailed description of the algorithm see:
-
-[1] Loitsch, "Printing Floating-Point Numbers Quickly and Accurately with
- Integers", Proceedings of the ACM SIGPLAN 2010 Conference on Programming
- Language Design and Implementation, PLDI 2010
-[2] Burger, Dybvig, "Printing Floating-Point Numbers Quickly and Accurately",
- Proceedings of the ACM SIGPLAN 1996 Conference on Programming Language
- Design and Implementation, PLDI 1996
-*/
-namespace dtoa_impl
-{
-
-template <typename Target, typename Source>
-Target reinterpret_bits(const Source source)
-{
- static_assert(sizeof(Target) == sizeof(Source), "size mismatch");
-
- Target target;
- std::memcpy(&target, &source, sizeof(Source));
- return target;
-}
-
-struct diyfp // f * 2^e
-{
- static constexpr int kPrecision = 64; // = q
-
- uint64_t f;
- int e;
-
- constexpr diyfp() noexcept : f(0), e(0) {}
- constexpr diyfp(uint64_t f_, int e_) noexcept : f(f_), e(e_) {}
-
- /*!
- @brief returns x - y
- @pre x.e == y.e and x.f >= y.f
- */
- static diyfp sub(const diyfp& x, const diyfp& y) noexcept
- {
- assert(x.e == y.e);
- assert(x.f >= y.f);
-
- return diyfp(x.f - y.f, x.e);
- }
-
- /*!
- @brief returns x * y
- @note The result is rounded. (Only the upper q bits are returned.)
- */
- static diyfp mul(const diyfp& x, const diyfp& y) noexcept
- {
- static_assert(kPrecision == 64, "internal error");
-
- // Computes:
- // f = round((x.f * y.f) / 2^q)
- // e = x.e + y.e + q
-
- // Emulate the 64-bit * 64-bit multiplication:
- //
- // p = u * v
- // = (u_lo + 2^32 u_hi) (v_lo + 2^32 v_hi)
- // = (u_lo v_lo ) + 2^32 ((u_lo v_hi ) + (u_hi v_lo )) + 2^64 (u_hi v_hi )
- // = (p0 ) + 2^32 ((p1 ) + (p2 )) + 2^64 (p3 )
- // = (p0_lo + 2^32 p0_hi) + 2^32 ((p1_lo + 2^32 p1_hi) + (p2_lo + 2^32 p2_hi)) + 2^64 (p3 )
- // = (p0_lo ) + 2^32 (p0_hi + p1_lo + p2_lo ) + 2^64 (p1_hi + p2_hi + p3)
- // = (p0_lo ) + 2^32 (Q ) + 2^64 (H )
- // = (p0_lo ) + 2^32 (Q_lo + 2^32 Q_hi ) + 2^64 (H )
- //
- // (Since Q might be larger than 2^32 - 1)
- //
- // = (p0_lo + 2^32 Q_lo) + 2^64 (Q_hi + H)
- //
- // (Q_hi + H does not overflow a 64-bit int)
- //
- // = p_lo + 2^64 p_hi
-
- const uint64_t u_lo = x.f & 0xFFFFFFFF;
- const uint64_t u_hi = x.f >> 32;
- const uint64_t v_lo = y.f & 0xFFFFFFFF;
- const uint64_t v_hi = y.f >> 32;
-
- const uint64_t p0 = u_lo * v_lo;
- const uint64_t p1 = u_lo * v_hi;
- const uint64_t p2 = u_hi * v_lo;
- const uint64_t p3 = u_hi * v_hi;
-
- const uint64_t p0_hi = p0 >> 32;
- const uint64_t p1_lo = p1 & 0xFFFFFFFF;
- const uint64_t p1_hi = p1 >> 32;
- const uint64_t p2_lo = p2 & 0xFFFFFFFF;
- const uint64_t p2_hi = p2 >> 32;
-
- uint64_t Q = p0_hi + p1_lo + p2_lo;
-
- // The full product might now be computed as
- //
- // p_hi = p3 + p2_hi + p1_hi + (Q >> 32)
- // p_lo = p0_lo + (Q << 32)
- //
- // But in this particular case here, the full p_lo is not required.
- // Effectively we only need to add the highest bit in p_lo to p_hi (and
- // Q_hi + 1 does not overflow).
-
- Q += uint64_t{1} << (64 - 32 - 1); // round, ties up
-
- const uint64_t h = p3 + p2_hi + p1_hi + (Q >> 32);
-
- return diyfp(h, x.e + y.e + 64);
- }
-
- /*!
- @brief normalize x such that the significand is >= 2^(q-1)
- @pre x.f != 0
- */
- static diyfp normalize(diyfp x) noexcept
- {
- assert(x.f != 0);
-
- while ((x.f >> 63) == 0)
- {
- x.f <<= 1;
- x.e--;
- }
-
- return x;
- }
-
- /*!
- @brief normalize x such that the result has the exponent E
- @pre e >= x.e and the upper e - x.e bits of x.f must be zero.
- */
- static diyfp normalize_to(const diyfp& x, const int target_exponent) noexcept
- {
- const int delta = x.e - target_exponent;
-
- assert(delta >= 0);
- assert(((x.f << delta) >> delta) == x.f);
-
- return diyfp(x.f << delta, target_exponent);
- }
-};
-
-struct boundaries
-{
- diyfp w;
- diyfp minus;
- diyfp plus;
-};
-
-/*!
-Compute the (normalized) diyfp representing the input number 'value' and its
-boundaries.
-
-@pre value must be finite and positive
-*/
-template <typename FloatType>
-boundaries compute_boundaries(FloatType value)
-{
- assert(std::isfinite(value));
- assert(value > 0);
-
- // Convert the IEEE representation into a diyfp.
- //
- // If v is denormal:
- // value = 0.F * 2^(1 - bias) = ( F) * 2^(1 - bias - (p-1))
- // If v is normalized:
- // value = 1.F * 2^(E - bias) = (2^(p-1) + F) * 2^(E - bias - (p-1))
-
- static_assert(std::numeric_limits<FloatType>::is_iec559,
- "internal error: dtoa_short requires an IEEE-754 floating-point implementation");
-
- constexpr int kPrecision = std::numeric_limits<FloatType>::digits; // = p (includes the hidden bit)
- constexpr int kBias = std::numeric_limits<FloatType>::max_exponent - 1 + (kPrecision - 1);
- constexpr int kMinExp = 1 - kBias;
- constexpr uint64_t kHiddenBit = uint64_t{1} << (kPrecision - 1); // = 2^(p-1)
-
- using bits_type = typename std::conditional< kPrecision == 24, uint32_t, uint64_t >::type;
-
- const uint64_t bits = reinterpret_bits<bits_type>(value);
- const uint64_t E = bits >> (kPrecision - 1);
- const uint64_t F = bits & (kHiddenBit - 1);
-
- const bool is_denormal = (E == 0);
- const diyfp v = is_denormal
- ? diyfp(F, kMinExp)
- : diyfp(F + kHiddenBit, static_cast<int>(E) - kBias);
-
- // Compute the boundaries m- and m+ of the floating-point value
- // v = f * 2^e.
- //
- // Determine v- and v+, the floating-point predecessor and successor if v,
- // respectively.
- //
- // v- = v - 2^e if f != 2^(p-1) or e == e_min (A)
- // = v - 2^(e-1) if f == 2^(p-1) and e > e_min (B)
- //
- // v+ = v + 2^e
- //
- // Let m- = (v- + v) / 2 and m+ = (v + v+) / 2. All real numbers _strictly_
- // between m- and m+ round to v, regardless of how the input rounding
- // algorithm breaks ties.
- //
- // ---+-------------+-------------+-------------+-------------+--- (A)
- // v- m- v m+ v+
- //
- // -----------------+------+------+-------------+-------------+--- (B)
- // v- m- v m+ v+
-
- const bool lower_boundary_is_closer = (F == 0 and E > 1);
- const diyfp m_plus = diyfp(2 * v.f + 1, v.e - 1);
- const diyfp m_minus = lower_boundary_is_closer
- ? diyfp(4 * v.f - 1, v.e - 2) // (B)
- : diyfp(2 * v.f - 1, v.e - 1); // (A)
-
- // Determine the normalized w+ = m+.
- const diyfp w_plus = diyfp::normalize(m_plus);
-
- // Determine w- = m- such that e_(w-) = e_(w+).
- const diyfp w_minus = diyfp::normalize_to(m_minus, w_plus.e);
-
- return {diyfp::normalize(v), w_minus, w_plus};
-}
-
-// Given normalized diyfp w, Grisu needs to find a (normalized) cached
-// power-of-ten c, such that the exponent of the product c * w = f * 2^e lies
-// within a certain range [alpha, gamma] (Definition 3.2 from [1])
-//
-// alpha <= e = e_c + e_w + q <= gamma
-//
-// or
-//
-// f_c * f_w * 2^alpha <= f_c 2^(e_c) * f_w 2^(e_w) * 2^q
-// <= f_c * f_w * 2^gamma
-//
-// Since c and w are normalized, i.e. 2^(q-1) <= f < 2^q, this implies
-//
-// 2^(q-1) * 2^(q-1) * 2^alpha <= c * w * 2^q < 2^q * 2^q * 2^gamma
-//
-// or
-//
-// 2^(q - 2 + alpha) <= c * w < 2^(q + gamma)
-//
-// The choice of (alpha,gamma) determines the size of the table and the form of
-// the digit generation procedure. Using (alpha,gamma)=(-60,-32) works out well
-// in practice:
-//
-// The idea is to cut the number c * w = f * 2^e into two parts, which can be
-// processed independently: An integral part p1, and a fractional part p2:
-//
-// f * 2^e = ( (f div 2^-e) * 2^-e + (f mod 2^-e) ) * 2^e
-// = (f div 2^-e) + (f mod 2^-e) * 2^e
-// = p1 + p2 * 2^e
-//
-// The conversion of p1 into decimal form requires a series of divisions and
-// modulos by (a power of) 10. These operations are faster for 32-bit than for
-// 64-bit integers, so p1 should ideally fit into a 32-bit integer. This can be
-// achieved by choosing
-//
-// -e >= 32 or e <= -32 := gamma
-//
-// In order to convert the fractional part
-//
-// p2 * 2^e = p2 / 2^-e = d[-1] / 10^1 + d[-2] / 10^2 + ...
-//
-// into decimal form, the fraction is repeatedly multiplied by 10 and the digits
-// d[-i] are extracted in order:
-//
-// (10 * p2) div 2^-e = d[-1]
-// (10 * p2) mod 2^-e = d[-2] / 10^1 + ...
-//
-// The multiplication by 10 must not overflow. It is sufficient to choose
-//
-// 10 * p2 < 16 * p2 = 2^4 * p2 <= 2^64.
-//
-// Since p2 = f mod 2^-e < 2^-e,
-//
-// -e <= 60 or e >= -60 := alpha
-
-constexpr int kAlpha = -60;
-constexpr int kGamma = -32;
-
-struct cached_power // c = f * 2^e ~= 10^k
-{
- uint64_t f;
- int e;
- int k;
-};
-
-/*!
-For a normalized diyfp w = f * 2^e, this function returns a (normalized) cached
-power-of-ten c = f_c * 2^e_c, such that the exponent of the product w * c
-satisfies (Definition 3.2 from [1])
-
- alpha <= e_c + e + q <= gamma.
-*/
-inline cached_power get_cached_power_for_binary_exponent(int e)
-{
- // Now
- //
- // alpha <= e_c + e + q <= gamma (1)
- // ==> f_c * 2^alpha <= c * 2^e * 2^q
- //
- // and since the c's are normalized, 2^(q-1) <= f_c,
- //
- // ==> 2^(q - 1 + alpha) <= c * 2^(e + q)
- // ==> 2^(alpha - e - 1) <= c
- //
- // If c were an exakt power of ten, i.e. c = 10^k, one may determine k as
- //
- // k = ceil( log_10( 2^(alpha - e - 1) ) )
- // = ceil( (alpha - e - 1) * log_10(2) )
- //
- // From the paper:
- // "In theory the result of the procedure could be wrong since c is rounded,
- // and the computation itself is approximated [...]. In practice, however,
- // this simple function is sufficient."
- //
- // For IEEE double precision floating-point numbers converted into
- // normalized diyfp's w = f * 2^e, with q = 64,
- //
- // e >= -1022 (min IEEE exponent)
- // -52 (p - 1)
- // -52 (p - 1, possibly normalize denormal IEEE numbers)
- // -11 (normalize the diyfp)
- // = -1137
- //
- // and
- //
- // e <= +1023 (max IEEE exponent)
- // -52 (p - 1)
- // -11 (normalize the diyfp)
- // = 960
- //
- // This binary exponent range [-1137,960] results in a decimal exponent
- // range [-307,324]. One does not need to store a cached power for each
- // k in this range. For each such k it suffices to find a cached power
- // such that the exponent of the product lies in [alpha,gamma].
- // This implies that the difference of the decimal exponents of adjacent
- // table entries must be less than or equal to
- //
- // floor( (gamma - alpha) * log_10(2) ) = 8.
- //
- // (A smaller distance gamma-alpha would require a larger table.)
-
- // NB:
- // Actually this function returns c, such that -60 <= e_c + e + 64 <= -34.
-
- constexpr int kCachedPowersSize = 79;
- constexpr int kCachedPowersMinDecExp = -300;
- constexpr int kCachedPowersDecStep = 8;
-
- static constexpr cached_power kCachedPowers[] =
- {
- { 0xAB70FE17C79AC6CA, -1060, -300 },
- { 0xFF77B1FCBEBCDC4F, -1034, -292 },
- { 0xBE5691EF416BD60C, -1007, -284 },
- { 0x8DD01FAD907FFC3C, -980, -276 },
- { 0xD3515C2831559A83, -954, -268 },
- { 0x9D71AC8FADA6C9B5, -927, -260 },
- { 0xEA9C227723EE8BCB, -901, -252 },
- { 0xAECC49914078536D, -874, -244 },
- { 0x823C12795DB6CE57, -847, -236 },
- { 0xC21094364DFB5637, -821, -228 },
- { 0x9096EA6F3848984F, -794, -220 },
- { 0xD77485CB25823AC7, -768, -212 },
- { 0xA086CFCD97BF97F4, -741, -204 },
- { 0xEF340A98172AACE5, -715, -196 },
- { 0xB23867FB2A35B28E, -688, -188 },
- { 0x84C8D4DFD2C63F3B, -661, -180 },
- { 0xC5DD44271AD3CDBA, -635, -172 },
- { 0x936B9FCEBB25C996, -608, -164 },
- { 0xDBAC6C247D62A584, -582, -156 },
- { 0xA3AB66580D5FDAF6, -555, -148 },
- { 0xF3E2F893DEC3F126, -529, -140 },
- { 0xB5B5ADA8AAFF80B8, -502, -132 },
- { 0x87625F056C7C4A8B, -475, -124 },
- { 0xC9BCFF6034C13053, -449, -116 },
- { 0x964E858C91BA2655, -422, -108 },
- { 0xDFF9772470297EBD, -396, -100 },
- { 0xA6DFBD9FB8E5B88F, -369, -92 },
- { 0xF8A95FCF88747D94, -343, -84 },
- { 0xB94470938FA89BCF, -316, -76 },
- { 0x8A08F0F8BF0F156B, -289, -68 },
- { 0xCDB02555653131B6, -263, -60 },
- { 0x993FE2C6D07B7FAC, -236, -52 },
- { 0xE45C10C42A2B3B06, -210, -44 },
- { 0xAA242499697392D3, -183, -36 },
- { 0xFD87B5F28300CA0E, -157, -28 },
- { 0xBCE5086492111AEB, -130, -20 },
- { 0x8CBCCC096F5088CC, -103, -12 },
- { 0xD1B71758E219652C, -77, -4 },
- { 0x9C40000000000000, -50, 4 },
- { 0xE8D4A51000000000, -24, 12 },
- { 0xAD78EBC5AC620000, 3, 20 },
- { 0x813F3978F8940984, 30, 28 },
- { 0xC097CE7BC90715B3, 56, 36 },
- { 0x8F7E32CE7BEA5C70, 83, 44 },
- { 0xD5D238A4ABE98068, 109, 52 },
- { 0x9F4F2726179A2245, 136, 60 },
- { 0xED63A231D4C4FB27, 162, 68 },
- { 0xB0DE65388CC8ADA8, 189, 76 },
- { 0x83C7088E1AAB65DB, 216, 84 },
- { 0xC45D1DF942711D9A, 242, 92 },
- { 0x924D692CA61BE758, 269, 100 },
- { 0xDA01EE641A708DEA, 295, 108 },
- { 0xA26DA3999AEF774A, 322, 116 },
- { 0xF209787BB47D6B85, 348, 124 },
- { 0xB454E4A179DD1877, 375, 132 },
- { 0x865B86925B9BC5C2, 402, 140 },
- { 0xC83553C5C8965D3D, 428, 148 },
- { 0x952AB45CFA97A0B3, 455, 156 },
- { 0xDE469FBD99A05FE3, 481, 164 },
- { 0xA59BC234DB398C25, 508, 172 },
- { 0xF6C69A72A3989F5C, 534, 180 },
- { 0xB7DCBF5354E9BECE, 561, 188 },
- { 0x88FCF317F22241E2, 588, 196 },
- { 0xCC20CE9BD35C78A5, 614, 204 },
- { 0x98165AF37B2153DF, 641, 212 },
- { 0xE2A0B5DC971F303A, 667, 220 },
- { 0xA8D9D1535CE3B396, 694, 228 },
- { 0xFB9B7CD9A4A7443C, 720, 236 },
- { 0xBB764C4CA7A44410, 747, 244 },
- { 0x8BAB8EEFB6409C1A, 774, 252 },
- { 0xD01FEF10A657842C, 800, 260 },
- { 0x9B10A4E5E9913129, 827, 268 },
- { 0xE7109BFBA19C0C9D, 853, 276 },
- { 0xAC2820D9623BF429, 880, 284 },
- { 0x80444B5E7AA7CF85, 907, 292 },
- { 0xBF21E44003ACDD2D, 933, 300 },
- { 0x8E679C2F5E44FF8F, 960, 308 },
- { 0xD433179D9C8CB841, 986, 316 },
- { 0x9E19DB92B4E31BA9, 1013, 324 },
- };
-
- // This computation gives exactly the same results for k as
- // k = ceil((kAlpha - e - 1) * 0.30102999566398114)
- // for |e| <= 1500, but doesn't require floating-point operations.
- // NB: log_10(2) ~= 78913 / 2^18
- assert(e >= -1500);
- assert(e <= 1500);
- const int f = kAlpha - e - 1;
- const int k = (f * 78913) / (1 << 18) + (f > 0);
-
- const int index = (-kCachedPowersMinDecExp + k + (kCachedPowersDecStep - 1)) / kCachedPowersDecStep;
- assert(index >= 0);
- assert(index < kCachedPowersSize);
- static_cast<void>(kCachedPowersSize); // Fix warning.
-
- const cached_power cached = kCachedPowers[index];
- assert(kAlpha <= cached.e + e + 64);
- assert(kGamma >= cached.e + e + 64);
-
- return cached;
-}
-
-/*!
-For n != 0, returns k, such that pow10 := 10^(k-1) <= n < 10^k.
-For n == 0, returns 1 and sets pow10 := 1.
-*/
-inline int find_largest_pow10(const uint32_t n, uint32_t& pow10)
-{
- // LCOV_EXCL_START
- if (n >= 1000000000)
- {
- pow10 = 1000000000;
- return 10;
- }
- // LCOV_EXCL_STOP
- else if (n >= 100000000)
- {
- pow10 = 100000000;
- return 9;
- }
- else if (n >= 10000000)
- {
- pow10 = 10000000;
- return 8;
- }
- else if (n >= 1000000)
- {
- pow10 = 1000000;
- return 7;
- }
- else if (n >= 100000)
- {
- pow10 = 100000;
- return 6;
- }
- else if (n >= 10000)
- {
- pow10 = 10000;
- return 5;
- }
- else if (n >= 1000)
- {
- pow10 = 1000;
- return 4;
- }
- else if (n >= 100)
- {
- pow10 = 100;
- return 3;
- }
- else if (n >= 10)
- {
- pow10 = 10;
- return 2;
- }
- else
- {
- pow10 = 1;
- return 1;
- }
-}
-
-inline void grisu2_round(char* buf, int len, uint64_t dist, uint64_t delta,
- uint64_t rest, uint64_t ten_k)
-{
- assert(len >= 1);
- assert(dist <= delta);
- assert(rest <= delta);
- assert(ten_k > 0);
-
- // <--------------------------- delta ---->
- // <---- dist --------->
- // --------------[------------------+-------------------]--------------
- // M- w M+
- //
- // ten_k
- // <------>
- // <---- rest ---->
- // --------------[------------------+----+--------------]--------------
- // w V
- // = buf * 10^k
- //
- // ten_k represents a unit-in-the-last-place in the decimal representation
- // stored in buf.
- // Decrement buf by ten_k while this takes buf closer to w.
-
- // The tests are written in this order to avoid overflow in unsigned
- // integer arithmetic.
-
- while (rest < dist
- and delta - rest >= ten_k
- and (rest + ten_k < dist or dist - rest > rest + ten_k - dist))
- {
- assert(buf[len - 1] != '0');
- buf[len - 1]--;
- rest += ten_k;
- }
-}
-
-/*!
-Generates V = buffer * 10^decimal_exponent, such that M- <= V <= M+.
-M- and M+ must be normalized and share the same exponent -60 <= e <= -32.
-*/
-inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent,
- diyfp M_minus, diyfp w, diyfp M_plus)
-{
- static_assert(kAlpha >= -60, "internal error");
- static_assert(kGamma <= -32, "internal error");
-
- // Generates the digits (and the exponent) of a decimal floating-point
- // number V = buffer * 10^decimal_exponent in the range [M-, M+]. The diyfp's
- // w, M- and M+ share the same exponent e, which satisfies alpha <= e <= gamma.
- //
- // <--------------------------- delta ---->
- // <---- dist --------->
- // --------------[------------------+-------------------]--------------
- // M- w M+
- //
- // Grisu2 generates the digits of M+ from left to right and stops as soon as
- // V is in [M-,M+].
-
- assert(M_plus.e >= kAlpha);
- assert(M_plus.e <= kGamma);
-
- uint64_t delta = diyfp::sub(M_plus, M_minus).f; // (significand of (M+ - M-), implicit exponent is e)
- uint64_t dist = diyfp::sub(M_plus, w ).f; // (significand of (M+ - w ), implicit exponent is e)
-
- // Split M+ = f * 2^e into two parts p1 and p2 (note: e < 0):
- //
- // M+ = f * 2^e
- // = ((f div 2^-e) * 2^-e + (f mod 2^-e)) * 2^e
- // = ((p1 ) * 2^-e + (p2 )) * 2^e
- // = p1 + p2 * 2^e
-
- const diyfp one(uint64_t{1} << -M_plus.e, M_plus.e);
-
- uint32_t p1 = static_cast<uint32_t>(M_plus.f >> -one.e); // p1 = f div 2^-e (Since -e >= 32, p1 fits into a 32-bit int.)
- uint64_t p2 = M_plus.f & (one.f - 1); // p2 = f mod 2^-e
-
- // 1)
- //
- // Generate the digits of the integral part p1 = d[n-1]...d[1]d[0]
-
- assert(p1 > 0);
-
- uint32_t pow10;
- const int k = find_largest_pow10(p1, pow10);
-
- // 10^(k-1) <= p1 < 10^k, pow10 = 10^(k-1)
- //
- // p1 = (p1 div 10^(k-1)) * 10^(k-1) + (p1 mod 10^(k-1))
- // = (d[k-1] ) * 10^(k-1) + (p1 mod 10^(k-1))
- //
- // M+ = p1 + p2 * 2^e
- // = d[k-1] * 10^(k-1) + (p1 mod 10^(k-1)) + p2 * 2^e
- // = d[k-1] * 10^(k-1) + ((p1 mod 10^(k-1)) * 2^-e + p2) * 2^e
- // = d[k-1] * 10^(k-1) + ( rest) * 2^e
- //
- // Now generate the digits d[n] of p1 from left to right (n = k-1,...,0)
- //
- // p1 = d[k-1]...d[n] * 10^n + d[n-1]...d[0]
- //
- // but stop as soon as
- //
- // rest * 2^e = (d[n-1]...d[0] * 2^-e + p2) * 2^e <= delta * 2^e
-
- int n = k;
- while (n > 0)
- {
- // Invariants:
- // M+ = buffer * 10^n + (p1 + p2 * 2^e) (buffer = 0 for n = k)
- // pow10 = 10^(n-1) <= p1 < 10^n
- //
- const uint32_t d = p1 / pow10; // d = p1 div 10^(n-1)
- const uint32_t r = p1 % pow10; // r = p1 mod 10^(n-1)
- //
- // M+ = buffer * 10^n + (d * 10^(n-1) + r) + p2 * 2^e
- // = (buffer * 10 + d) * 10^(n-1) + (r + p2 * 2^e)
- //
- assert(d <= 9);
- buffer[length++] = static_cast<char>('0' + d); // buffer := buffer * 10 + d
- //
- // M+ = buffer * 10^(n-1) + (r + p2 * 2^e)
- //
- p1 = r;
- n--;
- //
- // M+ = buffer * 10^n + (p1 + p2 * 2^e)
- // pow10 = 10^n
- //
-
- // Now check if enough digits have been generated.
- // Compute
- //
- // p1 + p2 * 2^e = (p1 * 2^-e + p2) * 2^e = rest * 2^e
- //
- // Note:
- // Since rest and delta share the same exponent e, it suffices to
- // compare the significands.
- const uint64_t rest = (uint64_t{p1} << -one.e) + p2;
- if (rest <= delta)
- {
- // V = buffer * 10^n, with M- <= V <= M+.
-
- decimal_exponent += n;
-
- // We may now just stop. But instead look if the buffer could be
- // decremented to bring V closer to w.
- //
- // pow10 = 10^n is now 1 ulp in the decimal representation V.
- // The rounding procedure works with diyfp's with an implicit
- // exponent of e.
- //
- // 10^n = (10^n * 2^-e) * 2^e = ulp * 2^e
- //
- const uint64_t ten_n = uint64_t{pow10} << -one.e;
- grisu2_round(buffer, length, dist, delta, rest, ten_n);
-
- return;
- }
-
- pow10 /= 10;
- //
- // pow10 = 10^(n-1) <= p1 < 10^n
- // Invariants restored.
- }
-
- // 2)
- //
- // The digits of the integral part have been generated:
- //
- // M+ = d[k-1]...d[1]d[0] + p2 * 2^e
- // = buffer + p2 * 2^e
- //
- // Now generate the digits of the fractional part p2 * 2^e.
- //
- // Note:
- // No decimal point is generated: the exponent is adjusted instead.
- //
- // p2 actually represents the fraction
- //
- // p2 * 2^e
- // = p2 / 2^-e
- // = d[-1] / 10^1 + d[-2] / 10^2 + ...
- //
- // Now generate the digits d[-m] of p1 from left to right (m = 1,2,...)
- //
- // p2 * 2^e = d[-1]d[-2]...d[-m] * 10^-m
- // + 10^-m * (d[-m-1] / 10^1 + d[-m-2] / 10^2 + ...)
- //
- // using
- //
- // 10^m * p2 = ((10^m * p2) div 2^-e) * 2^-e + ((10^m * p2) mod 2^-e)
- // = ( d) * 2^-e + ( r)
- //
- // or
- // 10^m * p2 * 2^e = d + r * 2^e
- //
- // i.e.
- //
- // M+ = buffer + p2 * 2^e
- // = buffer + 10^-m * (d + r * 2^e)
- // = (buffer * 10^m + d) * 10^-m + 10^-m * r * 2^e
- //
- // and stop as soon as 10^-m * r * 2^e <= delta * 2^e
-
- assert(p2 > delta);
-
- int m = 0;
- for (;;)
- {
- // Invariant:
- // M+ = buffer * 10^-m + 10^-m * (d[-m-1] / 10 + d[-m-2] / 10^2 + ...) * 2^e
- // = buffer * 10^-m + 10^-m * (p2 ) * 2^e
- // = buffer * 10^-m + 10^-m * (1/10 * (10 * p2) ) * 2^e
- // = buffer * 10^-m + 10^-m * (1/10 * ((10*p2 div 2^-e) * 2^-e + (10*p2 mod 2^-e)) * 2^e
- //
- assert(p2 <= UINT64_MAX / 10);
- p2 *= 10;
- const uint64_t d = p2 >> -one.e; // d = (10 * p2) div 2^-e
- const uint64_t r = p2 & (one.f - 1); // r = (10 * p2) mod 2^-e
- //
- // M+ = buffer * 10^-m + 10^-m * (1/10 * (d * 2^-e + r) * 2^e
- // = buffer * 10^-m + 10^-m * (1/10 * (d + r * 2^e))
- // = (buffer * 10 + d) * 10^(-m-1) + 10^(-m-1) * r * 2^e
- //
- assert(d <= 9);
- buffer[length++] = static_cast<char>('0' + d); // buffer := buffer * 10 + d
- //
- // M+ = buffer * 10^(-m-1) + 10^(-m-1) * r * 2^e
- //
- p2 = r;
- m++;
- //
- // M+ = buffer * 10^-m + 10^-m * p2 * 2^e
- // Invariant restored.
-
- // Check if enough digits have been generated.
- //
- // 10^-m * p2 * 2^e <= delta * 2^e
- // p2 * 2^e <= 10^m * delta * 2^e
- // p2 <= 10^m * delta
- delta *= 10;
- dist *= 10;
- if (p2 <= delta)
- {
- break;
- }
- }
-
- // V = buffer * 10^-m, with M- <= V <= M+.
-
- decimal_exponent -= m;
-
- // 1 ulp in the decimal representation is now 10^-m.
- // Since delta and dist are now scaled by 10^m, we need to do the
- // same with ulp in order to keep the units in sync.
- //
- // 10^m * 10^-m = 1 = 2^-e * 2^e = ten_m * 2^e
- //
- const uint64_t ten_m = one.f;
- grisu2_round(buffer, length, dist, delta, p2, ten_m);
-
- // By construction this algorithm generates the shortest possible decimal
- // number (Loitsch, Theorem 6.2) which rounds back to w.
- // For an input number of precision p, at least
- //
- // N = 1 + ceil(p * log_10(2))
- //
- // decimal digits are sufficient to identify all binary floating-point
- // numbers (Matula, "In-and-Out conversions").
- // This implies that the algorithm does not produce more than N decimal
- // digits.
- //
- // N = 17 for p = 53 (IEEE double precision)
- // N = 9 for p = 24 (IEEE single precision)
-}
-
-/*!
-v = buf * 10^decimal_exponent
-len is the length of the buffer (number of decimal digits)
-The buffer must be large enough, i.e. >= max_digits10.
-*/
-inline void grisu2(char* buf, int& len, int& decimal_exponent,
- diyfp m_minus, diyfp v, diyfp m_plus)
-{
- assert(m_plus.e == m_minus.e);
- assert(m_plus.e == v.e);
-
- // --------(-----------------------+-----------------------)-------- (A)
- // m- v m+
- //
- // --------------------(-----------+-----------------------)-------- (B)
- // m- v m+
- //
- // First scale v (and m- and m+) such that the exponent is in the range
- // [alpha, gamma].
-
- const cached_power cached = get_cached_power_for_binary_exponent(m_plus.e);
-
- const diyfp c_minus_k(cached.f, cached.e); // = c ~= 10^-k
-
- // The exponent of the products is = v.e + c_minus_k.e + q and is in the range [alpha,gamma]
- const diyfp w = diyfp::mul(v, c_minus_k);
- const diyfp w_minus = diyfp::mul(m_minus, c_minus_k);
- const diyfp w_plus = diyfp::mul(m_plus, c_minus_k);
-
- // ----(---+---)---------------(---+---)---------------(---+---)----
- // w- w w+
- // = c*m- = c*v = c*m+
- //
- // diyfp::mul rounds its result and c_minus_k is approximated too. w, w- and
- // w+ are now off by a small amount.
- // In fact:
- //
- // w - v * 10^k < 1 ulp
- //
- // To account for this inaccuracy, add resp. subtract 1 ulp.
- //
- // --------+---[---------------(---+---)---------------]---+--------
- // w- M- w M+ w+
- //
- // Now any number in [M-, M+] (bounds included) will round to w when input,
- // regardless of how the input rounding algorithm breaks ties.
- //
- // And digit_gen generates the shortest possible such number in [M-, M+].
- // Note that this does not mean that Grisu2 always generates the shortest
- // possible number in the interval (m-, m+).
- const diyfp M_minus(w_minus.f + 1, w_minus.e);
- const diyfp M_plus (w_plus.f - 1, w_plus.e );
-
- decimal_exponent = -cached.k; // = -(-k) = k
-
- grisu2_digit_gen(buf, len, decimal_exponent, M_minus, w, M_plus);
-}
-
-/*!
-v = buf * 10^decimal_exponent
-len is the length of the buffer (number of decimal digits)
-The buffer must be large enough, i.e. >= max_digits10.
-*/
-template <typename FloatType>
-void grisu2(char* buf, int& len, int& decimal_exponent, FloatType value)
-{
- static_assert(diyfp::kPrecision >= std::numeric_limits<FloatType>::digits + 3,
- "internal error: not enough precision");
-
- assert(std::isfinite(value));
- assert(value > 0);
-
- // If the neighbors (and boundaries) of 'value' are always computed for double-precision
- // numbers, all float's can be recovered using strtod (and strtof). However, the resulting
- // decimal representations are not exactly "short".
- //
- // The documentation for 'std::to_chars' (http://en.cppreference.com/w/cpp/utility/to_chars)
- // says "value is converted to a string as if by std::sprintf in the default ("C") locale"
- // and since sprintf promotes float's to double's, I think this is exactly what 'std::to_chars'
- // does.
- // On the other hand, the documentation for 'std::to_chars' requires that "parsing the
- // representation using the corresponding std::from_chars function recovers value exactly". That
- // indicates that single precision floating-point numbers should be recovered using
- // 'std::strtof'.
- //
- // NB: If the neighbors are computed for single-precision numbers, there is a single float
- // (7.0385307e-26f) which can't be recovered using strtod. The resulting double precision
- // value is off by 1 ulp.
-#if 0
- const boundaries w = compute_boundaries(static_cast<double>(value));
-#else
- const boundaries w = compute_boundaries(value);
-#endif
-
- grisu2(buf, len, decimal_exponent, w.minus, w.w, w.plus);
-}
-
-/*!
-@brief appends a decimal representation of e to buf
-@return a pointer to the element following the exponent.
-@pre -1000 < e < 1000
-*/
-inline char* append_exponent(char* buf, int e)
-{
- assert(e > -1000);
- assert(e < 1000);
-
- if (e < 0)
- {
- e = -e;
- *buf++ = '-';
- }
- else
- {
- *buf++ = '+';
- }
-
- uint32_t k = static_cast<uint32_t>(e);
- if (k < 10)
- {
- // Always print at least two digits in the exponent.
- // This is for compatibility with printf("%g").
- *buf++ = '0';
- *buf++ = static_cast<char>('0' + k);
- }
- else if (k < 100)
- {
- *buf++ = static_cast<char>('0' + k / 10);
- k %= 10;
- *buf++ = static_cast<char>('0' + k);
- }
- else
- {
- *buf++ = static_cast<char>('0' + k / 100);
- k %= 100;
- *buf++ = static_cast<char>('0' + k / 10);
- k %= 10;
- *buf++ = static_cast<char>('0' + k);
- }
-
- return buf;
-}
-
-/*!
-@brief prettify v = buf * 10^decimal_exponent
-
-If v is in the range [10^min_exp, 10^max_exp) it will be printed in fixed-point
-notation. Otherwise it will be printed in exponential notation.
-
-@pre min_exp < 0
-@pre max_exp > 0
-*/
-inline char* format_buffer(char* buf, int len, int decimal_exponent,
- int min_exp, int max_exp)
-{
- assert(min_exp < 0);
- assert(max_exp > 0);
-
- const int k = len;
- const int n = len + decimal_exponent;
-
- // v = buf * 10^(n-k)
- // k is the length of the buffer (number of decimal digits)
- // n is the position of the decimal point relative to the start of the buffer.
-
- if (k <= n and n <= max_exp)
- {
- // digits[000]
- // len <= max_exp + 2
-
- std::memset(buf + k, '0', static_cast<size_t>(n - k));
- // Make it look like a floating-point number (#362, #378)
- buf[n + 0] = '.';
- buf[n + 1] = '0';
- return buf + (n + 2);
- }
-
- if (0 < n and n <= max_exp)
- {
- // dig.its
- // len <= max_digits10 + 1
-
- assert(k > n);
-
- std::memmove(buf + (n + 1), buf + n, static_cast<size_t>(k - n));
- buf[n] = '.';
- return buf + (k + 1);
- }
-
- if (min_exp < n and n <= 0)
- {
- // 0.[000]digits
- // len <= 2 + (-min_exp - 1) + max_digits10
-
- std::memmove(buf + (2 + -n), buf, static_cast<size_t>(k));
- buf[0] = '0';
- buf[1] = '.';
- std::memset(buf + 2, '0', static_cast<size_t>(-n));
- return buf + (2 + (-n) + k);
- }
-
- if (k == 1)
- {
- // dE+123
- // len <= 1 + 5
-
- buf += 1;
- }
- else
- {
- // d.igitsE+123
- // len <= max_digits10 + 1 + 5
-
- std::memmove(buf + 2, buf + 1, static_cast<size_t>(k - 1));
- buf[1] = '.';
- buf += 1 + k;
- }
-
- *buf++ = 'e';
- return append_exponent(buf, n - 1);
-}
-
-} // namespace dtoa_impl
-
-/*!
-@brief generates a decimal representation of the floating-point number value in [first, last).
-
-The format of the resulting decimal representation is similar to printf's %g
-format. Returns an iterator pointing past-the-end of the decimal representation.
-
-@note The input number must be finite, i.e. NaN's and Inf's are not supported.
-@note The buffer must be large enough.
-@note The result is NOT null-terminated.
-*/
-template <typename FloatType>
-char* to_chars(char* first, char* last, FloatType value)
-{
- static_cast<void>(last); // maybe unused - fix warning
- assert(std::isfinite(value));
-
- // Use signbit(value) instead of (value < 0) since signbit works for -0.
- if (std::signbit(value))
- {
- value = -value;
- *first++ = '-';
- }
-
- if (value == 0) // +-0
- {
- *first++ = '0';
- // Make it look like a floating-point number (#362, #378)
- *first++ = '.';
- *first++ = '0';
- return first;
- }
-
- assert(last - first >= std::numeric_limits<FloatType>::max_digits10);
-
- // Compute v = buffer * 10^decimal_exponent.
- // The decimal digits are stored in the buffer, which needs to be interpreted
- // as an unsigned decimal integer.
- // len is the length of the buffer, i.e. the number of decimal digits.
- int len = 0;
- int decimal_exponent = 0;
- dtoa_impl::grisu2(first, len, decimal_exponent, value);
-
- assert(len <= std::numeric_limits<FloatType>::max_digits10);
-
- // Format the buffer like printf("%.*g", prec, value)
- constexpr int kMinExp = -4;
- // Use digits10 here to increase compatibility with version 2.
- constexpr int kMaxExp = std::numeric_limits<FloatType>::digits10;
-
- assert(last - first >= kMaxExp + 2);
- assert(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits<FloatType>::max_digits10);
- assert(last - first >= std::numeric_limits<FloatType>::max_digits10 + 6);
-
- return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp, kMaxExp);
-}
-
-} // namespace
-
-void json::serializer::dump(const json& val, const bool pretty_print,
- const bool ensure_ascii,
- const unsigned int indent_step,
- const unsigned int current_indent)
-{
- switch (val.m_type)
- {
- case value_t::object:
- {
- if (val.m_value.object->empty())
- {
- o << "{}";
- return;
- }
-
- // we need to iterate over the object values in sorted key order
- SmallVector<StringMapConstIterator<json>, 64> sorted;
- for (auto i = val.m_value.object->begin(),
- end = val.m_value.object->end(); i != end; ++i)
- {
- sorted.push_back(i);
- }
- std::sort(sorted.begin(), sorted.end(),
- [](const StringMapConstIterator<json>& a,
- const StringMapConstIterator<json>& b) {
- return a->getKey() < b->getKey();
- });
-
- if (pretty_print)
- {
- o << "{\n";
-
- // variable to hold indentation for recursive calls
- const auto new_indent = current_indent + indent_step;
- if (JSON_UNLIKELY(indent_string.size() < new_indent))
- {
- indent_string.resize(indent_string.size() * 2, indent_char);
- }
-
- // first n-1 elements
- auto i = sorted.begin();
- for (std::size_t cnt = 0; cnt < sorted.size() - 1; ++cnt, ++i)
- {
- o.write(indent_string.c_str(), new_indent);
- o << '\"';
- dump_escaped((*i)->first(), ensure_ascii);
- o << "\": ";
- dump((*i)->second, true, ensure_ascii, indent_step, new_indent);
- o << ",\n";
- }
-
- // last element
- assert(i != sorted.end());
- //assert(std::next(i) == val.m_value.object->end());
- o.write(indent_string.c_str(), new_indent);
- o << '\"';
- dump_escaped((*i)->first(), ensure_ascii);
- o << "\": ";
- dump((*i)->second, true, ensure_ascii, indent_step, new_indent);
-
- o << '\n';
- o.write(indent_string.c_str(), current_indent);
- o << '}';
- }
- else
- {
- o << '{';
-
- // first n-1 elements
- auto i = sorted.begin();
- for (std::size_t cnt = 0; cnt < sorted.size() - 1; ++cnt, ++i)
- {
- o << '\"';
- dump_escaped((*i)->first(), ensure_ascii);
- o << "\":";
- dump((*i)->second, false, ensure_ascii, indent_step, current_indent);
- o << ',';
- }
-
- // last element
- assert(i != sorted.end());
- //assert(std::next(i) == val.m_value.object->end());
- o << '\"';
- dump_escaped((*i)->first(), ensure_ascii);
- o << "\":";
- dump((*i)->second, false, ensure_ascii, indent_step, current_indent);
-
- o << '}';
- }
-
- return;
- }
-
- case value_t::array:
- {
- if (val.m_value.array->empty())
- {
- o << "[]";
- return;
- }
-
- if (pretty_print)
- {
- o << "[\n";
-
- // variable to hold indentation for recursive calls
- const auto new_indent = current_indent + indent_step;
- if (JSON_UNLIKELY(indent_string.size() < new_indent))
- {
- indent_string.resize(indent_string.size() * 2, indent_char);
- }
-
- // first n-1 elements
- for (auto i = val.m_value.array->cbegin();
- i != val.m_value.array->cend() - 1; ++i)
- {
- o.write(indent_string.c_str(), new_indent);
- dump(*i, true, ensure_ascii, indent_step, new_indent);
- o << ",\n";
- }
-
- // last element
- assert(not val.m_value.array->empty());
- o.write(indent_string.c_str(), new_indent);
- dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent);
-
- o << '\n';
- o.write(indent_string.c_str(), current_indent);
- o << ']';
- }
- else
- {
- o << '[';
-
- // first n-1 elements
- for (auto i = val.m_value.array->cbegin();
- i != val.m_value.array->cend() - 1; ++i)
- {
- dump(*i, false, ensure_ascii, indent_step, current_indent);
- o << ',';
- }
-
- // last element
- assert(not val.m_value.array->empty());
- dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent);
-
- o << ']';
- }
-
- return;
- }
-
- case value_t::string:
- {
- o << '\"';
- dump_escaped(*val.m_value.string, ensure_ascii);
- o << '\"';
- return;
- }
-
- case value_t::boolean:
- {
- if (val.m_value.boolean)
- {
- o << "true";
- }
- else
- {
- o << "false";
- }
- return;
- }
-
- case value_t::number_integer:
- {
- dump_integer(val.m_value.number_integer);
- return;
- }
-
- case value_t::number_unsigned:
- {
- dump_integer(val.m_value.number_unsigned);
- return;
- }
-
- case value_t::number_float:
- {
- dump_float(val.m_value.number_float);
- return;
- }
-
- case value_t::discarded:
- {
- o << "<discarded>";
- return;
- }
-
- case value_t::null:
- {
- o << "null";
- return;
- }
- }
-}
-
-void json::serializer::dump_escaped(std::string_view s, const bool ensure_ascii)
-{
- uint32_t codepoint;
- uint8_t state = UTF8_ACCEPT;
-
- for (std::size_t i = 0; i < s.size(); ++i)
- {
- const auto byte = static_cast<uint8_t>(s[i]);
-
- switch (decode(state, codepoint, byte))
- {
- case UTF8_ACCEPT: // decode found a new code point
- {
- switch (codepoint)
- {
- case 0x08: // backspace
- {
- o << '\\' << 'b';
- break;
- }
-
- case 0x09: // horizontal tab
- {
- o << '\\' << 't';
- break;
- }
-
- case 0x0A: // newline
- {
- o << '\\' << 'n';
- break;
- }
-
- case 0x0C: // formfeed
- {
- o << '\\' << 'f';
- break;
- }
-
- case 0x0D: // carriage return
- {
- o << '\\' << 'r';
- break;
- }
-
- case 0x22: // quotation mark
- {
- o << '\\' << '\"';
- break;
- }
-
- case 0x5C: // reverse solidus
- {
- o << '\\' << '\\';
- break;
- }
-
- default:
- {
- // escape control characters (0x00..0x1F) or, if
- // ensure_ascii parameter is used, non-ASCII characters
- if ((codepoint <= 0x1F) or (ensure_ascii and (codepoint >= 0x7F)))
- {
- if (codepoint <= 0xFFFF)
- {
- o << fmt::format("\\u{:04x}", codepoint);
- }
- else
- {
- o << fmt::format("\\u{:04x}", 0xD7C0 + (codepoint >> 10));
- o << fmt::format("\\u{:04x}", 0xDC00 + (codepoint & 0x3FF));
- }
- }
- else
- {
- // copy byte to buffer (all previous bytes
- // been copied have in default case above)
- o << s[i];
- }
- break;
- }
- }
- break;
- }
-
- case UTF8_REJECT: // decode found invalid UTF-8 byte
- {
- JSON_THROW(type_error::create(316, fmt::format("invalid UTF-8 byte at index {}: {:#02x}", i, byte)));
- }
-
- default: // decode found yet incomplete multi-byte code point
- {
- if (not ensure_ascii)
- {
- // code point will not be escaped - copy byte to buffer
- o << s[i];
- }
- break;
- }
- }
- }
-
- if (JSON_UNLIKELY(state != UTF8_ACCEPT))
- {
- // we finish reading, but do not accept: string was incomplete
- JSON_THROW(type_error::create(316, fmt::format("incomplete UTF-8 string; last byte: {:#02x}", s.back())));
- }
-}
-
-void json::serializer::dump_float(double x)
-{
- // NaN / inf
- if (not std::isfinite(x))
- {
- o << "null";
- return;
- }
-
- // use the Grisu2 algorithm to produce short numbers which are
- // guaranteed to round-trip, using strtof and strtod, resp.
- char* begin = number_buffer.data();
- char* end = to_chars(begin, begin + number_buffer.size(), x);
-
- o.write(begin, static_cast<size_t>(end - begin));
-}
-
-uint8_t json::serializer::decode(uint8_t& state, uint32_t& codep, const uint8_t byte) noexcept
-{
- static const std::array<uint8_t, 400> utf8d =
- {
- {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..1F
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20..3F
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..5F
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60..7F
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9F
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // A0..BF
- 8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C0..DF
- 0xA, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // E0..EF
- 0xB, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // F0..FF
- 0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2
- 1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4
- 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6
- 1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // s7..s8
- }
- };
-
- const uint8_t type = utf8d[byte];
-
- codep = (state != UTF8_ACCEPT)
- ? (byte & 0x3fu) | (codep << 6)
- : static_cast<uint32_t>(0xff >> type) & (byte);
-
- state = utf8d[256u + state * 16u + type];
- return state;
-}
-
-std::string json::dump(const int indent, const char indent_char,
- const bool ensure_ascii) const
-{
- std::string result;
- raw_string_ostream os(result);
- dump(os, indent, indent_char, ensure_ascii);
- return result;
-}
-
-void json::dump(raw_ostream& os, int indent, const char indent_char,
- const bool ensure_ascii) const
-{
- serializer s(os, indent_char);
-
- if (indent >= 0)
- {
- s.dump(*this, true, ensure_ascii, static_cast<unsigned int>(indent));
- }
- else
- {
- s.dump(*this, false, ensure_ascii, 0);
- }
-
- os.flush();
-}
-
-raw_ostream& operator<<(raw_ostream& o, const json& j)
-{
- j.dump(o, 0);
- return o;
-}
-
-std::ostream& operator<<(std::ostream& o, const json& j)
-{
- raw_os_ostream os(o);
- j.dump(os, 0);
- return o;
-}
-
-} // namespace wpi
diff --git a/wpiutil/src/main/native/cpp/leb128.cpp b/wpiutil/src/main/native/cpp/leb128.cpp
index 9657883..17fbeb9 100644
--- a/wpiutil/src/main/native/cpp/leb128.cpp
+++ b/wpiutil/src/main/native/cpp/leb128.cpp
@@ -7,7 +7,6 @@
#include "wpi/SpanExtras.h"
#include "wpi/raw_istream.h"
#include "wpi/raw_ostream.h"
-#include "wpi/span.h"
namespace wpi {
@@ -98,7 +97,7 @@
return true;
}
-std::optional<uint64_t> Uleb128Reader::ReadOne(span<const uint8_t>* in) {
+std::optional<uint64_t> Uleb128Reader::ReadOne(std::span<const uint8_t>* in) {
while (!in->empty()) {
uint8_t byte = in->front();
*in = wpi::drop_front(*in);
diff --git a/wpiutil/src/main/native/cpp/llvm/ConvertUTF.cpp b/wpiutil/src/main/native/cpp/llvm/ConvertUTF.cpp
deleted file mode 100644
index 3050e63..0000000
--- a/wpiutil/src/main/native/cpp/llvm/ConvertUTF.cpp
+++ /dev/null
@@ -1,834 +0,0 @@
-/*===--- ConvertUTF.c - Universal Character Names conversions ---------------===
- *
- * The LLVM Compiler Infrastructure
- *
- * This file is distributed under the University of Illinois Open Source
- * License. See LICENSE.TXT for details.
- *
- *===------------------------------------------------------------------------=*/
-/*
- * Copyright 2001-2004 Unicode, Inc.
- *
- * Disclaimer
- *
- * This source code is provided as is by Unicode, Inc. No claims are
- * made as to fitness for any particular purpose. No warranties of any
- * kind are expressed or implied. The recipient agrees to determine
- * applicability of information provided. If this file has been
- * purchased on magnetic or optical media from Unicode, Inc., the
- * sole remedy for any claim will be exchange of defective media
- * within 90 days of receipt.
- *
- * Limitations on Rights to Redistribute This Code
- *
- * Unicode, Inc. hereby grants the right to freely use the information
- * supplied in this file in the creation of products supporting the
- * Unicode Standard, and to make copies of this file in any form
- * for internal or external distribution as long as this notice
- * remains attached.
- */
-
-/* ---------------------------------------------------------------------
-
- Conversions between UTF32, UTF-16, and UTF-8. Source code file.
- Author: Mark E. Davis, 1994.
- Rev History: Rick McGowan, fixes & updates May 2001.
- Sept 2001: fixed const & error conditions per
- mods suggested by S. Parent & A. Lillich.
- June 2002: Tim Dodd added detection and handling of incomplete
- source sequences, enhanced error detection, added casts
- to eliminate compiler warnings.
- July 2003: slight mods to back out aggressive FFFE detection.
- Jan 2004: updated switches in from-UTF8 conversions.
- Oct 2004: updated to use UNI_MAX_LEGAL_UTF32 in UTF-32 conversions.
-
- See the header file "ConvertUTF.h" for complete documentation.
-
------------------------------------------------------------------------- */
-
-#include "wpi/ConvertUTF.h"
-#ifdef CVTUTF_DEBUG
-#include <stdio.h>
-#endif
-#include <assert.h>
-
-#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.
- */
-#if defined(__clang__) && defined(__has_warning)
-# if __has_warning("-Wimplicit-fallthrough")
-# define ConvertUTF_DISABLE_WARNINGS \
- _Pragma("clang diagnostic push") \
- _Pragma("clang diagnostic ignored \"-Wimplicit-fallthrough\"")
-# define ConvertUTF_RESTORE_WARNINGS \
- _Pragma("clang diagnostic pop")
-# endif
-#elif defined(__GNUC__) && __GNUC__ > 6
-# define ConvertUTF_DISABLE_WARNINGS \
- _Pragma("GCC diagnostic push") \
- _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"")
-# define ConvertUTF_RESTORE_WARNINGS \
- _Pragma("GCC diagnostic pop")
-#endif
-#ifndef ConvertUTF_DISABLE_WARNINGS
-# define ConvertUTF_DISABLE_WARNINGS
-#endif
-#ifndef ConvertUTF_RESTORE_WARNINGS
-# define ConvertUTF_RESTORE_WARNINGS
-#endif
-
-ConvertUTF_DISABLE_WARNINGS
-
-namespace wpi {
-
-static const int halfShift = 10; /* used for shifting by 10 bits */
-
-static const UTF32 halfBase = 0x0010000UL;
-static const UTF32 halfMask = 0x3FFUL;
-
-#define UNI_SUR_HIGH_START (UTF32)0xD800
-#define UNI_SUR_HIGH_END (UTF32)0xDBFF
-#define UNI_SUR_LOW_START (UTF32)0xDC00
-#define UNI_SUR_LOW_END (UTF32)0xDFFF
-
-/* --------------------------------------------------------------------- */
-
-/*
- * Index into the table below with the first byte of a UTF-8 sequence to
- * get the number of trailing bytes that are supposed to follow it.
- * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is
- * left as-is for anyone who may want to do such conversion, which was
- * allowed in earlier algorithms.
- */
-static const char trailingBytesForUTF8[256] = {
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
- 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
-};
-
-/*
- * Magic values subtracted from a buffer value during UTF8 conversion.
- * This table contains as many values as there might be trailing bytes
- * in a UTF-8 sequence.
- */
-static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL,
- 0x03C82080UL, 0xFA082080UL, 0x82082080UL };
-
-/*
- * Once the bits are split out into bytes of UTF-8, this is a mask OR-ed
- * into the first byte, depending on how many bytes follow. There are
- * as many entries in this table as there are UTF-8 sequence types.
- * (I.e., one byte sequence, two byte... etc.). Remember that sequencs
- * for *legal* UTF-8 will be 4 or fewer bytes total.
- */
-static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
-
-/* --------------------------------------------------------------------- */
-
-/* The interface converts a whole buffer to avoid function-call overhead.
- * Constants have been gathered. Loops & conditionals have been removed as
- * much as possible for efficiency, in favor of drop-through switches.
- * (See "Note A" at the bottom of the file for equivalent code.)
- * If your compiler supports it, the "isLegalUTF8" call can be turned
- * into an inline function.
- */
-
-
-/* --------------------------------------------------------------------- */
-
-ConversionResult ConvertUTF32toUTF16 (
- const UTF32** sourceStart, const UTF32* sourceEnd,
- UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) {
- ConversionResult result = conversionOK;
- const UTF32* source = *sourceStart;
- UTF16* target = *targetStart;
- while (source < sourceEnd) {
- UTF32 ch;
- if (target >= targetEnd) {
- result = targetExhausted; break;
- }
- ch = *source++;
- if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */
- /* UTF-16 surrogate values are illegal in UTF-32; 0xffff or 0xfffe are both reserved values */
- if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
- if (flags == strictConversion) {
- --source; /* return to the illegal value itself */
- result = sourceIllegal;
- break;
- } else {
- *target++ = UNI_REPLACEMENT_CHAR;
- }
- } else {
- *target++ = (UTF16)ch; /* normal case */
- }
- } else if (ch > UNI_MAX_LEGAL_UTF32) {
- if (flags == strictConversion) {
- result = sourceIllegal;
- } else {
- *target++ = UNI_REPLACEMENT_CHAR;
- }
- } else {
- /* target is a character in range 0xFFFF - 0x10FFFF. */
- if (target + 1 >= targetEnd) {
- --source; /* Back up source pointer! */
- result = targetExhausted; break;
- }
- ch -= halfBase;
- *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START);
- *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START);
- }
- }
- *sourceStart = source;
- *targetStart = target;
- return result;
-}
-
-/* --------------------------------------------------------------------- */
-
-ConversionResult ConvertUTF16toUTF32 (
- const UTF16** sourceStart, const UTF16* sourceEnd,
- UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) {
- ConversionResult result = conversionOK;
- const UTF16* source = *sourceStart;
- UTF32* target = *targetStart;
- UTF32 ch, ch2;
- while (source < sourceEnd) {
- const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */
- ch = *source++;
- /* If we have a surrogate pair, convert to UTF32 first. */
- if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) {
- /* If the 16 bits following the high surrogate are in the source buffer... */
- if (source < sourceEnd) {
- ch2 = *source;
- /* If it's a low surrogate, convert to UTF32. */
- if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) {
- ch = ((ch - UNI_SUR_HIGH_START) << halfShift)
- + (ch2 - UNI_SUR_LOW_START) + halfBase;
- ++source;
- } else if (flags == strictConversion) { /* it's an unpaired high surrogate */
- --source; /* return to the illegal value itself */
- result = sourceIllegal;
- break;
- }
- } else { /* We don't have the 16 bits following the high surrogate. */
- --source; /* return to the high surrogate */
- result = sourceExhausted;
- break;
- }
- } else if (flags == strictConversion) {
- /* UTF-16 surrogate values are illegal in UTF-32 */
- if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) {
- --source; /* return to the illegal value itself */
- result = sourceIllegal;
- break;
- }
- }
- if (target >= targetEnd) {
- source = oldSource; /* Back up source pointer! */
- result = targetExhausted; break;
- }
- *target++ = ch;
- }
- *sourceStart = source;
- *targetStart = target;
-#ifdef CVTUTF_DEBUG
-if (result == sourceIllegal) {
- fprintf(stderr, "ConvertUTF16toUTF32 illegal seq 0x%04x,%04x\n", ch, ch2);
- fflush(stderr);
-}
-#endif
- return result;
-}
-ConversionResult ConvertUTF16toUTF8 (
- const UTF16** sourceStart, const UTF16* sourceEnd,
- UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) {
- ConversionResult result = conversionOK;
- const UTF16* source = *sourceStart;
- UTF8* target = *targetStart;
- while (source < sourceEnd) {
- UTF32 ch;
- unsigned short bytesToWrite = 0;
- const UTF32 byteMask = 0xBF;
- const UTF32 byteMark = 0x80;
- const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */
- ch = *source++;
- /* If we have a surrogate pair, convert to UTF32 first. */
- if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) {
- /* If the 16 bits following the high surrogate are in the source buffer... */
- if (source < sourceEnd) {
- UTF32 ch2 = *source;
- /* If it's a low surrogate, convert to UTF32. */
- if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) {
- ch = ((ch - UNI_SUR_HIGH_START) << halfShift)
- + (ch2 - UNI_SUR_LOW_START) + halfBase;
- ++source;
- } else if (flags == strictConversion) { /* it's an unpaired high surrogate */
- --source; /* return to the illegal value itself */
- result = sourceIllegal;
- break;
- }
- } else { /* We don't have the 16 bits following the high surrogate. */
- --source; /* return to the high surrogate */
- result = sourceExhausted;
- break;
- }
- } else if (flags == strictConversion) {
- /* UTF-16 surrogate values are illegal in UTF-32 */
- if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) {
- --source; /* return to the illegal value itself */
- result = sourceIllegal;
- break;
- }
- }
- /* Figure out how many bytes the result will require */
- if (ch < (UTF32)0x80) { bytesToWrite = 1;
- } else if (ch < (UTF32)0x800) { bytesToWrite = 2;
- } else if (ch < (UTF32)0x10000) { bytesToWrite = 3;
- } else if (ch < (UTF32)0x110000) { bytesToWrite = 4;
- } else { bytesToWrite = 3;
- ch = UNI_REPLACEMENT_CHAR;
- }
-
- target += bytesToWrite;
- if (target > targetEnd) {
- source = oldSource; /* Back up source pointer! */
- target -= bytesToWrite; result = targetExhausted; break;
- }
- switch (bytesToWrite) { /* note: everything falls through. */
- case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
- case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
- case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
- case 1: *--target = (UTF8)(ch | firstByteMark[bytesToWrite]);
- }
- target += bytesToWrite;
- }
- *sourceStart = source;
- *targetStart = target;
- return result;
-}
-
-/* --------------------------------------------------------------------- */
-
-ConversionResult ConvertUTF32toUTF8 (
- const UTF32** sourceStart, const UTF32* sourceEnd,
- UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) {
- ConversionResult result = conversionOK;
- const UTF32* source = *sourceStart;
- UTF8* target = *targetStart;
- while (source < sourceEnd) {
- UTF32 ch;
- unsigned short bytesToWrite = 0;
- const UTF32 byteMask = 0xBF;
- const UTF32 byteMark = 0x80;
- ch = *source++;
- if (flags == strictConversion ) {
- /* UTF-16 surrogate values are illegal in UTF-32 */
- if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
- --source; /* return to the illegal value itself */
- result = sourceIllegal;
- break;
- }
- }
- /*
- * Figure out how many bytes the result will require. Turn any
- * illegally large UTF32 things (> Plane 17) into replacement chars.
- */
- if (ch < (UTF32)0x80) { bytesToWrite = 1;
- } else if (ch < (UTF32)0x800) { bytesToWrite = 2;
- } else if (ch < (UTF32)0x10000) { bytesToWrite = 3;
- } else if (ch <= UNI_MAX_LEGAL_UTF32) { bytesToWrite = 4;
- } else { bytesToWrite = 3;
- ch = UNI_REPLACEMENT_CHAR;
- result = sourceIllegal;
- }
-
- target += bytesToWrite;
- if (target > targetEnd) {
- --source; /* Back up source pointer! */
- target -= bytesToWrite; result = targetExhausted; break;
- }
- switch (bytesToWrite) { /* note: everything falls through. */
- case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
- case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
- case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
- case 1: *--target = (UTF8) (ch | firstByteMark[bytesToWrite]);
- }
- target += bytesToWrite;
- }
- *sourceStart = source;
- *targetStart = target;
- return result;
-}
-
-/* --------------------------------------------------------------------- */
-
-/*
- * Utility routine to tell whether a sequence of bytes is legal UTF-8.
- * This must be called with the length pre-determined by the first byte.
- * If not calling this from ConvertUTF8to*, then the length can be set by:
- * length = trailingBytesForUTF8[*source]+1;
- * and the sequence is illegal right away if there aren't that many bytes
- * available.
- * If presented with a length > 4, this returns false. The Unicode
- * definition of UTF-8 goes up to 4-byte sequences.
- */
-
-static Boolean isLegalUTF8(const UTF8 *source, int length) {
- UTF8 a;
- const UTF8 *srcptr = source+length;
- switch (length) {
- default: return false;
- /* Everything else falls through when "true"... */
- case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
- case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
- case 2: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
-
- switch (*source) {
- /* no fall-through in this inner switch */
- case 0xE0: if (a < 0xA0) return false; break;
- case 0xED: if (a > 0x9F) return false; break;
- case 0xF0: if (a < 0x90) return false; break;
- case 0xF4: if (a > 0x8F) return false; break;
- default: if (a < 0x80) return false;
- }
-
- case 1: if (*source >= 0x80 && *source < 0xC2) return false;
- }
- if (*source > 0xF4) return false;
- return true;
-}
-
-/* --------------------------------------------------------------------- */
-
-/*
- * Exported function to return whether a UTF-8 sequence is legal or not.
- * This is not used here; it's just exported.
- */
-Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd) {
- int length = trailingBytesForUTF8[*source]+1;
- if (length > sourceEnd - source) {
- return false;
- }
- return isLegalUTF8(source, length);
-}
-
-/* --------------------------------------------------------------------- */
-
-static unsigned
-findMaximalSubpartOfIllFormedUTF8Sequence(const UTF8 *source,
- const UTF8 *sourceEnd) {
- UTF8 b1, b2, b3;
-
- assert(!isLegalUTF8Sequence(source, sourceEnd));
-
- /*
- * Unicode 6.3.0, D93b:
- *
- * Maximal subpart of an ill-formed subsequence: The longest code unit
- * subsequence starting at an unconvertible offset that is either:
- * a. the initial subsequence of a well-formed code unit sequence, or
- * b. a subsequence of length one.
- */
-
- if (source == sourceEnd)
- return 0;
-
- /*
- * Perform case analysis. See Unicode 6.3.0, Table 3-7. Well-Formed UTF-8
- * Byte Sequences.
- */
-
- b1 = *source;
- ++source;
- if (b1 >= 0xC2 && b1 <= 0xDF) {
- /*
- * First byte is valid, but we know that this code unit sequence is
- * invalid, so the maximal subpart has to end after the first byte.
- */
- return 1;
- }
-
- if (source == sourceEnd)
- return 1;
-
- b2 = *source;
- ++source;
-
- if (b1 == 0xE0) {
- return (b2 >= 0xA0 && b2 <= 0xBF) ? 2 : 1;
- }
- if (b1 >= 0xE1 && b1 <= 0xEC) {
- return (b2 >= 0x80 && b2 <= 0xBF) ? 2 : 1;
- }
- if (b1 == 0xED) {
- return (b2 >= 0x80 && b2 <= 0x9F) ? 2 : 1;
- }
- if (b1 >= 0xEE && b1 <= 0xEF) {
- return (b2 >= 0x80 && b2 <= 0xBF) ? 2 : 1;
- }
- if (b1 == 0xF0) {
- if (b2 >= 0x90 && b2 <= 0xBF) {
- if (source == sourceEnd)
- return 2;
-
- b3 = *source;
- return (b3 >= 0x80 && b3 <= 0xBF) ? 3 : 2;
- }
- return 1;
- }
- if (b1 >= 0xF1 && b1 <= 0xF3) {
- if (b2 >= 0x80 && b2 <= 0xBF) {
- if (source == sourceEnd)
- return 2;
-
- b3 = *source;
- return (b3 >= 0x80 && b3 <= 0xBF) ? 3 : 2;
- }
- return 1;
- }
- if (b1 == 0xF4) {
- if (b2 >= 0x80 && b2 <= 0x8F) {
- if (source == sourceEnd)
- return 2;
-
- b3 = *source;
- return (b3 >= 0x80 && b3 <= 0xBF) ? 3 : 2;
- }
- return 1;
- }
-
- assert((b1 >= 0x80 && b1 <= 0xC1) || b1 >= 0xF5);
- /*
- * There are no valid sequences that start with these bytes. Maximal subpart
- * is defined to have length 1 in these cases.
- */
- return 1;
-}
-
-/* --------------------------------------------------------------------- */
-
-/*
- * Exported function to return the total number of bytes in a codepoint
- * represented in UTF-8, given the value of the first byte.
- */
-unsigned getNumBytesForUTF8(UTF8 first) {
- return trailingBytesForUTF8[first] + 1;
-}
-
-/* --------------------------------------------------------------------- */
-
-/*
- * Exported function to return whether a UTF-8 string is legal or not.
- * This is not used here; it's just exported.
- */
-Boolean isLegalUTF8String(const UTF8 **source, const UTF8 *sourceEnd) {
- while (*source != sourceEnd) {
- int length = trailingBytesForUTF8[**source] + 1;
- if (length > sourceEnd - *source || !isLegalUTF8(*source, length))
- return false;
- *source += length;
- }
- return true;
-}
-
-/* --------------------------------------------------------------------- */
-
-ConversionResult ConvertUTF8toUTF16 (
- const UTF8** sourceStart, const UTF8* sourceEnd,
- UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) {
- ConversionResult result = conversionOK;
- const UTF8* source = *sourceStart;
- UTF16* target = *targetStart;
- while (source < sourceEnd) {
- UTF32 ch = 0;
- unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
- if (extraBytesToRead >= sourceEnd - source) {
- result = sourceExhausted; break;
- }
- /* Do this check whether lenient or strict */
- if (!isLegalUTF8(source, extraBytesToRead+1)) {
- result = sourceIllegal;
- break;
- }
- /*
- * The cases all fall through. See "Note A" below.
- */
- switch (extraBytesToRead) {
- case 5: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */
- case 4: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */
- case 3: ch += *source++; ch <<= 6;
- case 2: ch += *source++; ch <<= 6;
- case 1: ch += *source++; ch <<= 6;
- case 0: ch += *source++;
- }
- ch -= offsetsFromUTF8[extraBytesToRead];
-
- if (target >= targetEnd) {
- source -= (extraBytesToRead+1); /* Back up source pointer! */
- result = targetExhausted; break;
- }
- if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */
- /* UTF-16 surrogate values are illegal in UTF-32 */
- if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
- if (flags == strictConversion) {
- source -= (extraBytesToRead+1); /* return to the illegal value itself */
- result = sourceIllegal;
- break;
- } else {
- *target++ = UNI_REPLACEMENT_CHAR;
- }
- } else {
- *target++ = (UTF16)ch; /* normal case */
- }
- } else if (ch > UNI_MAX_UTF16) {
- if (flags == strictConversion) {
- result = sourceIllegal;
- source -= (extraBytesToRead+1); /* return to the start */
- break; /* Bail out; shouldn't continue */
- } else {
- *target++ = UNI_REPLACEMENT_CHAR;
- }
- } else {
- /* target is a character in range 0xFFFF - 0x10FFFF. */
- if (target + 1 >= targetEnd) {
- source -= (extraBytesToRead+1); /* Back up source pointer! */
- result = targetExhausted; break;
- }
- ch -= halfBase;
- *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START);
- *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START);
- }
- }
- *sourceStart = source;
- *targetStart = target;
- return result;
-}
-
-/* --------------------------------------------------------------------- */
-
-static ConversionResult ConvertUTF8toUTF32Impl(
- const UTF8** sourceStart, const UTF8* sourceEnd,
- UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags,
- Boolean InputIsPartial) {
- ConversionResult result = conversionOK;
- const UTF8* source = *sourceStart;
- UTF32* target = *targetStart;
- while (source < sourceEnd) {
- UTF32 ch = 0;
- unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
- if (extraBytesToRead >= sourceEnd - source) {
- if (flags == strictConversion || InputIsPartial) {
- result = sourceExhausted;
- break;
- } else {
- result = sourceIllegal;
-
- /*
- * Replace the maximal subpart of ill-formed sequence with
- * replacement character.
- */
- source += findMaximalSubpartOfIllFormedUTF8Sequence(source,
- sourceEnd);
- *target++ = UNI_REPLACEMENT_CHAR;
- continue;
- }
- }
- if (target >= targetEnd) {
- result = targetExhausted; break;
- }
-
- /* Do this check whether lenient or strict */
- if (!isLegalUTF8(source, extraBytesToRead+1)) {
- result = sourceIllegal;
- if (flags == strictConversion) {
- /* Abort conversion. */
- break;
- } else {
- /*
- * Replace the maximal subpart of ill-formed sequence with
- * replacement character.
- */
- source += findMaximalSubpartOfIllFormedUTF8Sequence(source,
- sourceEnd);
- *target++ = UNI_REPLACEMENT_CHAR;
- continue;
- }
- }
- /*
- * The cases all fall through. See "Note A" below.
- */
- switch (extraBytesToRead) {
- case 5: ch += *source++; ch <<= 6;
- case 4: ch += *source++; ch <<= 6;
- case 3: ch += *source++; ch <<= 6;
- case 2: ch += *source++; ch <<= 6;
- case 1: ch += *source++; ch <<= 6;
- case 0: ch += *source++;
- }
- ch -= offsetsFromUTF8[extraBytesToRead];
-
- if (ch <= UNI_MAX_LEGAL_UTF32) {
- /*
- * UTF-16 surrogate values are illegal in UTF-32, and anything
- * over Plane 17 (> 0x10FFFF) is illegal.
- */
- if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
- if (flags == strictConversion) {
- source -= (extraBytesToRead+1); /* return to the illegal value itself */
- result = sourceIllegal;
- break;
- } else {
- *target++ = UNI_REPLACEMENT_CHAR;
- }
- } else {
- *target++ = ch;
- }
- } else { /* i.e., ch > UNI_MAX_LEGAL_UTF32 */
- result = sourceIllegal;
- *target++ = UNI_REPLACEMENT_CHAR;
- }
- }
- *sourceStart = source;
- *targetStart = target;
- return result;
-}
-
-ConversionResult ConvertUTF8toUTF32Partial(const UTF8 **sourceStart,
- const UTF8 *sourceEnd,
- UTF32 **targetStart,
- UTF32 *targetEnd,
- ConversionFlags flags) {
- return ConvertUTF8toUTF32Impl(sourceStart, sourceEnd, targetStart, targetEnd,
- flags, /*InputIsPartial=*/true);
-}
-
-ConversionResult ConvertUTF8toUTF32(const UTF8 **sourceStart,
- const UTF8 *sourceEnd, UTF32 **targetStart,
- UTF32 *targetEnd, ConversionFlags flags) {
- return ConvertUTF8toUTF32Impl(sourceStart, sourceEnd, targetStart, targetEnd,
- flags, /*InputIsPartial=*/false);
-}
-
-/* ---------------------------------------------------------------------
-
- Note A.
- The fall-through switches in UTF-8 reading code save a
- temp variable, some decrements & conditionals. The switches
- are equivalent to the following loop:
- {
- int tmpBytesToRead = extraBytesToRead+1;
- do {
- ch += *source++;
- --tmpBytesToRead;
- if (tmpBytesToRead) ch <<= 6;
- } while (tmpBytesToRead > 0);
- }
- In UTF-8 writing code, the switches on "bytesToWrite" are
- similarly unrolled loops.
-
- --------------------------------------------------------------------- */
-
-#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
deleted file mode 100644
index db190d0..0000000
--- a/wpiutil/src/main/native/cpp/llvm/ConvertUTFWrapper.cpp
+++ /dev/null
@@ -1,123 +0,0 @@
-//===-- ConvertUTFWrapper.cpp - Wrap ConvertUTF.h with clang data types -----===
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-
-#include "wpi/ConvertUTF.h"
-#include "wpi/SmallVector.h"
-#include <string>
-#include <vector>
-
-namespace wpi {
-
-bool ConvertCodePointToUTF8(unsigned Source, char *&ResultPtr) {
- const UTF32 *SourceStart = &Source;
- const UTF32 *SourceEnd = SourceStart + 1;
- UTF8 *TargetStart = reinterpret_cast<UTF8 *>(ResultPtr);
- UTF8 *TargetEnd = TargetStart + 4;
- ConversionResult CR = ConvertUTF32toUTF8(&SourceStart, SourceEnd,
- &TargetStart, TargetEnd,
- strictConversion);
- if (CR != conversionOK)
- return false;
-
- ResultPtr = reinterpret_cast<char*>(TargetStart);
- return true;
-}
-
-bool hasUTF16ByteOrderMark(span<const char> S) {
- return (S.size() >= 2 &&
- ((S[0] == '\xff' && S[1] == '\xfe') ||
- (S[0] == '\xfe' && S[1] == '\xff')));
-}
-
-bool convertUTF16ToUTF8String(span<const UTF16> SrcUTF16,
- SmallVectorImpl<char> &DstUTF8) {
- assert(DstUTF8.empty());
-
- // Avoid OOB by returning early on empty input.
- if (SrcUTF16.empty())
- return true;
-
- const UTF16 *Src = SrcUTF16.begin();
- const UTF16 *SrcEnd = SrcUTF16.end();
-
- // Byteswap if necessary.
- std::vector<UTF16> ByteSwapped;
- if (Src[0] == UNI_UTF16_BYTE_ORDER_MARK_SWAPPED) {
- ByteSwapped.insert(ByteSwapped.end(), Src, SrcEnd);
- for (unsigned I = 0, E = ByteSwapped.size(); I != E; ++I)
- ByteSwapped[I] = (ByteSwapped[I] << 8) | (ByteSwapped[I] >> 8);
- Src = &ByteSwapped[0];
- SrcEnd = &ByteSwapped[ByteSwapped.size() - 1] + 1;
- }
-
- // Skip the BOM for conversion.
- if (Src[0] == UNI_UTF16_BYTE_ORDER_MARK_NATIVE)
- Src++;
-
- // Just allocate enough space up front. We'll shrink it later. Allocate
- // enough that we can fit a null terminator without reallocating.
- DstUTF8.resize(SrcUTF16.size() * UNI_MAX_UTF8_BYTES_PER_CODE_POINT + 1);
- UTF8 *Dst = reinterpret_cast<UTF8*>(&DstUTF8[0]);
- UTF8 *DstEnd = Dst + DstUTF8.size();
-
- ConversionResult CR =
- ConvertUTF16toUTF8(&Src, SrcEnd, &Dst, DstEnd, strictConversion);
- assert(CR != targetExhausted);
-
- if (CR != conversionOK) {
- DstUTF8.clear();
- return false;
- }
-
- DstUTF8.resize(reinterpret_cast<char*>(Dst) - &DstUTF8[0]);
- DstUTF8.push_back(0);
- DstUTF8.pop_back();
- return true;
-}
-
-bool convertUTF8ToUTF16String(std::string_view SrcUTF8,
- SmallVectorImpl<UTF16> &DstUTF16) {
- assert(DstUTF16.empty());
-
- // Avoid OOB by returning early on empty input.
- if (SrcUTF8.empty()) {
- DstUTF16.push_back(0);
- DstUTF16.pop_back();
- return true;
- }
-
- const UTF8 *Src = reinterpret_cast<const UTF8 *>(SrcUTF8.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
- // UTF-8 encoding. Allocate one extra byte for the null terminator though,
- // so that someone calling DstUTF16.data() gets a null terminated string.
- // We resize down later so we don't have to worry that this over allocates.
- DstUTF16.resize(SrcUTF8.size()+1);
- UTF16 *Dst = &DstUTF16[0];
- UTF16 *DstEnd = Dst + DstUTF16.size();
-
- ConversionResult CR =
- ConvertUTF8toUTF16(&Src, SrcEnd, &Dst, DstEnd, strictConversion);
- assert(CR != targetExhausted);
-
- if (CR != conversionOK) {
- DstUTF16.clear();
- return false;
- }
-
- DstUTF16.resize(Dst - &DstUTF16[0]);
- DstUTF16.push_back(0);
- DstUTF16.pop_back();
- return true;
-}
-
-} // end namespace wpi
-
diff --git a/wpiutil/src/main/native/cpp/llvm/ErrorHandling.cpp b/wpiutil/src/main/native/cpp/llvm/ErrorHandling.cpp
deleted file mode 100644
index 22ae636..0000000
--- a/wpiutil/src/main/native/cpp/llvm/ErrorHandling.cpp
+++ /dev/null
@@ -1,250 +0,0 @@
-//===- lib/Support/ErrorHandling.cpp - Callbacks for errors ---------------===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file defines an API used to indicate fatal error conditions. Non-fatal
-// errors (most of them) should be handled through LLVMContext.
-//
-//===----------------------------------------------------------------------===//
-
-#include "wpi/ErrorHandling.h"
-#include "wpi/SmallVector.h"
-#include "wpi/WindowsError.h"
-#include "fmt/format.h"
-#include <cassert>
-#include <cstdio>
-#include <cstdlib>
-#include <mutex>
-#include <new>
-
-#ifndef _WIN32
-#include <unistd.h>
-#endif
-
-#if defined(_MSC_VER)
-#include <io.h>
-#endif
-
-using namespace wpi;
-
-static fatal_error_handler_t ErrorHandler = nullptr;
-static void *ErrorHandlerUserData = nullptr;
-
-static fatal_error_handler_t BadAllocErrorHandler = nullptr;
-static void *BadAllocErrorHandlerUserData = nullptr;
-
-// Mutexes to synchronize installing error handlers and calling error handlers.
-// Do not use ManagedStatic, or that may allocate memory while attempting to
-// report an OOM.
-//
-// This usage of std::mutex has to be conditionalized behind ifdefs because
-// of this script:
-// compiler-rt/lib/sanitizer_common/symbolizer/scripts/build_symbolizer.sh
-// That script attempts to statically link the LLVM symbolizer library with the
-// STL and hide all of its symbols with 'opt -internalize'. To reduce size, it
-// cuts out the threading portions of the hermetic copy of libc++ that it
-// builds. We can remove these ifdefs if that script goes away.
-static std::mutex ErrorHandlerMutex;
-static std::mutex BadAllocErrorHandlerMutex;
-
-void wpi::install_fatal_error_handler(fatal_error_handler_t handler,
- void *user_data) {
- std::scoped_lock Lock(ErrorHandlerMutex);
- assert(!ErrorHandler && "Error handler already registered!\n");
- ErrorHandler = handler;
- ErrorHandlerUserData = user_data;
-}
-
-void wpi::remove_fatal_error_handler() {
- std::scoped_lock Lock(ErrorHandlerMutex);
- ErrorHandler = nullptr;
- ErrorHandlerUserData = nullptr;
-}
-
-void wpi::report_fatal_error(const char *Reason, bool GenCrashDiag) {
- report_fatal_error(std::string_view(Reason), GenCrashDiag);
-}
-
-void wpi::report_fatal_error(const std::string &Reason, bool GenCrashDiag) {
- report_fatal_error(std::string_view(Reason), GenCrashDiag);
-}
-
-void wpi::report_fatal_error(std::string_view Reason, bool GenCrashDiag) {
- wpi::fatal_error_handler_t handler = nullptr;
- void* handlerData = nullptr;
- {
- // Only acquire the mutex while reading the handler, so as not to invoke a
- // user-supplied callback under a lock.
- std::scoped_lock Lock(ErrorHandlerMutex);
- handler = ErrorHandler;
- handlerData = ErrorHandlerUserData;
- }
-
- if (handler) {
- handler(handlerData, std::string{Reason}, GenCrashDiag);
- } else {
- fmt::print(stderr, "LLVM ERROR: {}\n", Reason);
- }
-
- exit(1);
-}
-
-void wpi::install_bad_alloc_error_handler(fatal_error_handler_t handler,
- void *user_data) {
- std::scoped_lock Lock(BadAllocErrorHandlerMutex);
- assert(!ErrorHandler && "Bad alloc error handler already registered!\n");
- BadAllocErrorHandler = handler;
- BadAllocErrorHandlerUserData = user_data;
-}
-
-void wpi::remove_bad_alloc_error_handler() {
- std::scoped_lock Lock(BadAllocErrorHandlerMutex);
- BadAllocErrorHandler = nullptr;
- BadAllocErrorHandlerUserData = nullptr;
-}
-
-void wpi::report_bad_alloc_error(const char *Reason, bool GenCrashDiag) {
- fatal_error_handler_t Handler = nullptr;
- void *HandlerData = nullptr;
- {
- // Only acquire the mutex while reading the handler, so as not to invoke a
- // user-supplied callback under a lock.
- std::scoped_lock Lock(BadAllocErrorHandlerMutex);
- Handler = BadAllocErrorHandler;
- HandlerData = BadAllocErrorHandlerUserData;
- }
-
- if (Handler) {
- Handler(HandlerData, Reason, GenCrashDiag);
- wpi_unreachable("bad alloc handler should not return");
- }
-
- // Don't call the normal error handler. It may allocate memory. Directly write
- // an OOM to stderr and abort.
- char OOMMessage[] = "LLVM ERROR: out of memory\n";
-#ifdef _WIN32
- int written = ::_write(2, OOMMessage, strlen(OOMMessage));
-#else
- ssize_t written = ::write(2, OOMMessage, strlen(OOMMessage));
-#endif
- (void)written;
- abort();
-}
-
-// Causes crash on allocation failure. It is called prior to the handler set by
-// 'install_bad_alloc_error_handler'.
-static void out_of_memory_new_handler() {
- wpi::report_bad_alloc_error("Allocation failed");
-}
-
-// Installs new handler that causes crash on allocation failure. It does not
-// need to be called explicitly, if this file is linked to application, because
-// in this case it is called during construction of 'new_handler_installer'.
-void wpi::install_out_of_memory_new_handler() {
- static bool out_of_memory_new_handler_installed = false;
- if (!out_of_memory_new_handler_installed) {
- std::set_new_handler(out_of_memory_new_handler);
- out_of_memory_new_handler_installed = true;
- }
-}
-
-// Static object that causes installation of 'out_of_memory_new_handler' before
-// execution of 'main'.
-static class NewHandlerInstaller {
-public:
- NewHandlerInstaller() {
- install_out_of_memory_new_handler();
- }
-} new_handler_installer;
-
-void wpi::wpi_unreachable_internal(const char *msg, const char *file,
- unsigned line) {
- // This code intentionally doesn't call the ErrorHandler callback, because
- // wpi_unreachable is intended to be used to indicate "impossible"
- // situations, and not legitimate runtime errors.
- if (msg)
- fmt::print(stderr, "{}\n", msg);
- std::fputs("UNREACHABLE executed", stderr);
- if (file)
- 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,
- // so use the unreachable builtin to avoid a Clang self-host warning.
- LLVM_BUILTIN_UNREACHABLE;
-#endif
-}
-
-#ifdef _WIN32
-
-#include <system_error>
-#include <winerror.h>
-
-// I'd rather not double the line count of the following.
-#define MAP_ERR_TO_COND(x, y) \
- case x: \
- return std::make_error_code(std::errc::y)
-
-std::error_code wpi::mapWindowsError(unsigned EV) {
- switch (EV) {
- MAP_ERR_TO_COND(ERROR_ACCESS_DENIED, permission_denied);
- MAP_ERR_TO_COND(ERROR_ALREADY_EXISTS, file_exists);
- MAP_ERR_TO_COND(ERROR_BAD_UNIT, no_such_device);
- MAP_ERR_TO_COND(ERROR_BUFFER_OVERFLOW, filename_too_long);
- MAP_ERR_TO_COND(ERROR_BUSY, device_or_resource_busy);
- MAP_ERR_TO_COND(ERROR_BUSY_DRIVE, device_or_resource_busy);
- MAP_ERR_TO_COND(ERROR_CANNOT_MAKE, permission_denied);
- MAP_ERR_TO_COND(ERROR_CANTOPEN, io_error);
- MAP_ERR_TO_COND(ERROR_CANTREAD, io_error);
- MAP_ERR_TO_COND(ERROR_CANTWRITE, io_error);
- MAP_ERR_TO_COND(ERROR_CURRENT_DIRECTORY, permission_denied);
- MAP_ERR_TO_COND(ERROR_DEV_NOT_EXIST, no_such_device);
- MAP_ERR_TO_COND(ERROR_DEVICE_IN_USE, device_or_resource_busy);
- MAP_ERR_TO_COND(ERROR_DIR_NOT_EMPTY, directory_not_empty);
- MAP_ERR_TO_COND(ERROR_DIRECTORY, invalid_argument);
- MAP_ERR_TO_COND(ERROR_DISK_FULL, no_space_on_device);
- MAP_ERR_TO_COND(ERROR_FILE_EXISTS, file_exists);
- MAP_ERR_TO_COND(ERROR_FILE_NOT_FOUND, no_such_file_or_directory);
- MAP_ERR_TO_COND(ERROR_HANDLE_DISK_FULL, no_space_on_device);
- MAP_ERR_TO_COND(ERROR_INVALID_ACCESS, permission_denied);
- MAP_ERR_TO_COND(ERROR_INVALID_DRIVE, no_such_device);
- MAP_ERR_TO_COND(ERROR_INVALID_FUNCTION, function_not_supported);
- MAP_ERR_TO_COND(ERROR_INVALID_HANDLE, invalid_argument);
- MAP_ERR_TO_COND(ERROR_INVALID_NAME, invalid_argument);
- MAP_ERR_TO_COND(ERROR_LOCK_VIOLATION, no_lock_available);
- MAP_ERR_TO_COND(ERROR_LOCKED, no_lock_available);
- MAP_ERR_TO_COND(ERROR_NEGATIVE_SEEK, invalid_argument);
- MAP_ERR_TO_COND(ERROR_NOACCESS, permission_denied);
- MAP_ERR_TO_COND(ERROR_NOT_ENOUGH_MEMORY, not_enough_memory);
- MAP_ERR_TO_COND(ERROR_NOT_READY, resource_unavailable_try_again);
- MAP_ERR_TO_COND(ERROR_OPEN_FAILED, io_error);
- MAP_ERR_TO_COND(ERROR_OPEN_FILES, device_or_resource_busy);
- MAP_ERR_TO_COND(ERROR_OUTOFMEMORY, not_enough_memory);
- MAP_ERR_TO_COND(ERROR_PATH_NOT_FOUND, no_such_file_or_directory);
- MAP_ERR_TO_COND(ERROR_BAD_NETPATH, no_such_file_or_directory);
- MAP_ERR_TO_COND(ERROR_READ_FAULT, io_error);
- MAP_ERR_TO_COND(ERROR_RETRY, resource_unavailable_try_again);
- MAP_ERR_TO_COND(ERROR_SEEK, io_error);
- MAP_ERR_TO_COND(ERROR_SHARING_VIOLATION, permission_denied);
- MAP_ERR_TO_COND(ERROR_TOO_MANY_OPEN_FILES, too_many_files_open);
- MAP_ERR_TO_COND(ERROR_WRITE_FAULT, io_error);
- MAP_ERR_TO_COND(ERROR_WRITE_PROTECT, permission_denied);
- MAP_ERR_TO_COND(WSAEACCES, permission_denied);
- MAP_ERR_TO_COND(WSAEBADF, bad_file_descriptor);
- MAP_ERR_TO_COND(WSAEFAULT, bad_address);
- MAP_ERR_TO_COND(WSAEINTR, interrupted);
- MAP_ERR_TO_COND(WSAEINVAL, invalid_argument);
- MAP_ERR_TO_COND(WSAEMFILE, too_many_files_open);
- MAP_ERR_TO_COND(WSAENAMETOOLONG, filename_too_long);
- default:
- return std::error_code(EV, std::system_category());
- }
-}
-
-#endif
diff --git a/wpiutil/src/main/native/cpp/llvm/Hashing.cpp b/wpiutil/src/main/native/cpp/llvm/Hashing.cpp
deleted file mode 100644
index e916751..0000000
--- a/wpiutil/src/main/native/cpp/llvm/Hashing.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-//===-------------- lib/Support/Hashing.cpp -------------------------------===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file provides implementation bits for the LLVM common hashing
-// infrastructure. Documentation and most of the other information is in the
-// header file.
-//
-//===----------------------------------------------------------------------===//
-
-#include "wpi/Hashing.h"
-
-using namespace wpi;
-
-// Provide a definition and static initializer for the fixed seed. This
-// initializer should always be zero to ensure its value can never appear to be
-// non-zero, even during dynamic initialization.
-uint64_t wpi::hashing::detail::fixed_seed_override = 0;
-
-// Implement the function for forced setting of the fixed seed.
-// FIXME: Use atomic operations here so that there is no data race.
-void wpi::set_fixed_execution_hash_seed(uint64_t fixed_value) {
- hashing::detail::fixed_seed_override = fixed_value;
-}
diff --git a/wpiutil/src/main/native/cpp/llvm/ManagedStatic.cpp b/wpiutil/src/main/native/cpp/llvm/ManagedStatic.cpp
deleted file mode 100644
index a8c82bf..0000000
--- a/wpiutil/src/main/native/cpp/llvm/ManagedStatic.cpp
+++ /dev/null
@@ -1,88 +0,0 @@
-//===-- ManagedStatic.cpp - Static Global wrapper -------------------------===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file implements the ManagedStatic class and wpi_shutdown().
-//
-//===----------------------------------------------------------------------===//
-
-#include "wpi/ManagedStatic.h"
-#include "wpi/mutex.h"
-#include <cassert>
-#include <mutex>
-using namespace wpi;
-
-static const ManagedStaticBase *StaticList = nullptr;
-static wpi::mutex *ManagedStaticMutex = nullptr;
-static std::once_flag mutex_init_flag;
-
-static void initializeMutex() {
- ManagedStaticMutex = new wpi::mutex();
-}
-
-static wpi::mutex* getManagedStaticMutex() {
- std::call_once(mutex_init_flag, initializeMutex);
- return ManagedStaticMutex;
-}
-
-void ManagedStaticBase::RegisterManagedStatic(void* created,
- void (*Deleter)(void*)) const {
- std::scoped_lock Lock(*getManagedStaticMutex());
-
- if (!Ptr.load(std::memory_order_relaxed)) {
- void *Tmp = created;
-
- Ptr.store(Tmp, std::memory_order_release);
- DeleterFn = Deleter;
-
- // Add to list of managed statics.
- Next = StaticList;
- StaticList = this;
- }
-}
-
-void ManagedStaticBase::RegisterManagedStatic(void *(*Creator)(),
- void (*Deleter)(void*)) const {
- assert(Creator);
- std::scoped_lock Lock(*getManagedStaticMutex());
-
- if (!Ptr.load(std::memory_order_relaxed)) {
- void *Tmp = Creator();
-
- Ptr.store(Tmp, std::memory_order_release);
- DeleterFn = Deleter;
-
- // Add to list of managed statics.
- Next = StaticList;
- StaticList = this;
- }
-}
-
-void ManagedStaticBase::destroy() const {
- assert(DeleterFn && "ManagedStatic not initialized correctly!");
- assert(StaticList == this &&
- "Not destroyed in reverse order of construction?");
- // Unlink from list.
- StaticList = Next;
- Next = nullptr;
-
- // Destroy memory.
- DeleterFn(Ptr);
-
- // Cleanup.
- Ptr = nullptr;
- DeleterFn = nullptr;
-}
-
-/// wpi_shutdown - Deallocate and destroy all ManagedStatic variables.
-void wpi::wpi_shutdown() {
- std::scoped_lock Lock(*getManagedStaticMutex());
-
- while (StaticList)
- StaticList->destroy();
-}
diff --git a/wpiutil/src/main/native/cpp/llvm/SmallPtrSet.cpp b/wpiutil/src/main/native/cpp/llvm/SmallPtrSet.cpp
deleted file mode 100644
index 1b33f4c..0000000
--- a/wpiutil/src/main/native/cpp/llvm/SmallPtrSet.cpp
+++ /dev/null
@@ -1,271 +0,0 @@
-//===- llvm/ADT/SmallPtrSet.cpp - 'Normally small' pointer set ------------===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file implements the SmallPtrSet class. See SmallPtrSet.h for an
-// overview of the algorithm.
-//
-//===----------------------------------------------------------------------===//
-
-#include "wpi/SmallPtrSet.h"
-#include "wpi/DenseMapInfo.h"
-#include "wpi/MathExtras.h"
-#include "wpi/MemAlloc.h"
-#include "wpi/ErrorHandling.h"
-#include <algorithm>
-#include <cassert>
-#include <cstdlib>
-
-using namespace wpi;
-
-void SmallPtrSetImplBase::shrink_and_clear() {
- assert(!isSmall() && "Can't shrink a small set!");
- free(CurArray);
-
- // Reduce the number of buckets.
- unsigned Size = size();
- CurArraySize = Size > 16 ? 1 << (Log2_32_Ceil(Size) + 1) : 32;
- NumNonEmpty = NumTombstones = 0;
-
- // Install the new array. Clear all the buckets to empty.
- CurArray = (const void**)safe_malloc(sizeof(void*) * CurArraySize);
-
- memset(CurArray, -1, CurArraySize*sizeof(void*));
-}
-
-std::pair<const void *const *, bool>
-SmallPtrSetImplBase::insert_imp_big(const void *Ptr) {
- if (LLVM_UNLIKELY(size() * 4 >= CurArraySize * 3)) {
- // If more than 3/4 of the array is full, grow.
- Grow(CurArraySize < 64 ? 128 : CurArraySize * 2);
- } else if (LLVM_UNLIKELY(CurArraySize - NumNonEmpty < CurArraySize / 8)) {
- // If fewer of 1/8 of the array is empty (meaning that many are filled with
- // tombstones), rehash.
- Grow(CurArraySize);
- }
-
- // Okay, we know we have space. Find a hash bucket.
- const void **Bucket = const_cast<const void**>(FindBucketFor(Ptr));
- if (*Bucket == Ptr)
- return std::make_pair(Bucket, false); // Already inserted, good.
-
- // Otherwise, insert it!
- if (*Bucket == getTombstoneMarker())
- --NumTombstones;
- else
- ++NumNonEmpty; // Track density.
- *Bucket = Ptr;
- return std::make_pair(Bucket, true);
-}
-
-const void * const *SmallPtrSetImplBase::FindBucketFor(const void *Ptr) const {
- unsigned Bucket = DenseMapInfo<void *>::getHashValue(Ptr) & (CurArraySize-1);
- unsigned ArraySize = CurArraySize;
- unsigned ProbeAmt = 1;
- const void *const *Array = CurArray;
- const void *const *Tombstone = nullptr;
- while (true) {
- // If we found an empty bucket, the pointer doesn't exist in the set.
- // Return a tombstone if we've seen one so far, or the empty bucket if
- // not.
- if (LLVM_LIKELY(Array[Bucket] == getEmptyMarker()))
- return Tombstone ? Tombstone : Array+Bucket;
-
- // Found Ptr's bucket?
- if (LLVM_LIKELY(Array[Bucket] == Ptr))
- return Array+Bucket;
-
- // If this is a tombstone, remember it. If Ptr ends up not in the set, we
- // prefer to return it than something that would require more probing.
- if (Array[Bucket] == getTombstoneMarker() && !Tombstone)
- Tombstone = Array+Bucket; // Remember the first tombstone found.
-
- // It's a hash collision or a tombstone. Reprobe.
- Bucket = (Bucket + ProbeAmt++) & (ArraySize-1);
- }
-}
-
-/// Grow - Allocate a larger backing store for the buckets and move it over.
-///
-void SmallPtrSetImplBase::Grow(unsigned NewSize) {
- const void **OldBuckets = CurArray;
- const void **OldEnd = EndPointer();
- bool WasSmall = isSmall();
-
- // Install the new array. Clear all the buckets to empty.
- const void **NewBuckets = (const void**) safe_malloc(sizeof(void*) * NewSize);
-
- // Reset member only if memory was allocated successfully
- CurArray = NewBuckets;
- CurArraySize = NewSize;
- memset(CurArray, -1, NewSize*sizeof(void*));
-
- // Copy over all valid entries.
- for (const void **BucketPtr = OldBuckets; BucketPtr != OldEnd; ++BucketPtr) {
- // Copy over the element if it is valid.
- const void *Elt = *BucketPtr;
- if (Elt != getTombstoneMarker() && Elt != getEmptyMarker())
- *const_cast<void**>(FindBucketFor(Elt)) = const_cast<void*>(Elt);
- }
-
- if (!WasSmall)
- free(OldBuckets);
- NumNonEmpty -= NumTombstones;
- NumTombstones = 0;
-}
-
-SmallPtrSetImplBase::SmallPtrSetImplBase(const void **SmallStorage,
- const SmallPtrSetImplBase &that) {
- SmallArray = SmallStorage;
-
- // If we're becoming small, prepare to insert into our stack space
- if (that.isSmall()) {
- CurArray = SmallArray;
- // Otherwise, allocate new heap space (unless we were the same size)
- } else {
- CurArray = (const void**)safe_malloc(sizeof(void*) * that.CurArraySize);
- }
-
- // Copy over the that array.
- CopyHelper(that);
-}
-
-SmallPtrSetImplBase::SmallPtrSetImplBase(const void **SmallStorage,
- unsigned SmallSize,
- SmallPtrSetImplBase &&that) {
- SmallArray = SmallStorage;
- MoveHelper(SmallSize, std::move(that));
-}
-
-void SmallPtrSetImplBase::CopyFrom(const SmallPtrSetImplBase &RHS) {
- assert(&RHS != this && "Self-copy should be handled by the caller.");
-
- if (isSmall() && RHS.isSmall())
- assert(CurArraySize == RHS.CurArraySize &&
- "Cannot assign sets with different small sizes");
-
- // If we're becoming small, prepare to insert into our stack space
- if (RHS.isSmall()) {
- if (!isSmall())
- free(CurArray);
- CurArray = SmallArray;
- // Otherwise, allocate new heap space (unless we were the same size)
- } else if (CurArraySize != RHS.CurArraySize) {
- if (isSmall())
- CurArray = (const void**)safe_malloc(sizeof(void*) * RHS.CurArraySize);
- else {
- const void **T = (const void**)safe_realloc(CurArray,
- sizeof(void*) * RHS.CurArraySize);
- CurArray = T;
- }
- }
-
- CopyHelper(RHS);
-}
-
-void SmallPtrSetImplBase::CopyHelper(const SmallPtrSetImplBase &RHS) {
- // Copy over the new array size
- CurArraySize = RHS.CurArraySize;
-
- // Copy over the contents from the other set
- std::copy(RHS.CurArray, RHS.EndPointer(), CurArray);
-
- NumNonEmpty = RHS.NumNonEmpty;
- NumTombstones = RHS.NumTombstones;
-}
-
-void SmallPtrSetImplBase::MoveFrom(unsigned SmallSize,
- SmallPtrSetImplBase &&RHS) {
- if (!isSmall())
- free(CurArray);
- MoveHelper(SmallSize, std::move(RHS));
-}
-
-void SmallPtrSetImplBase::MoveHelper(unsigned SmallSize,
- SmallPtrSetImplBase &&RHS) {
- assert(&RHS != this && "Self-move should be handled by the caller.");
-
- if (RHS.isSmall()) {
- // Copy a small RHS rather than moving.
- CurArray = SmallArray;
- std::copy(RHS.CurArray, RHS.CurArray + RHS.NumNonEmpty, CurArray);
- } else {
- CurArray = RHS.CurArray;
- RHS.CurArray = RHS.SmallArray;
- }
-
- // Copy the rest of the trivial members.
- CurArraySize = RHS.CurArraySize;
- NumNonEmpty = RHS.NumNonEmpty;
- NumTombstones = RHS.NumTombstones;
-
- // Make the RHS small and empty.
- RHS.CurArraySize = SmallSize;
- assert(RHS.CurArray == RHS.SmallArray);
- RHS.NumNonEmpty = 0;
- RHS.NumTombstones = 0;
-}
-
-void SmallPtrSetImplBase::swap(SmallPtrSetImplBase &RHS) {
- if (this == &RHS) return;
-
- // We can only avoid copying elements if neither set is small.
- if (!this->isSmall() && !RHS.isSmall()) {
- std::swap(this->CurArray, RHS.CurArray);
- std::swap(this->CurArraySize, RHS.CurArraySize);
- std::swap(this->NumNonEmpty, RHS.NumNonEmpty);
- std::swap(this->NumTombstones, RHS.NumTombstones);
- return;
- }
-
- // FIXME: From here on we assume that both sets have the same small size.
-
- // If only RHS is small, copy the small elements into LHS and move the pointer
- // from LHS to RHS.
- if (!this->isSmall() && RHS.isSmall()) {
- assert(RHS.CurArray == RHS.SmallArray);
- std::copy(RHS.CurArray, RHS.CurArray + RHS.NumNonEmpty, this->SmallArray);
- std::swap(RHS.CurArraySize, this->CurArraySize);
- std::swap(this->NumNonEmpty, RHS.NumNonEmpty);
- std::swap(this->NumTombstones, RHS.NumTombstones);
- RHS.CurArray = this->CurArray;
- this->CurArray = this->SmallArray;
- return;
- }
-
- // If only LHS is small, copy the small elements into RHS and move the pointer
- // from RHS to LHS.
- if (this->isSmall() && !RHS.isSmall()) {
- assert(this->CurArray == this->SmallArray);
- std::copy(this->CurArray, this->CurArray + this->NumNonEmpty,
- RHS.SmallArray);
- std::swap(RHS.CurArraySize, this->CurArraySize);
- std::swap(RHS.NumNonEmpty, this->NumNonEmpty);
- std::swap(RHS.NumTombstones, this->NumTombstones);
- this->CurArray = RHS.CurArray;
- RHS.CurArray = RHS.SmallArray;
- return;
- }
-
- // Both a small, just swap the small elements.
- assert(this->isSmall() && RHS.isSmall());
- unsigned MinNonEmpty = std::min(this->NumNonEmpty, RHS.NumNonEmpty);
- std::swap_ranges(this->SmallArray, this->SmallArray + MinNonEmpty,
- RHS.SmallArray);
- if (this->NumNonEmpty > MinNonEmpty) {
- std::copy(this->SmallArray + MinNonEmpty,
- this->SmallArray + this->NumNonEmpty,
- RHS.SmallArray + MinNonEmpty);
- } else {
- std::copy(RHS.SmallArray + MinNonEmpty, RHS.SmallArray + RHS.NumNonEmpty,
- this->SmallArray + MinNonEmpty);
- }
- assert(this->CurArraySize == RHS.CurArraySize);
- std::swap(this->NumNonEmpty, RHS.NumNonEmpty);
- std::swap(this->NumTombstones, RHS.NumTombstones);
-}
diff --git a/wpiutil/src/main/native/cpp/llvm/SmallVector.cpp b/wpiutil/src/main/native/cpp/llvm/SmallVector.cpp
deleted file mode 100644
index 974fec9..0000000
--- a/wpiutil/src/main/native/cpp/llvm/SmallVector.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-//===- llvm/ADT/SmallVector.cpp - 'Normally small' vectors ----------------===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file implements the SmallVector class.
-//
-//===----------------------------------------------------------------------===//
-
-#include "wpi/SmallVector.h"
-#include "wpi/MemAlloc.h"
-using namespace wpi;
-
-/// grow_pod - This is an implementation of the grow() method which only works
-/// on POD-like datatypes and is out of line to reduce code duplication.
-void SmallVectorBase::grow_pod(void *FirstEl, size_t MinCapacity,
- size_t TSize) {
- // Ensure we can fit the new capacity in 32 bits.
- if (MinCapacity > UINT32_MAX)
- report_bad_alloc_error("SmallVector capacity overflow during allocation");
-
- size_t NewCapacity = 2 * capacity() + 1; // Always grow.
- NewCapacity =
- std::min(std::max(NewCapacity, MinCapacity), size_t(UINT32_MAX));
-
- void *NewElts;
- if (BeginX == FirstEl) {
- NewElts = safe_malloc(NewCapacity * TSize);
-
- // Copy the elements over. No need to run dtors on PODs.
- memcpy(NewElts, this->BeginX, size() * TSize);
- } else {
- // If this wasn't grown from the inline copy, grow the allocated space.
- NewElts = safe_realloc(this->BeginX, NewCapacity * TSize);
- }
-
- this->BeginX = NewElts;
- this->Capacity = NewCapacity;
-}
diff --git a/wpiutil/src/main/native/cpp/llvm/StringMap.cpp b/wpiutil/src/main/native/cpp/llvm/StringMap.cpp
deleted file mode 100644
index 768801d..0000000
--- a/wpiutil/src/main/native/cpp/llvm/StringMap.cpp
+++ /dev/null
@@ -1,276 +0,0 @@
-//===--- StringMap.cpp - String Hash table map implementation -------------===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file implements the StringMap class.
-//
-//===----------------------------------------------------------------------===//
-
-#include "wpi/StringMap.h"
-#include "wpi/StringExtras.h"
-#include "wpi/Compiler.h"
-#include "wpi/MathExtras.h"
-#include <cassert>
-
-using namespace wpi;
-
-/// 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) {
- // Ensure that "NumEntries * 4 < NumBuckets * 3"
- if (NumEntries == 0)
- return 0;
- // +1 is required because of the strict equality.
- // For example if NumEntries is 48, we need to return 401.
- return NextPowerOf2(NumEntries * 4 / 3 + 1);
-}
-
-StringMapImpl::StringMapImpl(unsigned InitSize, unsigned itemSize) {
- ItemSize = itemSize;
-
- // If a size is specified, initialize the table with that many buckets.
- if (InitSize) {
- // The table will grow when the number of entries reach 3/4 of the number of
- // buckets. To guarantee that "InitSize" number of entries can be inserted
- // in the table without growing, we allocate just what is needed here.
- init(getMinBucketToReserveForEntries(InitSize));
- return;
- }
-
- // Otherwise, initialize it with zero buckets to avoid the allocation.
- TheTable = nullptr;
- NumBuckets = 0;
- NumItems = 0;
- NumTombstones = 0;
-}
-
-void StringMapImpl::init(unsigned InitSize) {
- assert((InitSize & (InitSize-1)) == 0 &&
- "Init Size must be a power of 2 or zero!");
-
- unsigned NewNumBuckets = InitSize ? InitSize : 16;
- NumItems = 0;
- NumTombstones = 0;
-
- TheTable = static_cast<StringMapEntryBase **>(
- safe_calloc(NewNumBuckets+1,
- sizeof(StringMapEntryBase **) + sizeof(unsigned)));
-
- // Set the member only if TheTable was successfully allocated
- NumBuckets = NewNumBuckets;
-
- // Allocate one extra bucket, set it to look filled so the iterators stop at
- // end.
- TheTable[NumBuckets] = (StringMapEntryBase*)2;
-}
-
-/// LookupBucketFor - Look up the bucket that the specified string should end
-/// up in. If it already exists as a key in the map, the Item pointer for the
-/// specified bucket will be non-null. Otherwise, it will be null. In either
-/// case, the FullHashValue field of the bucket will be set to the hash value
-/// of the string.
-unsigned StringMapImpl::LookupBucketFor(std::string_view Name) {
- unsigned HTSize = NumBuckets;
- if (HTSize == 0) { // Hash table unallocated so far?
- init(16);
- HTSize = NumBuckets;
- }
- unsigned FullHashValue = HashString(Name);
- unsigned BucketNo = FullHashValue & (HTSize-1);
- unsigned *HashTable = (unsigned *)(TheTable + NumBuckets + 1);
-
- unsigned ProbeAmt = 1;
- int FirstTombstone = -1;
- while (true) {
- StringMapEntryBase *BucketItem = TheTable[BucketNo];
- // If we found an empty bucket, this key isn't in the table yet, return it.
- if (LLVM_LIKELY(!BucketItem)) {
- // If we found a tombstone, we want to reuse the tombstone instead of an
- // empty bucket. This reduces probing.
- if (FirstTombstone != -1) {
- HashTable[FirstTombstone] = FullHashValue;
- return FirstTombstone;
- }
-
- HashTable[BucketNo] = FullHashValue;
- return BucketNo;
- }
-
- if (BucketItem == getTombstoneVal()) {
- // Skip over tombstones. However, remember the first one we see.
- if (FirstTombstone == -1) FirstTombstone = BucketNo;
- } else if (LLVM_LIKELY(HashTable[BucketNo] == FullHashValue)) {
- // If the full hash value matches, check deeply for a match. The common
- // case here is that we are only looking at the buckets (for item info
- // being non-null and for the full hash value) not at the items. This
- // is important for cache locality.
-
- // Do the comparison like this because Name isn't necessarily
- // null-terminated!
- char *ItemStr = (char*)BucketItem+ItemSize;
- if (Name == std::string_view(ItemStr, BucketItem->getKeyLength())) {
- // We found a match!
- return BucketNo;
- }
- }
-
- // Okay, we didn't find the item. Probe to the next bucket.
- BucketNo = (BucketNo+ProbeAmt) & (HTSize-1);
-
- // Use quadratic probing, it has fewer clumping artifacts than linear
- // probing and has good cache behavior in the common case.
- ++ProbeAmt;
- }
-}
-
-/// FindKey - Look up the bucket that contains the specified key. If it exists
-/// in the map, return the bucket number of the key. Otherwise return -1.
-/// This does not modify the map.
-int StringMapImpl::FindKey(std::string_view Key) const {
- unsigned HTSize = NumBuckets;
- if (HTSize == 0) return -1; // Really empty table?
- unsigned FullHashValue = HashString(Key);
- unsigned BucketNo = FullHashValue & (HTSize-1);
- unsigned *HashTable = (unsigned *)(TheTable + NumBuckets + 1);
-
- unsigned ProbeAmt = 1;
- while (true) {
- StringMapEntryBase *BucketItem = TheTable[BucketNo];
- // If we found an empty bucket, this key isn't in the table yet, return.
- if (LLVM_LIKELY(!BucketItem))
- return -1;
-
- if (BucketItem == getTombstoneVal()) {
- // Ignore tombstones.
- } else if (LLVM_LIKELY(HashTable[BucketNo] == FullHashValue)) {
- // If the full hash value matches, check deeply for a match. The common
- // case here is that we are only looking at the buckets (for item info
- // being non-null and for the full hash value) not at the items. This
- // is important for cache locality.
-
- // Do the comparison like this because NameStart isn't necessarily
- // null-terminated!
- char *ItemStr = (char*)BucketItem+ItemSize;
- if (Key == std::string_view(ItemStr, BucketItem->getKeyLength())) {
- // We found a match!
- return BucketNo;
- }
- }
-
- // Okay, we didn't find the item. Probe to the next bucket.
- BucketNo = (BucketNo+ProbeAmt) & (HTSize-1);
-
- // Use quadratic probing, it has fewer clumping artifacts than linear
- // probing and has good cache behavior in the common case.
- ++ProbeAmt;
- }
-}
-
-/// RemoveKey - Remove the specified StringMapEntry from the table, but do not
-/// delete it. This aborts if the value isn't in the table.
-void StringMapImpl::RemoveKey(StringMapEntryBase *V) {
- const char *VStr = (char*)V + ItemSize;
- StringMapEntryBase *V2 = RemoveKey(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(std::string_view Key) {
- int Bucket = FindKey(Key);
- if (Bucket == -1) return nullptr;
-
- StringMapEntryBase *Result = TheTable[Bucket];
- TheTable[Bucket] = getTombstoneVal();
- --NumItems;
- ++NumTombstones;
- assert(NumItems + NumTombstones <= NumBuckets);
-
- return Result;
-}
-
-/// RehashTable - Grow the table, redistributing values into the buckets with
-/// the appropriate mod-of-hashtable-size.
-unsigned StringMapImpl::RehashTable(unsigned BucketNo) {
- unsigned NewSize;
- unsigned *HashTable = (unsigned *)(TheTable + NumBuckets + 1);
-
- // If the hash table is now more than 3/4 full, or if fewer than 1/8 of
- // the buckets are empty (meaning that many are filled with tombstones),
- // grow/rehash the table.
- if (LLVM_UNLIKELY(NumItems * 4 > NumBuckets * 3)) {
- NewSize = NumBuckets*2;
- } else if (LLVM_UNLIKELY(NumBuckets - (NumItems + NumTombstones) <=
- NumBuckets / 8)) {
- NewSize = NumBuckets;
- } else {
- return BucketNo;
- }
-
- unsigned NewBucketNo = BucketNo;
- // Allocate one extra bucket which will always be non-empty. This allows the
- // iterators to stop at end.
- auto NewTableArray = static_cast<StringMapEntryBase **>(
- safe_calloc(NewSize+1, sizeof(StringMapEntryBase *) + sizeof(unsigned)));
-
- unsigned *NewHashArray = (unsigned *)(NewTableArray + NewSize + 1);
- NewTableArray[NewSize] = (StringMapEntryBase*)2;
-
- // Rehash all the items into their new buckets. Luckily :) we already have
- // the hash values available, so we don't have to rehash any strings.
- for (unsigned I = 0, E = NumBuckets; I != E; ++I) {
- StringMapEntryBase *Bucket = TheTable[I];
- if (Bucket && Bucket != getTombstoneVal()) {
- // Fast case, bucket available.
- unsigned FullHash = HashTable[I];
- unsigned NewBucket = FullHash & (NewSize-1);
- if (!NewTableArray[NewBucket]) {
- NewTableArray[FullHash & (NewSize-1)] = Bucket;
- NewHashArray[FullHash & (NewSize-1)] = FullHash;
- if (I == BucketNo)
- NewBucketNo = NewBucket;
- continue;
- }
-
- // Otherwise probe for a spot.
- unsigned ProbeSize = 1;
- do {
- NewBucket = (NewBucket + ProbeSize++) & (NewSize-1);
- } while (NewTableArray[NewBucket]);
-
- // Finally found a slot. Fill it in.
- NewTableArray[NewBucket] = Bucket;
- NewHashArray[NewBucket] = FullHash;
- if (I == BucketNo)
- NewBucketNo = NewBucket;
- }
- }
-
- free(TheTable);
-
- TheTable = NewTableArray;
- NumBuckets = NewSize;
- NumTombstones = 0;
- return NewBucketNo;
-}
diff --git a/wpiutil/src/main/native/cpp/llvm/Windows/WindowsSupport.h b/wpiutil/src/main/native/cpp/llvm/Windows/WindowsSupport.h
deleted file mode 100644
index 7307337..0000000
--- a/wpiutil/src/main/native/cpp/llvm/Windows/WindowsSupport.h
+++ /dev/null
@@ -1,245 +0,0 @@
-//===- WindowsSupport.h - Common Windows Include File -----------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file defines things specific to Windows implementations. In addition to
-// providing some helpers for working with win32 APIs, this header wraps
-// <windows.h> with some portability macros. Always include WindowsSupport.h
-// instead of including <windows.h> directly.
-//
-//===----------------------------------------------------------------------===//
-
-//===----------------------------------------------------------------------===//
-//=== WARNING: Implementation here must contain only generic Win32 code that
-//=== is guaranteed to work on *all* Win32 variants.
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_SUPPORT_WINDOWSSUPPORT_H
-#define LLVM_SUPPORT_WINDOWSSUPPORT_H
-
-// mingw-w64 tends to define it as 0x0502 in its headers.
-#undef _WIN32_WINNT
-#undef _WIN32_IE
-
-// Require at least Windows 7 API.
-#define _WIN32_WINNT 0x0601
-#define _WIN32_IE 0x0800 // MinGW at it again. FIXME: verify if still needed.
-#define WIN32_LEAN_AND_MEAN
-#ifndef NOMINMAX
-#define NOMINMAX
-#endif
-
-#include "wpi/SmallVector.h"
-#include "wpi/StringExtras.h"
-#include "wpi/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>
-#undef WIN32_NO_STATUS
-#include <winternl.h>
-#include <ntstatus.h>
-
-namespace wpi {
-
-/// Returns the Windows version as Major.Minor.0.BuildNumber. Uses
-/// RtlGetVersion or GetVersionEx under the hood depending on what is available.
-/// GetVersionEx is deprecated, but this API exposes the build number which can
-/// be useful for working around certain kernel bugs.
-inline wpi::VersionTuple GetWindowsOSVersion() {
- typedef NTSTATUS(WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW);
- HMODULE hMod = ::GetModuleHandleW(L"ntdll.dll");
- if (hMod) {
- auto getVer = (RtlGetVersionPtr)::GetProcAddress(hMod, "RtlGetVersion");
- if (getVer) {
- RTL_OSVERSIONINFOEXW info{};
- info.dwOSVersionInfoSize = sizeof(info);
- if (getVer((PRTL_OSVERSIONINFOW)&info) == ((NTSTATUS)0x00000000L)) {
- return wpi::VersionTuple(info.dwMajorVersion, info.dwMinorVersion, 0,
- info.dwBuildNumber);
- }
- }
- }
- return wpi::VersionTuple(0, 0, 0, 0);
-}
-
-/// Determines if the program is running on Windows 8 or newer. This
-/// reimplements one of the helpers in the Windows 8.1 SDK, which are intended
-/// to supercede raw calls to GetVersionEx. Old SDKs, Cygwin, and MinGW don't
-/// yet have VersionHelpers.h, so we have our own helper.
-inline bool RunningWindows8OrGreater() {
- // Windows 8 is version 6.2, service pack 0.
- return GetWindowsOSVersion() >= wpi::VersionTuple(6, 2, 0, 0);
-}
-
-inline bool MakeErrMsg(std::string *ErrMsg, const std::string &prefix) {
- if (!ErrMsg)
- return true;
- char *buffer = NULL;
- DWORD LastError = GetLastError();
- DWORD R = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |
- FORMAT_MESSAGE_FROM_SYSTEM |
- FORMAT_MESSAGE_MAX_WIDTH_MASK,
- NULL, LastError, 0, (LPSTR)&buffer, 1, NULL);
- if (R)
- *ErrMsg = prefix + ": " + buffer;
- else
- *ErrMsg = prefix + ": Unknown error";
- *ErrMsg += " (0x" + wpi::utohexstr(LastError) + ")";
-
- LocalFree(buffer);
- return R != 0;
-}
-
-template <typename HandleTraits>
-class ScopedHandle {
- typedef typename HandleTraits::handle_type handle_type;
- handle_type Handle;
-
- ScopedHandle(const ScopedHandle &other) = delete;
- void operator=(const ScopedHandle &other) = delete;
-public:
- ScopedHandle()
- : Handle(HandleTraits::GetInvalid()) {}
-
- explicit ScopedHandle(handle_type h)
- : Handle(h) {}
-
- ~ScopedHandle() {
- if (HandleTraits::IsValid(Handle))
- HandleTraits::Close(Handle);
- }
-
- handle_type take() {
- handle_type t = Handle;
- Handle = HandleTraits::GetInvalid();
- return t;
- }
-
- ScopedHandle &operator=(handle_type h) {
- if (HandleTraits::IsValid(Handle))
- HandleTraits::Close(Handle);
- Handle = h;
- return *this;
- }
-
- // True if Handle is valid.
- explicit operator bool() const {
- return HandleTraits::IsValid(Handle) ? true : false;
- }
-
- operator handle_type() const {
- return Handle;
- }
-};
-
-struct CommonHandleTraits {
- typedef HANDLE handle_type;
-
- static handle_type GetInvalid() {
- return INVALID_HANDLE_VALUE;
- }
-
- static void Close(handle_type h) {
- ::CloseHandle(h);
- }
-
- static bool IsValid(handle_type h) {
- return h != GetInvalid();
- }
-};
-
-struct JobHandleTraits : CommonHandleTraits {
- static handle_type GetInvalid() {
- return NULL;
- }
-};
-
-struct RegTraits : CommonHandleTraits {
- typedef HKEY handle_type;
-
- static handle_type GetInvalid() {
- return NULL;
- }
-
- static void Close(handle_type h) {
- ::RegCloseKey(h);
- }
-
- static bool IsValid(handle_type h) {
- return h != GetInvalid();
- }
-};
-
-struct FindHandleTraits : CommonHandleTraits {
- static void Close(handle_type h) {
- ::FindClose(h);
- }
-};
-
-struct FileHandleTraits : CommonHandleTraits {};
-
-typedef ScopedHandle<CommonHandleTraits> ScopedCommonHandle;
-typedef ScopedHandle<FileHandleTraits> ScopedFileHandle;
-typedef ScopedHandle<RegTraits> ScopedRegHandle;
-typedef ScopedHandle<FindHandleTraits> ScopedFindHandle;
-typedef ScopedHandle<JobHandleTraits> ScopedJobHandle;
-
-template <class T>
-class SmallVectorImpl;
-
-template <class T>
-typename SmallVectorImpl<T>::const_pointer
-c_str(SmallVectorImpl<T> &str) {
- str.push_back(0);
- str.pop_back();
- return str.data();
-}
-
-namespace sys {
-
-inline std::chrono::nanoseconds toDuration(FILETIME Time) {
- ULARGE_INTEGER TimeInteger;
- TimeInteger.LowPart = Time.dwLowDateTime;
- TimeInteger.HighPart = Time.dwHighDateTime;
-
- // FILETIME's are # of 100 nanosecond ticks (1/10th of a microsecond)
- return std::chrono::nanoseconds(100 * TimeInteger.QuadPart);
-}
-
-inline TimePoint<> toTimePoint(FILETIME Time) {
- ULARGE_INTEGER TimeInteger;
- TimeInteger.LowPart = Time.dwLowDateTime;
- TimeInteger.HighPart = Time.dwHighDateTime;
-
- // Adjust for different epoch
- TimeInteger.QuadPart -= 11644473600ll * 10000000;
-
- // FILETIME's are # of 100 nanosecond ticks (1/10th of a microsecond)
- return TimePoint<>(std::chrono::nanoseconds(100 * TimeInteger.QuadPart));
-}
-
-inline FILETIME toFILETIME(TimePoint<> TP) {
- ULARGE_INTEGER TimeInteger;
- TimeInteger.QuadPart = TP.time_since_epoch().count() / 100;
- TimeInteger.QuadPart += 11644473600ll * 10000000;
-
- FILETIME Time;
- Time.dwLowDateTime = TimeInteger.LowPart;
- Time.dwHighDateTime = TimeInteger.HighPart;
- return Time;
-}
-
-} // end namespace sys
-} // end namespace wpi.
-
-#endif
diff --git a/wpiutil/src/main/native/cpp/llvm/raw_os_ostream.cpp b/wpiutil/src/main/native/cpp/llvm/raw_os_ostream.cpp
deleted file mode 100644
index 1fb6c51..0000000
--- a/wpiutil/src/main/native/cpp/llvm/raw_os_ostream.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
-//===--- raw_os_ostream.cpp - Implement the raw_os_ostream class ----------===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This implements support adapting raw_ostream to std::ostream.
-//
-//===----------------------------------------------------------------------===//
-
-#include "wpi/raw_os_ostream.h"
-#include <ostream>
-using namespace wpi;
-
-//===----------------------------------------------------------------------===//
-// raw_os_ostream
-//===----------------------------------------------------------------------===//
-
-raw_os_ostream::~raw_os_ostream() {
- flush();
-}
-
-void raw_os_ostream::write_impl(const char *Ptr, size_t Size) {
- OS.write(Ptr, Size);
-}
-
-uint64_t raw_os_ostream::current_pos() const { return OS.tellp(); }
diff --git a/wpiutil/src/main/native/cpp/llvm/raw_ostream.cpp b/wpiutil/src/main/native/cpp/llvm/raw_ostream.cpp
deleted file mode 100644
index 622e0bf..0000000
--- a/wpiutil/src/main/native/cpp/llvm/raw_ostream.cpp
+++ /dev/null
@@ -1,705 +0,0 @@
-//===--- raw_ostream.cpp - Implement the raw_ostream classes --------------===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This implements support for bulk buffered stream output.
-//
-//===----------------------------------------------------------------------===//
-
-#ifdef _WIN32
-#define _CRT_NONSTDC_NO_WARNINGS
-#endif
-
-#include "wpi/raw_ostream.h"
-#include "wpi/SmallString.h"
-#include "wpi/SmallVector.h"
-#include "wpi/StringExtras.h"
-#include "wpi/Compiler.h"
-#include "wpi/ErrorHandling.h"
-#include "wpi/MathExtras.h"
-#include "wpi/WindowsError.h"
-#include "wpi/fs.h"
-#include <algorithm>
-#include <cctype>
-#include <cerrno>
-#include <cstdio>
-#include <iterator>
-#include <sys/stat.h>
-#include <system_error>
-
-// <fcntl.h> may provide O_BINARY.
-#include <fcntl.h>
-
-#ifndef _WIN32
-#include <unistd.h>
-#include <sys/uio.h>
-#endif
-
-#if defined(__CYGWIN__)
-#include <io.h>
-#endif
-
-#if defined(_MSC_VER)
-#include <io.h>
-#ifndef STDIN_FILENO
-# define STDIN_FILENO 0
-#endif
-#ifndef STDOUT_FILENO
-# define STDOUT_FILENO 1
-#endif
-#ifndef STDERR_FILENO
-# define STDERR_FILENO 2
-#endif
-#endif
-
-#ifdef _WIN32
-#include "wpi/ConvertUTF.h"
-#include "Windows/WindowsSupport.h"
-#endif
-
-using namespace wpi;
-
-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.
- assert(OutBufCur == OutBufStart &&
- "raw_ostream destructor called with non-empty buffer!");
-
- if (BufferMode == InternalBuffer)
- delete [] OutBufStart;
-}
-
-// An out of line virtual method to provide a home for the class vtable.
-void raw_ostream::handle() {}
-
-size_t raw_ostream::preferred_buffer_size() const {
- // BUFSIZ is intended to be a reasonable default.
- return BUFSIZ;
-}
-
-void raw_ostream::SetBuffered() {
- // Ask the subclass to determine an appropriate buffer size.
- if (size_t Size = preferred_buffer_size())
- SetBufferSize(Size);
- else
- // It may return 0, meaning this stream should be unbuffered.
- SetUnbuffered();
-}
-
-void raw_ostream::SetBufferAndMode(char *BufferStart, size_t Size,
- BufferKind Mode) {
- assert(((Mode == Unbuffered && !BufferStart && Size == 0) ||
- (Mode != Unbuffered && BufferStart && Size != 0)) &&
- "stream must be unbuffered or have at least one byte");
- // Make sure the current buffer is free of content (we can't flush here; the
- // child buffer management logic will be in write_impl).
- assert(GetNumBytesInBuffer() == 0 && "Current buffer is non-empty!");
-
- if (BufferMode == InternalBuffer)
- delete [] OutBufStart;
- OutBufStart = BufferStart;
- OutBufEnd = OutBufStart+Size;
- OutBufCur = OutBufStart;
- BufferMode = Mode;
-
- assert(OutBufStart <= OutBufEnd && "Invalid size!");
-}
-
-raw_ostream &raw_ostream::write_escaped(std::string_view Str,
- bool UseHexEscapes) {
- for (unsigned char c : Str) {
- switch (c) {
- case '\\':
- *this << '\\' << '\\';
- break;
- case '\t':
- *this << '\\' << 't';
- break;
- case '\n':
- *this << '\\' << 'n';
- break;
- case '"':
- *this << '\\' << '"';
- break;
- default:
- if (isPrint(c)) {
- *this << c;
- break;
- }
-
- // Write out the escaped representation.
- if (UseHexEscapes) {
- *this << '\\' << 'x';
- *this << hexdigit((c >> 4 & 0xF));
- *this << hexdigit((c >> 0) & 0xF);
- } else {
- // Always use a full 3-character octal escape.
- *this << '\\';
- *this << char('0' + ((c >> 6) & 7));
- *this << char('0' + ((c >> 3) & 7));
- *this << char('0' + ((c >> 0) & 7));
- }
- }
- }
-
- return *this;
-}
-
-void raw_ostream::flush_nonempty() {
- assert(OutBufCur > OutBufStart && "Invalid call to flush_nonempty.");
- size_t Length = OutBufCur - OutBufStart;
- OutBufCur = OutBufStart;
- write_impl(OutBufStart, Length);
-}
-
-raw_ostream &raw_ostream::write(unsigned char C) {
- // Group exceptional cases into a single branch.
- if (LLVM_UNLIKELY(OutBufCur >= OutBufEnd)) {
- if (LLVM_UNLIKELY(!OutBufStart)) {
- if (BufferMode == Unbuffered) {
- write_impl(reinterpret_cast<char*>(&C), 1);
- return *this;
- }
- // Set up a buffer and start over.
- SetBuffered();
- return write(C);
- }
-
- flush_nonempty();
- }
-
- *OutBufCur++ = C;
- return *this;
-}
-
-raw_ostream &raw_ostream::write(const char *Ptr, size_t Size) {
- // Group exceptional cases into a single branch.
- if (LLVM_UNLIKELY(size_t(OutBufEnd - OutBufCur) < Size)) {
- if (LLVM_UNLIKELY(!OutBufStart)) {
- if (BufferMode == Unbuffered) {
- write_impl(Ptr, Size);
- return *this;
- }
- // Set up a buffer and start over.
- SetBuffered();
- return write(Ptr, Size);
- }
-
- size_t NumBytes = OutBufEnd - OutBufCur;
-
- // If the buffer is empty at this point we have a string that is larger
- // than the buffer. Directly write the chunk that is a multiple of the
- // preferred buffer size and put the remainder in the buffer.
- if (LLVM_UNLIKELY(OutBufCur == OutBufStart)) {
- assert(NumBytes != 0 && "undefined behavior");
- size_t BytesToWrite = Size - (Size % NumBytes);
- write_impl(Ptr, BytesToWrite);
- size_t BytesRemaining = Size - BytesToWrite;
- if (BytesRemaining > size_t(OutBufEnd - OutBufCur)) {
- // Too much left over to copy into our buffer.
- return write(Ptr + BytesToWrite, BytesRemaining);
- }
- copy_to_buffer(Ptr + BytesToWrite, BytesRemaining);
- return *this;
- }
-
- // We don't have enough space in the buffer to fit the string in. Insert as
- // much as possible, flush and start over with the remainder.
- copy_to_buffer(Ptr, NumBytes);
- flush_nonempty();
- return write(Ptr + NumBytes, Size - NumBytes);
- }
-
- copy_to_buffer(Ptr, Size);
-
- return *this;
-}
-
-void raw_ostream::copy_to_buffer(const char *Ptr, size_t Size) {
- assert(Size <= size_t(OutBufEnd - OutBufCur) && "Buffer overrun!");
-
- // Handle short strings specially, memcpy isn't very good at very short
- // strings.
- switch (Size) {
- case 4: OutBufCur[3] = Ptr[3]; LLVM_FALLTHROUGH;
- case 3: OutBufCur[2] = Ptr[2]; LLVM_FALLTHROUGH;
- case 2: OutBufCur[1] = Ptr[1]; LLVM_FALLTHROUGH;
- case 1: OutBufCur[0] = Ptr[0]; LLVM_FALLTHROUGH;
- case 0: break;
- default:
- memcpy(OutBufCur, Ptr, Size);
- break;
- }
-
- OutBufCur += Size;
-}
-
-template <char C>
-static raw_ostream &write_padding(raw_ostream &OS, unsigned NumChars) {
- static const char Chars[] = {C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C,
- C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C,
- C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C,
- C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C,
- C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C};
-
- // Usually the indentation is small, handle it with a fastpath.
- if (NumChars < array_lengthof(Chars))
- return OS.write(Chars, NumChars);
-
- while (NumChars) {
- unsigned NumToWrite = std::min(NumChars,
- (unsigned)array_lengthof(Chars)-1);
- OS.write(Chars, NumToWrite);
- NumChars -= NumToWrite;
- }
- return OS;
-}
-
-/// indent - Insert 'NumSpaces' spaces.
-raw_ostream &raw_ostream::indent(unsigned NumSpaces) {
- return write_padding<' '>(*this, NumSpaces);
-}
-
-/// write_zeros - Insert 'NumZeros' nulls.
-raw_ostream &raw_ostream::write_zeros(unsigned NumZeros) {
- return write_padding<'\0'>(*this, NumZeros);
-}
-
-void raw_ostream::anchor() {}
-
-//===----------------------------------------------------------------------===//
-// raw_fd_ostream
-//===----------------------------------------------------------------------===//
-
-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
- // the owner of stdout and may set the "binary" flag globally based on Flags.
- if (Filename == "-") {
- EC = std::error_code();
- // If user requested binary then put stdout into binary mode if
- // possible.
- if (!(Flags & fs::OF_Text)) {
-#if defined(_WIN32)
- _setmode(_fileno(stdout), _O_BINARY);
-#endif
- }
- return STDOUT_FILENO;
- }
-
- 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(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(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(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(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(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
-/// closes the file when the stream is destroyed.
-raw_fd_ostream::raw_fd_ostream(int fd, bool shouldClose, bool unbuffered)
- : raw_pwrite_stream(unbuffered), FD(fd), ShouldClose(shouldClose) {
- if (FD < 0 ) {
- ShouldClose = false;
- return;
- }
-
- // Do not attempt to close stdout or stderr. We used to try to maintain the
- // property that tools that support writing file to stdout should not also
- // write informational output to stdout, but in practice we were never able to
- // maintain this invariant. Many features have been added to LLVM and clang
- // (-fdump-record-layouts, optimization remarks, etc) that print to stdout, so
- // users must simply be aware that mixed output and remarks is a possibility.
- if (FD <= STDERR_FILENO)
- ShouldClose = false;
-
-#ifdef _WIN32
- // Check if this is a console device. This is not equivalent to isatty.
- IsWindowsConsole =
- ::GetFileType((HANDLE)::_get_osfhandle(fd)) == FILE_TYPE_CHAR;
-#endif
-
- // Get the starting position.
- off_t loc = ::lseek(FD, 0, SEEK_CUR);
-#ifdef _WIN32
- // MSVCRT's _lseek(SEEK_CUR) doesn't return -1 for pipes.
- SupportsSeeking = loc != (off_t)-1 && ::GetFileType(reinterpret_cast<HANDLE>(::_get_osfhandle(FD))) != FILE_TYPE_PIPE;
-#else
- SupportsSeeking = loc != (off_t)-1;
-#endif
- if (!SupportsSeeking)
- pos = 0;
- else
- pos = static_cast<uint64_t>(loc);
-}
-
-raw_fd_ostream::~raw_fd_ostream() {
- if (FD >= 0) {
- flush();
- if (ShouldClose && ::close(FD) < 0)
- error_detected(std::error_code(errno, std::generic_category()));
- }
-
-#ifdef __MINGW32__
- // On mingw, global dtors should not call exit().
- // report_fatal_error() invokes exit(). We know report_fatal_error()
- // might not write messages to stderr when any errors were detected
- // on FD == 2.
- if (FD == 2) return;
-#endif
-
- // If there are any pending errors, report them now. Clients wishing
- // to avoid report_fatal_error calls should check for errors with
- // has_error() and clear the error flag with clear_error() before
- // destructing raw_ostream objects which may have errors.
- if (has_error())
- report_fatal_error("IO failure on output stream: " + error().message(),
- /*GenCrashDiag=*/false);
-}
-
-#if defined(_WIN32)
-// The most reliable way to print unicode in a Windows console is with
-// WriteConsoleW. To use that, first transcode from UTF-8 to UTF-16. This
-// assumes that LLVM programs always print valid UTF-8 to the console. The data
-// might not be UTF-8 for two major reasons:
-// 1. The program is printing binary (-filetype=obj -o -), in which case it
-// would have been gibberish anyway.
-// 2. The program is printing text in a semi-ascii compatible codepage like
-// shift-jis or cp1252.
-//
-// Most LLVM programs don't produce non-ascii text unless they are quoting
-// user source input. A well-behaved LLVM program should either validate that
-// the input is UTF-8 or transcode from the local codepage to UTF-8 before
-// quoting it. If they don't, this may mess up the encoding, but this is still
-// probably the best compromise we can make.
-static bool write_console_impl(int FD, std::string_view Data) {
- SmallVector<wchar_t, 256> WideText;
-
- // Fall back to ::write if it wasn't valid UTF-8.
- if (auto EC = sys::windows::UTF8ToUTF16(Data, WideText))
- return false;
-
- // On Windows 7 and earlier, WriteConsoleW has a low maximum amount of data
- // that can be written to the console at a time.
- size_t MaxWriteSize = WideText.size();
- if (!RunningWindows8OrGreater())
- MaxWriteSize = 32767;
-
- size_t WCharsWritten = 0;
- do {
- size_t WCharsToWrite =
- std::min(MaxWriteSize, WideText.size() - WCharsWritten);
- DWORD ActuallyWritten;
- bool Success =
- ::WriteConsoleW((HANDLE)::_get_osfhandle(FD), &WideText[WCharsWritten],
- WCharsToWrite, &ActuallyWritten,
- /*Reserved=*/nullptr);
-
- // The most likely reason for WriteConsoleW to fail is that FD no longer
- // points to a console. Fall back to ::write. If this isn't the first loop
- // iteration, something is truly wrong.
- if (!Success)
- return false;
-
- WCharsWritten += ActuallyWritten;
- } while (WCharsWritten != WideText.size());
- return true;
-}
-#endif
-
-void raw_fd_ostream::write_impl(const char *Ptr, size_t Size) {
- assert(FD >= 0 && "File already closed.");
- pos += Size;
-
-#if defined(_WIN32)
- // If this is a Windows console device, try re-encoding from UTF-8 to UTF-16
- // and using WriteConsoleW. If that fails, fall back to plain write().
- if (IsWindowsConsole)
- if (write_console_impl(FD, std::string_view(Ptr, Size)))
- return;
-#endif
-
- // The maximum write size is limited to INT32_MAX. A write
- // greater than SSIZE_MAX is implementation-defined in POSIX,
- // and Windows _write requires 32 bit input.
- size_t MaxWriteSize = INT32_MAX;
-
-#if defined(__linux__)
- // It is observed that Linux returns EINVAL for a very large write (>2G).
- // Make it a reasonably small value.
- MaxWriteSize = 1024 * 1024 * 1024;
-#endif
-
- do {
- size_t ChunkSize = std::min(Size, MaxWriteSize);
-#ifdef _WIN32
- int ret = ::_write(FD, Ptr, ChunkSize);
-#else
- ssize_t ret = ::write(FD, Ptr, ChunkSize);
-#endif
-
- if (ret < 0) {
- // If it's a recoverable error, swallow it and retry the write.
- //
- // Ideally we wouldn't ever see EAGAIN or EWOULDBLOCK here, since
- // raw_ostream isn't designed to do non-blocking I/O. However, some
- // programs, such as old versions of bjam, have mistakenly used
- // O_NONBLOCK. For compatibility, emulate blocking semantics by
- // spinning until the write succeeds. If you don't want spinning,
- // don't use O_NONBLOCK file descriptors with raw_ostream.
- if (errno == EINTR || errno == EAGAIN
-#ifdef EWOULDBLOCK
- || errno == EWOULDBLOCK
-#endif
- )
- continue;
-
- // Otherwise it's a non-recoverable error. Note it and quit.
- error_detected(std::error_code(errno, std::generic_category()));
- break;
- }
-
- // The write may have written some or all of the data. Update the
- // size and buffer pointer to reflect the remainder that needs
- // to be written. If there are no bytes left, we're done.
- Ptr += ret;
- Size -= ret;
- } while (Size > 0);
-}
-
-void raw_fd_ostream::close() {
- assert(ShouldClose);
- ShouldClose = false;
- flush();
- if (::close(FD) < 0)
- error_detected(std::error_code(errno, std::generic_category()));
- FD = -1;
-}
-
-uint64_t raw_fd_ostream::seek(uint64_t off) {
- assert(SupportsSeeking && "Stream does not support seeking!");
- flush();
-#ifdef _WIN32
- pos = ::_lseeki64(FD, off, SEEK_SET);
-#else
- pos = ::lseek(FD, off, SEEK_SET);
-#endif
- if (pos == (uint64_t)-1)
- error_detected(std::error_code(errno, std::generic_category()));
- return pos;
-}
-
-void raw_fd_ostream::pwrite_impl(const char *Ptr, size_t Size,
- uint64_t Offset) {
- uint64_t Pos = tell();
- seek(Offset);
- write(Ptr, Size);
- seek(Pos);
-}
-
-size_t raw_fd_ostream::preferred_buffer_size() const {
-#if defined(_WIN32)
- // Disable buffering for console devices. Console output is re-encoded from
- // UTF-8 to UTF-16 on Windows, and buffering it would require us to split the
- // buffer on a valid UTF-8 codepoint boundary. Terminal buffering is disabled
- // below on most other OSs, so do the same thing on Windows and avoid that
- // complexity.
- if (IsWindowsConsole)
- return 0;
- return raw_ostream::preferred_buffer_size();
-#elif !defined(__minix)
- // Minix has no st_blksize.
- assert(FD >= 0 && "File not yet open!");
- struct stat statbuf;
- if (fstat(FD, &statbuf) != 0)
- return 0;
-
- // If this is a terminal, don't use buffering. Line buffering
- // would be a more traditional thing to do, but it's not worth
- // the complexity.
- if (S_ISCHR(statbuf.st_mode) && isatty(FD))
- return 0;
- // Return the preferred block size.
- return statbuf.st_blksize;
-#else
- return raw_ostream::preferred_buffer_size();
-#endif
-}
-
-void raw_fd_ostream::anchor() {}
-
-//===----------------------------------------------------------------------===//
-// outs(), errs(), nulls()
-//===----------------------------------------------------------------------===//
-
-/// outs() - This returns a reference to a raw_ostream for standard output.
-/// Use it like: outs() << "foo" << "bar";
-raw_ostream &wpi::outs() {
- // Set buffer settings to model stdout behavior.
- std::error_code EC;
- static raw_fd_ostream* S = new raw_fd_ostream("-", EC, fs::F_None);
- assert(!EC);
- return *S;
-}
-
-/// errs() - This returns a reference to a raw_ostream for standard error.
-/// Use it like: errs() << "foo" << "bar";
-raw_ostream &wpi::errs() {
- // Set standard error to be unbuffered by default.
- static raw_fd_ostream* S = new raw_fd_ostream(STDERR_FILENO, false, true);
- return *S;
-}
-
-/// nulls() - This returns a reference to a raw_ostream which discards output.
-raw_ostream &wpi::nulls() {
- static raw_null_ostream* S = new raw_null_ostream;
- return *S;
-}
-
-//===----------------------------------------------------------------------===//
-// raw_string_ostream
-//===----------------------------------------------------------------------===//
-
-raw_string_ostream::~raw_string_ostream() {
- flush();
-}
-
-void raw_string_ostream::write_impl(const char *Ptr, size_t Size) {
- OS.append(Ptr, Size);
-}
-
-//===----------------------------------------------------------------------===//
-// raw_svector_ostream
-//===----------------------------------------------------------------------===//
-
-uint64_t raw_svector_ostream::current_pos() const { return OS.size(); }
-
-void raw_svector_ostream::write_impl(const char *Ptr, size_t Size) {
- OS.append(Ptr, Ptr + Size);
-}
-
-void raw_svector_ostream::pwrite_impl(const char *Ptr, size_t Size,
- uint64_t Offset) {
- memcpy(OS.data() + Offset, Ptr, Size);
-}
-
-//===----------------------------------------------------------------------===//
-// raw_vector_ostream
-//===----------------------------------------------------------------------===//
-
-uint64_t raw_vector_ostream::current_pos() const { return OS.size(); }
-
-void raw_vector_ostream::write_impl(const char *Ptr, size_t Size) {
- OS.insert(OS.end(), Ptr, Ptr + Size);
-}
-
-void raw_vector_ostream::pwrite_impl(const char *Ptr, size_t Size,
- uint64_t Offset) {
- memcpy(OS.data() + Offset, Ptr, Size);
-}
-
-//===----------------------------------------------------------------------===//
-// raw_usvector_ostream
-//===----------------------------------------------------------------------===//
-
-uint64_t raw_usvector_ostream::current_pos() const { return OS.size(); }
-
-void raw_usvector_ostream::write_impl(const char *Ptr, size_t Size) {
- OS.append(reinterpret_cast<const uint8_t *>(Ptr),
- reinterpret_cast<const uint8_t *>(Ptr) + Size);
-}
-
-void raw_usvector_ostream::pwrite_impl(const char *Ptr, size_t Size,
- uint64_t Offset) {
- memcpy(OS.data() + Offset, Ptr, Size);
-}
-
-//===----------------------------------------------------------------------===//
-// raw_uvector_ostream
-//===----------------------------------------------------------------------===//
-
-uint64_t raw_uvector_ostream::current_pos() const { return OS.size(); }
-
-void raw_uvector_ostream::write_impl(const char *Ptr, size_t Size) {
- OS.insert(OS.end(), reinterpret_cast<const uint8_t *>(Ptr),
- reinterpret_cast<const uint8_t *>(Ptr) + Size);
-}
-
-void raw_uvector_ostream::pwrite_impl(const char *Ptr, size_t Size,
- uint64_t Offset) {
- memcpy(OS.data() + Offset, Ptr, Size);
-}
-
-//===----------------------------------------------------------------------===//
-// raw_null_ostream
-//===----------------------------------------------------------------------===//
-
-raw_null_ostream::~raw_null_ostream() {
-#ifndef NDEBUG
- // ~raw_ostream asserts that the buffer is empty. This isn't necessary
- // with raw_null_ostream, but it's better to have raw_null_ostream follow
- // the rules than to change the rules just for raw_null_ostream.
- flush();
-#endif
-}
-
-void raw_null_ostream::write_impl(const char * /*Ptr*/, size_t /*Size*/) {}
-
-uint64_t raw_null_ostream::current_pos() const {
- return 0;
-}
-
-void raw_null_ostream::pwrite_impl(const char * /*Ptr*/, size_t /*Size*/,
- uint64_t /*Offset*/) {}
-
-void raw_pwrite_stream::anchor() {}
-
-void buffer_ostream::anchor() {}
diff --git a/wpiutil/src/main/native/cpp/mpack.cpp b/wpiutil/src/main/native/cpp/mpack.cpp
deleted file mode 100644
index fbcb6a4..0000000
--- a/wpiutil/src/main/native/cpp/mpack.cpp
+++ /dev/null
@@ -1,7251 +0,0 @@
-/**
- * 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_socket_istream.cpp b/wpiutil/src/main/native/cpp/raw_socket_istream.cpp
deleted file mode 100644
index c6b759e..0000000
--- a/wpiutil/src/main/native/cpp/raw_socket_istream.cpp
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (c) FIRST and other WPILib contributors.
-// Open Source Software; you can modify and/or share it under the terms of
-// the WPILib BSD license file in the root directory of this project.
-
-#include "wpi/raw_socket_istream.h"
-
-#include "wpi/NetworkStream.h"
-
-using namespace wpi;
-
-void raw_socket_istream::read_impl(void* data, size_t len) {
- char* cdata = static_cast<char*>(data);
- size_t pos = 0;
-
- while (pos < len) {
- NetworkStream::Error err;
- size_t count = m_stream.receive(&cdata[pos], len - pos, &err, m_timeout);
- if (count == 0) {
- error_detected();
- break;
- }
- pos += count;
- }
- set_read_count(pos);
-}
-
-void raw_socket_istream::close() {
- m_stream.close();
-}
-
-size_t raw_socket_istream::in_avail() const {
- return 0;
-}
diff --git a/wpiutil/src/main/native/cpp/raw_socket_ostream.cpp b/wpiutil/src/main/native/cpp/raw_socket_ostream.cpp
deleted file mode 100644
index af01c84..0000000
--- a/wpiutil/src/main/native/cpp/raw_socket_ostream.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (c) FIRST and other WPILib contributors.
-// Open Source Software; you can modify and/or share it under the terms of
-// the WPILib BSD license file in the root directory of this project.
-
-#include "wpi/raw_socket_ostream.h"
-
-#include "wpi/NetworkStream.h"
-
-using namespace wpi;
-
-raw_socket_ostream::~raw_socket_ostream() {
- flush();
- if (m_shouldClose) {
- close();
- }
-}
-
-void raw_socket_ostream::write_impl(const char* data, size_t len) {
- size_t pos = 0;
-
- while (pos < len) {
- NetworkStream::Error err;
- size_t count = m_stream.send(&data[pos], len - pos, &err);
- if (count == 0) {
- error_detected();
- return;
- }
- pos += count;
- }
-}
-
-uint64_t raw_socket_ostream::current_pos() const {
- return 0;
-}
-
-void raw_socket_ostream::close() {
- if (!m_shouldClose) {
- return;
- }
- flush();
- m_stream.close();
-}
diff --git a/wpiutil/src/main/native/cpp/raw_uv_ostream.cpp b/wpiutil/src/main/native/cpp/raw_uv_ostream.cpp
deleted file mode 100644
index f055a2a..0000000
--- a/wpiutil/src/main/native/cpp/raw_uv_ostream.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright (c) FIRST and other WPILib contributors.
-// Open Source Software; you can modify and/or share it under the terms of
-// the WPILib BSD license file in the root directory of this project.
-
-#include "wpi/raw_uv_ostream.h"
-
-#include <cstring>
-
-using namespace wpi;
-
-void raw_uv_ostream::write_impl(const char* data, size_t len) {
- while (len > 0) {
- // allocate additional buffers as required
- if (m_left == 0) {
- m_bufs.emplace_back(m_alloc());
- // we want bufs() to always be valid, so set len=0 and keep track of the
- // amount of space remaining separately
- m_left = m_bufs.back().len;
- m_bufs.back().len = 0;
- assert(m_left != 0);
- }
-
- size_t amt = (std::min)(m_left, len);
- auto& buf = m_bufs.back();
- std::memcpy(buf.base + buf.len, data, amt);
- data += amt;
- len -= amt;
- buf.len += amt;
- m_left -= amt;
- }
-}
-
-uint64_t raw_uv_ostream::current_pos() const {
- uint64_t size = 0;
- for (auto&& buf : m_bufs) {
- size += buf.len;
- }
- return size;
-}
diff --git a/wpiutil/src/main/native/cpp/sha1.cpp b/wpiutil/src/main/native/cpp/sha1.cpp
index 8ec7cb1..98dc543 100644
--- a/wpiutil/src/main/native/cpp/sha1.cpp
+++ b/wpiutil/src/main/native/cpp/sha1.cpp
@@ -217,7 +217,7 @@
}
void SHA1::Update(std::string_view s) {
- raw_mem_istream is(span<const char>(s.data(), s.size()));
+ raw_mem_istream is(std::span<const char>(s.data(), s.size()));
Update(is);
}
diff --git a/wpiutil/src/main/native/cpp/uv/Async.cpp b/wpiutil/src/main/native/cpp/uv/Async.cpp
deleted file mode 100644
index c9d698e..0000000
--- a/wpiutil/src/main/native/cpp/uv/Async.cpp
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (c) FIRST and other WPILib contributors.
-// Open Source Software; you can modify and/or share it under the terms of
-// the WPILib BSD license file in the root directory of this project.
-
-#include "wpi/uv/Async.h"
-
-#include "wpi/uv/Loop.h"
-
-namespace wpi::uv {
-
-Async<>::~Async() noexcept {
- if (auto loop = m_loop.lock()) {
- Close();
- } else {
- ForceClosed();
- }
-}
-
-std::shared_ptr<Async<>> Async<>::Create(const std::shared_ptr<Loop>& loop) {
- auto h = std::make_shared<Async>(loop, private_init{});
- int err = uv_async_init(loop->GetRaw(), h->GetRaw(), [](uv_async_t* handle) {
- Async& h = *static_cast<Async*>(handle->data);
- h.wakeup();
- });
- if (err < 0) {
- loop->ReportError(err);
- return nullptr;
- }
- h->Keep();
- return h;
-}
-
-} // namespace wpi::uv
diff --git a/wpiutil/src/main/native/cpp/uv/Check.cpp b/wpiutil/src/main/native/cpp/uv/Check.cpp
deleted file mode 100644
index 97265d4..0000000
--- a/wpiutil/src/main/native/cpp/uv/Check.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (c) FIRST and other WPILib contributors.
-// Open Source Software; you can modify and/or share it under the terms of
-// the WPILib BSD license file in the root directory of this project.
-
-#include "wpi/uv/Check.h"
-
-#include "wpi/uv/Loop.h"
-
-namespace wpi::uv {
-
-std::shared_ptr<Check> Check::Create(Loop& loop) {
- auto h = std::make_shared<Check>(private_init{});
- int err = uv_check_init(loop.GetRaw(), h->GetRaw());
- if (err < 0) {
- loop.ReportError(err);
- return nullptr;
- }
- h->Keep();
- return h;
-}
-
-void Check::Start() {
- Invoke(&uv_check_start, GetRaw(), [](uv_check_t* handle) {
- Check& h = *static_cast<Check*>(handle->data);
- h.check();
- });
-}
-
-} // namespace wpi::uv
diff --git a/wpiutil/src/main/native/cpp/uv/FsEvent.cpp b/wpiutil/src/main/native/cpp/uv/FsEvent.cpp
deleted file mode 100644
index 3c83d1d..0000000
--- a/wpiutil/src/main/native/cpp/uv/FsEvent.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright (c) FIRST and other WPILib contributors.
-// Open Source Software; you can modify and/or share it under the terms of
-// the WPILib BSD license file in the root directory of this project.
-
-#include "wpi/uv/FsEvent.h"
-
-#include <cstdlib>
-
-#include "wpi/SmallString.h"
-#include "wpi/uv/Loop.h"
-
-namespace wpi::uv {
-
-std::shared_ptr<FsEvent> FsEvent::Create(Loop& loop) {
- auto h = std::make_shared<FsEvent>(private_init{});
- int err = uv_fs_event_init(loop.GetRaw(), h->GetRaw());
- if (err < 0) {
- loop.ReportError(err);
- return nullptr;
- }
- h->Keep();
- return h;
-}
-
-void FsEvent::Start(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) {
- h.ReportError(status);
- } else {
- h.fsEvent(filename, events);
- }
- },
- pathBuf.c_str(), flags);
-}
-
-std::string FsEvent::GetPath() {
- // Per the libuv docs, GetPath() always gives us a null-terminated string.
- // common case should be small
- char buf[128];
- size_t size = 128;
- int r = uv_fs_event_getpath(GetRaw(), buf, &size);
- if (r == 0) {
- return buf;
- } else if (r == UV_ENOBUFS) {
- // need to allocate a big enough buffer
- char* buf2 = static_cast<char*>(std::malloc(size));
- r = uv_fs_event_getpath(GetRaw(), buf2, &size);
- if (r == 0) {
- std::string out{buf2};
- std::free(buf2);
- return out;
- }
- std::free(buf2);
- }
- ReportError(r);
- return std::string{};
-}
-
-} // namespace wpi::uv
diff --git a/wpiutil/src/main/native/cpp/uv/GetAddrInfo.cpp b/wpiutil/src/main/native/cpp/uv/GetAddrInfo.cpp
deleted file mode 100644
index 2e6e38f..0000000
--- a/wpiutil/src/main/native/cpp/uv/GetAddrInfo.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright (c) FIRST and other WPILib contributors.
-// Open Source Software; you can modify and/or share it under the terms of
-// the WPILib BSD license file in the root directory of this project.
-
-#include "wpi/uv/GetAddrInfo.h"
-
-#include "wpi/SmallString.h"
-#include "wpi/uv/Loop.h"
-#include "wpi/uv/util.h"
-
-namespace wpi::uv {
-
-GetAddrInfoReq::GetAddrInfoReq() {
- error = [this](Error err) { GetLoop().error(err); };
-}
-
-void GetAddrInfo(Loop& loop, const std::shared_ptr<GetAddrInfoReq>& req,
- std::string_view node, std::string_view service,
- const addrinfo* hints) {
- 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) {
- h.ReportError(status);
- } else {
- h.resolved(*res);
- }
- uv_freeaddrinfo(res);
- h.Release(); // this is always a one-shot
- },
- node.empty() ? nullptr : nodeStr.c_str(),
- service.empty() ? nullptr : serviceStr.c_str(), hints);
- if (err < 0) {
- loop.ReportError(err);
- } else {
- req->Keep();
- }
-}
-
-void GetAddrInfo(Loop& loop, std::function<void(const addrinfo&)> callback,
- std::string_view node, std::string_view service,
- const addrinfo* hints) {
- auto req = std::make_shared<GetAddrInfoReq>();
- req->resolved.connect(std::move(callback));
- GetAddrInfo(loop, req, node, service, hints);
-}
-
-} // namespace wpi::uv
diff --git a/wpiutil/src/main/native/cpp/uv/GetNameInfo.cpp b/wpiutil/src/main/native/cpp/uv/GetNameInfo.cpp
deleted file mode 100644
index 4e662f3..0000000
--- a/wpiutil/src/main/native/cpp/uv/GetNameInfo.cpp
+++ /dev/null
@@ -1,94 +0,0 @@
-// Copyright (c) FIRST and other WPILib contributors.
-// Open Source Software; you can modify and/or share it under the terms of
-// the WPILib BSD license file in the root directory of this project.
-
-#include "wpi/uv/GetNameInfo.h"
-
-#include "wpi/uv/Loop.h"
-#include "wpi/uv/util.h"
-
-namespace wpi::uv {
-
-GetNameInfoReq::GetNameInfoReq() {
- error = [this](Error err) { GetLoop().error(err); };
-}
-
-void GetNameInfo(Loop& loop, const std::shared_ptr<GetNameInfoReq>& req,
- const sockaddr& addr, int flags) {
- int err = uv_getnameinfo(
- loop.GetRaw(), req->GetRaw(),
- [](uv_getnameinfo_t* req, int status, const char* hostname,
- const char* service) {
- auto& h = *static_cast<GetNameInfoReq*>(req->data);
- if (status < 0) {
- h.ReportError(status);
- } else {
- h.resolved(hostname, service);
- }
- h.Release(); // this is always a one-shot
- },
- &addr, flags);
- if (err < 0) {
- loop.ReportError(err);
- } else {
- req->Keep();
- }
-}
-
-void GetNameInfo(Loop& loop,
- std::function<void(const char*, const char*)> callback,
- const sockaddr& addr, int flags) {
- auto req = std::make_shared<GetNameInfoReq>();
- req->resolved.connect(std::move(callback));
- GetNameInfo(loop, req, addr, flags);
-}
-
-void GetNameInfo4(Loop& loop, const std::shared_ptr<GetNameInfoReq>& req,
- std::string_view ip, unsigned int port, int flags) {
- sockaddr_in addr;
- int err = NameToAddr(ip, port, &addr);
- if (err < 0) {
- loop.ReportError(err);
- } else {
- GetNameInfo(loop, req, reinterpret_cast<const sockaddr&>(addr), flags);
- }
-}
-
-void GetNameInfo4(Loop& loop,
- std::function<void(const char*, const char*)> callback,
- std::string_view ip, unsigned int port, int flags) {
- sockaddr_in addr;
- int err = NameToAddr(ip, port, &addr);
- if (err < 0) {
- loop.ReportError(err);
- } else {
- GetNameInfo(loop, std::move(callback),
- reinterpret_cast<const sockaddr&>(addr), flags);
- }
-}
-
-void GetNameInfo6(Loop& loop, const std::shared_ptr<GetNameInfoReq>& req,
- std::string_view ip, unsigned int port, int flags) {
- sockaddr_in6 addr;
- int err = NameToAddr(ip, port, &addr);
- if (err < 0) {
- loop.ReportError(err);
- } else {
- GetNameInfo(loop, req, reinterpret_cast<const sockaddr&>(addr), flags);
- }
-}
-
-void GetNameInfo6(Loop& loop,
- std::function<void(const char*, const char*)> callback,
- std::string_view ip, unsigned int port, int flags) {
- sockaddr_in6 addr;
- int err = NameToAddr(ip, port, &addr);
- if (err < 0) {
- loop.ReportError(err);
- } else {
- GetNameInfo(loop, std::move(callback),
- reinterpret_cast<const sockaddr&>(addr), flags);
- }
-}
-
-} // namespace wpi::uv
diff --git a/wpiutil/src/main/native/cpp/uv/Handle.cpp b/wpiutil/src/main/native/cpp/uv/Handle.cpp
deleted file mode 100644
index 74c4c60..0000000
--- a/wpiutil/src/main/native/cpp/uv/Handle.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (c) FIRST and other WPILib contributors.
-// Open Source Software; you can modify and/or share it under the terms of
-// the WPILib BSD license file in the root directory of this project.
-
-#include "wpi/uv/Handle.h"
-
-using namespace wpi::uv;
-
-Handle::~Handle() noexcept {
- if (!m_closed && m_uv_handle->type != UV_UNKNOWN_HANDLE) {
- uv_close(m_uv_handle, [](uv_handle_t* uv_handle) { std::free(uv_handle); });
- } else {
- std::free(m_uv_handle);
- }
-}
-
-void Handle::Close() noexcept {
- if (!IsClosing()) {
- uv_close(m_uv_handle, [](uv_handle_t* handle) {
- Handle& h = *static_cast<Handle*>(handle->data);
- h.closed();
- h.Release(); // free ourselves
- });
- m_closed = true;
- }
-}
-
-void Handle::AllocBuf(uv_handle_t* handle, size_t size, uv_buf_t* buf) {
- auto& h = *static_cast<Handle*>(handle->data);
- *buf = h.m_allocBuf(size);
-}
-
-void Handle::DefaultFreeBuf(Buffer& buf) {
- buf.Deallocate();
-}
diff --git a/wpiutil/src/main/native/cpp/uv/Idle.cpp b/wpiutil/src/main/native/cpp/uv/Idle.cpp
deleted file mode 100644
index 6bf8602..0000000
--- a/wpiutil/src/main/native/cpp/uv/Idle.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (c) FIRST and other WPILib contributors.
-// Open Source Software; you can modify and/or share it under the terms of
-// the WPILib BSD license file in the root directory of this project.
-
-#include "wpi/uv/Idle.h"
-
-#include "wpi/uv/Loop.h"
-
-namespace wpi::uv {
-
-std::shared_ptr<Idle> Idle::Create(Loop& loop) {
- auto h = std::make_shared<Idle>(private_init{});
- int err = uv_idle_init(loop.GetRaw(), h->GetRaw());
- if (err < 0) {
- loop.ReportError(err);
- return nullptr;
- }
- h->Keep();
- return h;
-}
-
-void Idle::Start() {
- Invoke(&uv_idle_start, GetRaw(), [](uv_idle_t* handle) {
- Idle& h = *static_cast<Idle*>(handle->data);
- h.idle();
- });
-}
-
-} // namespace wpi::uv
diff --git a/wpiutil/src/main/native/cpp/uv/Loop.cpp b/wpiutil/src/main/native/cpp/uv/Loop.cpp
deleted file mode 100644
index c5b7163..0000000
--- a/wpiutil/src/main/native/cpp/uv/Loop.cpp
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright (c) FIRST and other WPILib contributors.
-// Open Source Software; you can modify and/or share it under the terms of
-// the WPILib BSD license file in the root directory of this project.
-
-#include "wpi/uv/Loop.h"
-
-using namespace wpi::uv;
-
-Loop::Loop(const private_init&) noexcept {
-#ifndef _WIN32
- // Ignore SIGPIPE (see https://github.com/joyent/libuv/issues/1254)
- static bool once = []() {
- signal(SIGPIPE, SIG_IGN);
- return true;
- }();
- (void)once;
-#endif
-}
-
-Loop::~Loop() noexcept {
- if (m_loop) {
- m_loop->data = nullptr;
- Close();
- }
-}
-
-std::shared_ptr<Loop> Loop::Create() {
- auto loop = std::make_shared<Loop>(private_init{});
- if (uv_loop_init(&loop->m_loopStruct) < 0) {
- return nullptr;
- }
- loop->m_loop = &loop->m_loopStruct;
- loop->m_loop->data = loop.get();
- return loop;
-}
-
-std::shared_ptr<Loop> Loop::GetDefault() {
- static std::shared_ptr<Loop> loop = std::make_shared<Loop>(private_init{});
- loop->m_loop = uv_default_loop();
- if (!loop->m_loop) {
- return nullptr;
- }
- loop->m_loop->data = loop.get();
- return loop;
-}
-
-void Loop::Close() {
- int err = uv_loop_close(m_loop);
- if (err < 0) {
- ReportError(err);
- }
-}
-
-void Loop::Walk(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<function_ref<void(Handle&)>*>(func);
- f(h);
- },
- &callback);
-}
-
-void Loop::Fork() {
- int err = uv_loop_fork(m_loop);
- if (err < 0) {
- ReportError(err);
- }
-}
diff --git a/wpiutil/src/main/native/cpp/uv/NameToAddr.cpp b/wpiutil/src/main/native/cpp/uv/NameToAddr.cpp
deleted file mode 100644
index 23ec6da..0000000
--- a/wpiutil/src/main/native/cpp/uv/NameToAddr.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright (c) FIRST and other WPILib contributors.
-// Open Source Software; you can modify and/or share it under the terms of
-// the WPILib BSD license file in the root directory of this project.
-
-#include "wpi/uv/util.h" // NOLINT(build/include_order)
-
-#include <cstring>
-
-#include "wpi/SmallString.h"
-
-namespace wpi::uv {
-
-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 {
- SmallString<128> ipBuf{ip};
- return uv_ip4_addr(ipBuf.c_str(), port, addr);
- }
-}
-
-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 {
- SmallString<128> ipBuf{ip};
- return uv_ip6_addr(ipBuf.c_str(), port, addr);
- }
-}
-
-int NameToAddr(std::string_view ip, in_addr* addr) {
- if (ip.empty()) {
- addr->s_addr = INADDR_ANY;
- return 0;
- } else {
- SmallString<128> ipBuf{ip};
- return uv_inet_pton(AF_INET, ipBuf.c_str(), addr);
- }
-}
-
-int NameToAddr(std::string_view ip, in6_addr* addr) {
- if (ip.empty()) {
- *addr = in6addr_any;
- return 0;
- } else {
- SmallString<128> ipBuf{ip};
- return uv_inet_pton(AF_INET6, ipBuf.c_str(), addr);
- }
-}
-
-} // namespace wpi::uv
diff --git a/wpiutil/src/main/native/cpp/uv/NetworkStream.cpp b/wpiutil/src/main/native/cpp/uv/NetworkStream.cpp
deleted file mode 100644
index 0bc3337..0000000
--- a/wpiutil/src/main/native/cpp/uv/NetworkStream.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (c) FIRST and other WPILib contributors.
-// Open Source Software; you can modify and/or share it under the terms of
-// the WPILib BSD license file in the root directory of this project.
-
-#include "wpi/uv/NetworkStream.h"
-
-namespace wpi::uv {
-
-ConnectReq::ConnectReq() {
- error = [this](Error err) { GetStream().error(err); };
-}
-
-void NetworkStream::Listen(int backlog) {
- Invoke(&uv_listen, GetRawStream(), backlog,
- [](uv_stream_t* handle, int status) {
- auto& h = *static_cast<NetworkStream*>(handle->data);
- if (status < 0) {
- h.ReportError(status);
- } else {
- h.connection();
- }
- });
-}
-
-void NetworkStream::Listen(std::function<void()> callback, int backlog) {
- connection.connect(std::move(callback));
- Listen(backlog);
-}
-
-} // namespace wpi::uv
diff --git a/wpiutil/src/main/native/cpp/uv/Pipe.cpp b/wpiutil/src/main/native/cpp/uv/Pipe.cpp
deleted file mode 100644
index b5ca673..0000000
--- a/wpiutil/src/main/native/cpp/uv/Pipe.cpp
+++ /dev/null
@@ -1,138 +0,0 @@
-// Copyright (c) FIRST and other WPILib contributors.
-// Open Source Software; you can modify and/or share it under the terms of
-// the WPILib BSD license file in the root directory of this project.
-
-#include "wpi/uv/Pipe.h"
-
-#include <cstdlib>
-
-#include "wpi/SmallString.h"
-
-namespace wpi::uv {
-
-std::shared_ptr<Pipe> Pipe::Create(Loop& loop, bool ipc) {
- auto h = std::make_shared<Pipe>(private_init{});
- int err = uv_pipe_init(loop.GetRaw(), h->GetRaw(), ipc ? 1 : 0);
- if (err < 0) {
- loop.ReportError(err);
- return nullptr;
- }
- h->Keep();
- return h;
-}
-
-void Pipe::Reuse(std::function<void()> callback, bool ipc) {
- if (IsClosing()) {
- return;
- }
- if (!m_reuseData) {
- m_reuseData = std::make_unique<ReuseData>();
- }
- m_reuseData->callback = 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;
- }
- auto data = std::move(h.m_reuseData);
- auto err =
- uv_pipe_init(h.GetLoopRef().GetRaw(), h.GetRaw(), data->ipc ? 1 : 0);
- if (err < 0) {
- h.ReportError(err);
- return;
- }
- data->callback();
- });
-}
-
-std::shared_ptr<Pipe> Pipe::Accept() {
- auto client = Create(GetLoopRef(), GetRaw()->ipc);
- if (!client) {
- return nullptr;
- }
- if (!Accept(client)) {
- client->Release();
- return nullptr;
- }
- return client;
-}
-
-Pipe* Pipe::DoAccept() {
- return Accept().get();
-}
-
-void Pipe::Bind(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{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) {
- h.ReportError(status);
- } else {
- h.connected();
- }
- h.Release(); // this is always a one-shot
- });
- req->Keep();
-}
-
-void Pipe::Connect(std::string_view name, std::function<void()> callback) {
- auto req = std::make_shared<PipeConnectReq>();
- req->connected.connect(std::move(callback));
- Connect(name, req);
-}
-
-std::string Pipe::GetSock() {
- // Per libuv docs, the returned buffer is NOT null terminated.
- // common case should be small
- char buf[128];
- size_t size = 128;
- int r = uv_pipe_getsockname(GetRaw(), buf, &size);
- if (r == 0) {
- return std::string{buf, size};
- } else if (r == UV_ENOBUFS) {
- // need to allocate a big enough buffer
- char* buf2 = static_cast<char*>(std::malloc(size));
- r = uv_pipe_getsockname(GetRaw(), buf2, &size);
- if (r == 0) {
- std::string out{buf2, size};
- std::free(buf2);
- return out;
- }
- std::free(buf2);
- }
- ReportError(r);
- return std::string{};
-}
-
-std::string Pipe::GetPeer() {
- // Per libuv docs, the returned buffer is NOT null terminated.
- // common case should be small
- char buf[128];
- size_t size = 128;
- int r = uv_pipe_getpeername(GetRaw(), buf, &size);
- if (r == 0) {
- return std::string{buf, size};
- } else if (r == UV_ENOBUFS) {
- // need to allocate a big enough buffer
- char* buf2 = static_cast<char*>(std::malloc(size));
- r = uv_pipe_getpeername(GetRaw(), buf2, &size);
- if (r == 0) {
- std::string out{buf2, size};
- std::free(buf2);
- return out;
- }
- std::free(buf2);
- }
- ReportError(r);
- return std::string{};
-}
-
-} // namespace wpi::uv
diff --git a/wpiutil/src/main/native/cpp/uv/Poll.cpp b/wpiutil/src/main/native/cpp/uv/Poll.cpp
deleted file mode 100644
index 090a40b..0000000
--- a/wpiutil/src/main/native/cpp/uv/Poll.cpp
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright (c) FIRST and other WPILib contributors.
-// Open Source Software; you can modify and/or share it under the terms of
-// the WPILib BSD license file in the root directory of this project.
-
-#include "wpi/uv/Poll.h"
-
-#include "wpi/uv/Loop.h"
-
-namespace wpi::uv {
-
-std::shared_ptr<Poll> Poll::Create(Loop& loop, int fd) {
- auto h = std::make_shared<Poll>(private_init{});
- int err = uv_poll_init(loop.GetRaw(), h->GetRaw(), fd);
- if (err < 0) {
- loop.ReportError(err);
- return nullptr;
- }
- h->Keep();
- return h;
-}
-
-std::shared_ptr<Poll> Poll::CreateSocket(Loop& loop, uv_os_sock_t sock) {
- auto h = std::make_shared<Poll>(private_init{});
- int err = uv_poll_init_socket(loop.GetRaw(), h->GetRaw(), sock);
- if (err < 0) {
- loop.ReportError(err);
- return nullptr;
- }
- h->Keep();
- return h;
-}
-
-void Poll::Reuse(int fd, std::function<void()> callback) {
- if (IsClosing()) {
- return;
- }
- if (!m_reuseData) {
- m_reuseData = std::make_unique<ReuseData>();
- }
- m_reuseData->callback = 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
- }
- auto data = std::move(h.m_reuseData);
- int err = uv_poll_init(h.GetLoopRef().GetRaw(), h.GetRaw(), data->fd);
- if (err < 0) {
- h.ReportError(err);
- return;
- }
- data->callback();
- });
-}
-
-void Poll::ReuseSocket(uv_os_sock_t sock, std::function<void()> callback) {
- if (IsClosing()) {
- return;
- }
- if (!m_reuseData) {
- m_reuseData = std::make_unique<ReuseData>();
- }
- m_reuseData->callback = 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
- }
- auto data = std::move(h.m_reuseData);
- int err = uv_poll_init(h.GetLoopRef().GetRaw(), h.GetRaw(), data->sock);
- if (err < 0) {
- h.ReportError(err);
- return;
- }
- data->callback();
- });
-}
-
-void Poll::Start(int events) {
- Invoke(&uv_poll_start, GetRaw(), events,
- [](uv_poll_t* handle, int status, int events) {
- Poll& h = *static_cast<Poll*>(handle->data);
- if (status < 0) {
- h.ReportError(status);
- } else {
- h.pollEvent(events);
- }
- });
-}
-
-} // namespace wpi::uv
diff --git a/wpiutil/src/main/native/cpp/uv/Prepare.cpp b/wpiutil/src/main/native/cpp/uv/Prepare.cpp
deleted file mode 100644
index 048fd08..0000000
--- a/wpiutil/src/main/native/cpp/uv/Prepare.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (c) FIRST and other WPILib contributors.
-// Open Source Software; you can modify and/or share it under the terms of
-// the WPILib BSD license file in the root directory of this project.
-
-#include "wpi/uv/Prepare.h"
-
-#include "wpi/uv/Loop.h"
-
-namespace wpi::uv {
-
-std::shared_ptr<Prepare> Prepare::Create(Loop& loop) {
- auto h = std::make_shared<Prepare>(private_init{});
- int err = uv_prepare_init(loop.GetRaw(), h->GetRaw());
- if (err < 0) {
- loop.ReportError(err);
- return nullptr;
- }
- h->Keep();
- return h;
-}
-
-void Prepare::Start() {
- Invoke(&uv_prepare_start, GetRaw(), [](uv_prepare_t* handle) {
- Prepare& h = *static_cast<Prepare*>(handle->data);
- h.prepare();
- });
-}
-
-} // namespace wpi::uv
diff --git a/wpiutil/src/main/native/cpp/uv/Process.cpp b/wpiutil/src/main/native/cpp/uv/Process.cpp
deleted file mode 100644
index c8d5229..0000000
--- a/wpiutil/src/main/native/cpp/uv/Process.cpp
+++ /dev/null
@@ -1,133 +0,0 @@
-// Copyright (c) FIRST and other WPILib contributors.
-// Open Source Software; you can modify and/or share it under the terms of
-// the WPILib BSD license file in the root directory of this project.
-
-#include "wpi/uv/Process.h"
-
-#include "wpi/SmallString.h"
-#include "wpi/uv/Loop.h"
-#include "wpi/uv/Pipe.h"
-
-namespace wpi::uv {
-
-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;
-
- coptions.exit_cb = [](uv_process_t* handle, int64_t status, int signal) {
- auto& h = *static_cast<Process*>(handle->data);
- h.exited(status, signal);
- };
-
- SmallString<128> fileBuf{file};
- coptions.file = fileBuf.c_str();
- coptions.cwd = nullptr;
- coptions.flags = 0;
- coptions.uid = 0;
- coptions.gid = 0;
-
- SmallVector<char*, 4> argsBuf;
- SmallVector<char*, 4> envBuf;
- struct StdioContainer : public uv_stdio_container_t {
- StdioContainer() {
- flags = UV_IGNORE;
- data.fd = 0;
- }
- };
- SmallVector<StdioContainer, 4> stdioBuf;
-
- for (auto&& o : options) {
- switch (o.m_type) {
- case Option::kArg:
- argsBuf.push_back(const_cast<char*>(o.m_data.str));
- break;
- case Option::kEnv:
- envBuf.push_back(const_cast<char*>(o.m_data.str));
- break;
- case Option::kCwd:
- coptions.cwd = o.m_data.str[0] == '\0' ? nullptr : o.m_data.str;
- break;
- case Option::kUid:
- coptions.uid = o.m_data.uid;
- coptions.flags |= UV_PROCESS_SETUID;
- break;
- case Option::kGid:
- coptions.gid = o.m_data.gid;
- coptions.flags |= UV_PROCESS_SETGID;
- break;
- case Option::kSetFlags:
- coptions.flags |= o.m_data.flags;
- break;
- case Option::kClearFlags:
- coptions.flags &= ~o.m_data.flags;
- break;
- case Option::kStdioIgnore: {
- size_t index = o.m_data.stdio.index;
- if (index >= stdioBuf.size()) {
- stdioBuf.resize(index + 1);
- }
- stdioBuf[index].flags = UV_IGNORE;
- stdioBuf[index].data.fd = 0;
- break;
- }
- case Option::kStdioInheritFd: {
- size_t index = o.m_data.stdio.index;
- if (index >= stdioBuf.size()) {
- stdioBuf.resize(index + 1);
- }
- stdioBuf[index].flags = UV_INHERIT_FD;
- stdioBuf[index].data.fd = o.m_data.stdio.fd;
- break;
- }
- case Option::kStdioInheritPipe: {
- size_t index = o.m_data.stdio.index;
- if (index >= stdioBuf.size()) {
- stdioBuf.resize(index + 1);
- }
- stdioBuf[index].flags = UV_INHERIT_STREAM;
- stdioBuf[index].data.stream = o.m_data.stdio.pipe->GetRawStream();
- break;
- }
- case Option::kStdioCreatePipe: {
- size_t index = o.m_data.stdio.index;
- if (index >= stdioBuf.size()) {
- stdioBuf.resize(index + 1);
- }
- stdioBuf[index].flags =
- static_cast<uv_stdio_flags>(UV_CREATE_PIPE | o.m_data.stdio.flags);
- stdioBuf[index].data.stream = o.m_data.stdio.pipe->GetRawStream();
- break;
- }
- default:
- break;
- }
- }
-
- if (argsBuf.empty()) {
- argsBuf.push_back(const_cast<char*>(coptions.file));
- }
- argsBuf.push_back(nullptr);
- coptions.args = argsBuf.data();
-
- if (envBuf.empty()) {
- coptions.env = nullptr;
- } else {
- envBuf.push_back(nullptr);
- coptions.env = envBuf.data();
- }
-
- coptions.stdio_count = stdioBuf.size();
- coptions.stdio = static_cast<uv_stdio_container_t*>(stdioBuf.data());
-
- auto h = std::make_shared<Process>(private_init{});
- int err = uv_spawn(loop.GetRaw(), h->GetRaw(), &coptions);
- if (err < 0) {
- loop.ReportError(err);
- return nullptr;
- }
- h->Keep();
- return h;
-}
-
-} // namespace wpi::uv
diff --git a/wpiutil/src/main/native/cpp/uv/Signal.cpp b/wpiutil/src/main/native/cpp/uv/Signal.cpp
deleted file mode 100644
index 81d7c3e..0000000
--- a/wpiutil/src/main/native/cpp/uv/Signal.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (c) FIRST and other WPILib contributors.
-// Open Source Software; you can modify and/or share it under the terms of
-// the WPILib BSD license file in the root directory of this project.
-
-#include "wpi/uv/Signal.h"
-
-#include "wpi/uv/Loop.h"
-
-namespace wpi::uv {
-
-std::shared_ptr<Signal> Signal::Create(Loop& loop) {
- auto h = std::make_shared<Signal>(private_init{});
- int err = uv_signal_init(loop.GetRaw(), h->GetRaw());
- if (err < 0) {
- loop.ReportError(err);
- return nullptr;
- }
- h->Keep();
- return h;
-}
-
-void Signal::Start(int signum) {
- Invoke(
- &uv_signal_start, GetRaw(),
- [](uv_signal_t* handle, int signum) {
- Signal& h = *static_cast<Signal*>(handle->data);
- h.signal(signum);
- },
- signum);
-}
-
-} // namespace wpi::uv
diff --git a/wpiutil/src/main/native/cpp/uv/Stream.cpp b/wpiutil/src/main/native/cpp/uv/Stream.cpp
deleted file mode 100644
index a37750b..0000000
--- a/wpiutil/src/main/native/cpp/uv/Stream.cpp
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright (c) FIRST and other WPILib contributors.
-// Open Source Software; you can modify and/or share it under the terms of
-// the WPILib BSD license file in the root directory of this project.
-
-#include "wpi/uv/Stream.h"
-
-#include "wpi/SmallVector.h"
-
-using namespace wpi;
-using namespace wpi::uv;
-
-namespace {
-class CallbackWriteReq : public WriteReq {
- public:
- CallbackWriteReq(span<const Buffer> bufs,
- std::function<void(span<Buffer>, Error)> callback)
- : m_bufs{bufs.begin(), bufs.end()} {
- finish.connect(
- [this, f = std::move(callback)](Error err) { f(m_bufs, err); });
- }
-
- private:
- SmallVector<Buffer, 4> m_bufs;
-};
-} // namespace
-
-namespace wpi::uv {
-
-ShutdownReq::ShutdownReq() {
- error = [this](Error err) { GetStream().error(err); };
-}
-
-WriteReq::WriteReq() {
- error = [this](Error err) { GetStream().error(err); };
-}
-
-void Stream::Shutdown(const std::shared_ptr<ShutdownReq>& req) {
- if (Invoke(&uv_shutdown, req->GetRaw(), GetRawStream(),
- [](uv_shutdown_t* req, int status) {
- auto& h = *static_cast<ShutdownReq*>(req->data);
- if (status < 0) {
- h.ReportError(status);
- } else {
- h.complete();
- }
- h.Release(); // this is always a one-shot
- })) {
- req->Keep();
- }
-}
-
-void Stream::Shutdown(std::function<void()> callback) {
- auto req = std::make_shared<ShutdownReq>();
- if (callback) {
- req->complete.connect(std::move(callback));
- }
- Shutdown(req);
-}
-
-void Stream::StartRead() {
- Invoke(&uv_read_start, GetRawStream(), &Handle::AllocBuf,
- [](uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) {
- auto& h = *static_cast<Stream*>(stream->data);
- Buffer data = *buf;
-
- // nread=0 is simply ignored
- if (nread == UV_EOF) {
- h.end();
- } else if (nread > 0) {
- h.data(data, static_cast<size_t>(nread));
- } else if (nread < 0) {
- h.ReportError(nread);
- }
-
- // free the buffer
- h.FreeBuf(data);
- });
-}
-
-void Stream::Write(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);
- }
- h.finish(Error(status));
- h.Release(); // this is always a one-shot
- })) {
- req->Keep();
- }
-}
-
-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(span<const Buffer> bufs) {
- int val = uv_try_write(GetRawStream(), bufs.data(), bufs.size());
- if (val < 0) {
- this->ReportError(val);
- return 0;
- }
- return val;
-}
-
-} // namespace wpi::uv
diff --git a/wpiutil/src/main/native/cpp/uv/Tcp.cpp b/wpiutil/src/main/native/cpp/uv/Tcp.cpp
deleted file mode 100644
index 6f92557..0000000
--- a/wpiutil/src/main/native/cpp/uv/Tcp.cpp
+++ /dev/null
@@ -1,170 +0,0 @@
-// Copyright (c) FIRST and other WPILib contributors.
-// Open Source Software; you can modify and/or share it under the terms of
-// the WPILib BSD license file in the root directory of this project.
-
-#include "wpi/uv/Tcp.h"
-
-#include <cstring>
-
-#include "wpi/uv/util.h"
-
-namespace wpi::uv {
-
-std::shared_ptr<Tcp> Tcp::Create(Loop& loop, unsigned int flags) {
- auto h = std::make_shared<Tcp>(private_init{});
- int err = uv_tcp_init_ex(loop.GetRaw(), h->GetRaw(), flags);
- if (err < 0) {
- loop.ReportError(err);
- return nullptr;
- }
- h->Keep();
- return h;
-}
-
-void Tcp::Reuse(std::function<void()> callback, unsigned int flags) {
- if (IsClosing()) {
- return;
- }
- if (!m_reuseData) {
- m_reuseData = std::make_unique<ReuseData>();
- }
- m_reuseData->callback = 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
- }
- auto data = std::move(h.m_reuseData);
- int err = uv_tcp_init_ex(h.GetLoopRef().GetRaw(), h.GetRaw(), data->flags);
- if (err < 0) {
- h.ReportError(err);
- return;
- }
- data->callback();
- });
-}
-
-std::shared_ptr<Tcp> Tcp::Accept() {
- auto client = Create(GetLoopRef());
- if (!client) {
- return nullptr;
- }
- if (!Accept(client)) {
- client->Release();
- return nullptr;
- }
- return client;
-}
-
-Tcp* Tcp::DoAccept() {
- return Accept().get();
-}
-
-void Tcp::Bind(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) {
- ReportError(err);
- } else {
- Bind(reinterpret_cast<const sockaddr&>(addr), flags);
- }
-}
-
-sockaddr_storage Tcp::GetSock() {
- sockaddr_storage name;
- int len = sizeof(name);
- if (!Invoke(&uv_tcp_getsockname, GetRaw(), reinterpret_cast<sockaddr*>(&name),
- &len)) {
- std::memset(&name, 0, sizeof(name));
- }
- return name;
-}
-
-sockaddr_storage Tcp::GetPeer() {
- sockaddr_storage name;
- int len = sizeof(name);
- if (!Invoke(&uv_tcp_getpeername, GetRaw(), reinterpret_cast<sockaddr*>(&name),
- &len)) {
- std::memset(&name, 0, sizeof(name));
- }
- return name;
-}
-
-void Tcp::Connect(const sockaddr& addr,
- const std::shared_ptr<TcpConnectReq>& req) {
- if (Invoke(&uv_tcp_connect, req->GetRaw(), GetRaw(), &addr,
- [](uv_connect_t* req, int status) {
- auto& h = *static_cast<TcpConnectReq*>(req->data);
- if (status < 0) {
- h.ReportError(status);
- } else {
- h.connected();
- }
- h.Release(); // this is always a one-shot
- })) {
- req->Keep();
- }
-}
-
-void Tcp::Connect(const sockaddr& addr, std::function<void()> callback) {
- auto req = std::make_shared<TcpConnectReq>();
- req->connected.connect(std::move(callback));
- Connect(addr, req);
-}
-
-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) {
- ReportError(err);
- } else {
- Connect(reinterpret_cast<const sockaddr&>(addr), req);
- }
-}
-
-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) {
- ReportError(err);
- } else {
- Connect(reinterpret_cast<const sockaddr&>(addr), std::move(callback));
- }
-}
-
-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) {
- ReportError(err);
- } else {
- Connect(reinterpret_cast<const sockaddr&>(addr), req);
- }
-}
-
-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) {
- ReportError(err);
- } else {
- Connect(reinterpret_cast<const sockaddr&>(addr), std::move(callback));
- }
-}
-
-} // namespace wpi::uv
diff --git a/wpiutil/src/main/native/cpp/uv/Timer.cpp b/wpiutil/src/main/native/cpp/uv/Timer.cpp
deleted file mode 100644
index 33fd851..0000000
--- a/wpiutil/src/main/native/cpp/uv/Timer.cpp
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (c) FIRST and other WPILib contributors.
-// Open Source Software; you can modify and/or share it under the terms of
-// the WPILib BSD license file in the root directory of this project.
-
-#include "wpi/uv/Timer.h"
-
-#include "wpi/uv/Loop.h"
-
-namespace wpi::uv {
-
-std::shared_ptr<Timer> Timer::Create(Loop& loop) {
- auto h = std::make_shared<Timer>(private_init{});
- int err = uv_timer_init(loop.GetRaw(), h->GetRaw());
- if (err < 0) {
- loop.ReportError(err);
- return nullptr;
- }
- h->Keep();
- return h;
-}
-
-void Timer::SingleShot(Loop& loop, Time timeout, std::function<void()> func) {
- auto h = Create(loop);
- if (!h) {
- return;
- }
- h->timeout.connect([theTimer = h.get(), f = std::move(func)]() {
- f();
- theTimer->Close();
- });
- h->Start(timeout);
-}
-
-void Timer::Start(Time timeout, Time repeat) {
- Invoke(
- &uv_timer_start, GetRaw(),
- [](uv_timer_t* handle) {
- Timer& h = *static_cast<Timer*>(handle->data);
- h.timeout();
- },
- timeout.count(), repeat.count());
-}
-
-} // namespace wpi::uv
diff --git a/wpiutil/src/main/native/cpp/uv/Tty.cpp b/wpiutil/src/main/native/cpp/uv/Tty.cpp
deleted file mode 100644
index 4531ded..0000000
--- a/wpiutil/src/main/native/cpp/uv/Tty.cpp
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright (c) FIRST and other WPILib contributors.
-// Open Source Software; you can modify and/or share it under the terms of
-// the WPILib BSD license file in the root directory of this project.
-
-#include "wpi/uv/Tty.h"
-
-#include "wpi/uv/Loop.h"
-
-namespace wpi::uv {
-
-std::shared_ptr<Tty> Tty::Create(Loop& loop, uv_file fd, bool readable) {
- auto h = std::make_shared<Tty>(private_init{});
- int err = uv_tty_init(loop.GetRaw(), h->GetRaw(), fd, readable ? 1 : 0);
- if (err < 0) {
- loop.ReportError(err);
- return nullptr;
- }
- h->Keep();
- return h;
-}
-
-} // namespace wpi::uv
diff --git a/wpiutil/src/main/native/cpp/uv/Udp.cpp b/wpiutil/src/main/native/cpp/uv/Udp.cpp
deleted file mode 100644
index bea2b57..0000000
--- a/wpiutil/src/main/native/cpp/uv/Udp.cpp
+++ /dev/null
@@ -1,184 +0,0 @@
-// Copyright (c) FIRST and other WPILib contributors.
-// Open Source Software; you can modify and/or share it under the terms of
-// the WPILib BSD license file in the root directory of this project.
-
-#include "wpi/uv/Udp.h"
-
-#include <cstring>
-
-#include "wpi/SmallString.h"
-#include "wpi/SmallVector.h"
-#include "wpi/uv/util.h"
-
-namespace {
-
-using namespace wpi;
-using namespace wpi::uv;
-
-class CallbackUdpSendReq : public UdpSendReq {
- public:
- CallbackUdpSendReq(span<const Buffer> bufs,
- std::function<void(span<Buffer>, Error)> callback)
- : m_bufs{bufs.begin(), bufs.end()} {
- complete.connect(
- [this, f = std::move(callback)](Error err) { f(m_bufs, err); });
- }
-
- private:
- SmallVector<Buffer, 4> m_bufs;
-};
-
-} // namespace
-
-namespace wpi::uv {
-
-UdpSendReq::UdpSendReq() {
- error = [this](Error err) { GetUdp().error(err); };
-}
-
-std::shared_ptr<Udp> Udp::Create(Loop& loop, unsigned int flags) {
- auto h = std::make_shared<Udp>(private_init{});
- int err = uv_udp_init_ex(loop.GetRaw(), h->GetRaw(), flags);
- if (err < 0) {
- loop.ReportError(err);
- return nullptr;
- }
- h->Keep();
- return h;
-}
-
-void Udp::Bind(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 Udp::Bind6(std::string_view ip, unsigned int port, unsigned int flags) {
- sockaddr_in6 addr;
- int err = NameToAddr(ip, port, &addr);
- if (err < 0) {
- ReportError(err);
- } else {
- Bind(reinterpret_cast<const sockaddr&>(addr), flags);
- }
-}
-
-void Udp::Connect(std::string_view ip, unsigned int port) {
- sockaddr_in addr;
- int err = NameToAddr(ip, port, &addr);
- if (err < 0) {
- ReportError(err);
- } else {
- Connect(reinterpret_cast<const sockaddr&>(addr));
- }
-}
-
-void Udp::Connect6(std::string_view ip, unsigned int port) {
- sockaddr_in6 addr;
- int err = NameToAddr(ip, port, &addr);
- if (err < 0) {
- ReportError(err);
- } else {
- Connect(reinterpret_cast<const sockaddr&>(addr));
- }
-}
-
-sockaddr_storage Udp::GetPeer() {
- sockaddr_storage name;
- int len = sizeof(name);
- if (!Invoke(&uv_udp_getpeername, GetRaw(), reinterpret_cast<sockaddr*>(&name),
- &len)) {
- std::memset(&name, 0, sizeof(name));
- }
- return name;
-}
-
-sockaddr_storage Udp::GetSock() {
- sockaddr_storage name;
- int len = sizeof(name);
- if (!Invoke(&uv_udp_getsockname, GetRaw(), reinterpret_cast<sockaddr*>(&name),
- &len)) {
- std::memset(&name, 0, sizeof(name));
- }
- return name;
-}
-
-void Udp::SetMembership(std::string_view multicastAddr,
- std::string_view interfaceAddr,
- uv_membership 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(std::string_view interfaceAddr) {
- SmallString<128> interfaceAddrBuf{interfaceAddr};
- Invoke(&uv_udp_set_multicast_interface, GetRaw(), interfaceAddrBuf.c_str());
-}
-
-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);
- }
- h.complete(Error(status));
- h.Release(); // this is always a one-shot
- })) {
- req->Keep();
- }
-}
-
-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(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);
- }
- h.complete(Error(status));
- h.Release(); // this is always a one-shot
- })) {
- req->Keep();
- }
-}
-
-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() {
- Invoke(&uv_udp_recv_start, GetRaw(), &AllocBuf,
- [](uv_udp_t* handle, ssize_t nread, const uv_buf_t* buf,
- const sockaddr* addr, unsigned flags) {
- auto& h = *static_cast<Udp*>(handle->data);
- Buffer data = *buf;
-
- // nread=0 is simply ignored
- if (nread > 0) {
- h.received(data, static_cast<size_t>(nread), *addr, flags);
- } else if (nread < 0) {
- h.ReportError(nread);
- }
-
- // free the buffer
- h.FreeBuf(data);
- });
-}
-
-} // namespace wpi::uv
diff --git a/wpiutil/src/main/native/cpp/uv/Work.cpp b/wpiutil/src/main/native/cpp/uv/Work.cpp
deleted file mode 100644
index 0fc254e..0000000
--- a/wpiutil/src/main/native/cpp/uv/Work.cpp
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (c) FIRST and other WPILib contributors.
-// Open Source Software; you can modify and/or share it under the terms of
-// the WPILib BSD license file in the root directory of this project.
-
-#include "wpi/uv/Work.h"
-
-#include "wpi/uv/Loop.h"
-
-namespace wpi::uv {
-
-WorkReq::WorkReq() {
- error = [this](Error err) { GetLoop().error(err); };
-}
-
-void QueueWork(Loop& loop, const std::shared_ptr<WorkReq>& req) {
- int err = uv_queue_work(
- loop.GetRaw(), req->GetRaw(),
- [](uv_work_t* req) {
- auto& h = *static_cast<WorkReq*>(req->data);
- h.work();
- },
- [](uv_work_t* req, int status) {
- auto& h = *static_cast<WorkReq*>(req->data);
- if (status < 0) {
- h.ReportError(status);
- } else {
- h.afterWork();
- }
- h.Release(); // this is always a one-shot
- });
- if (err < 0) {
- loop.ReportError(err);
- } else {
- req->Keep();
- }
-}
-
-void QueueWork(Loop& loop, std::function<void()> work,
- std::function<void()> afterWork) {
- auto req = std::make_shared<WorkReq>();
- if (work) {
- req->work.connect(std::move(work));
- }
- if (afterWork) {
- req->afterWork.connect(std::move(afterWork));
- }
- QueueWork(loop, req);
-}
-
-} // namespace wpi::uv