feat: add display UI with splash screen, dashboard, and RSSI sparkline
Implement full SSD1306 OLED display support: - Splash screen on boot (1.5s) with project name, version, board - Dashboard showing radio state, config, counters, RSSI/SNR - RSSI history sparkline (64-sample ring buffer, 2px-wide bars) - DisplayOn/DisplayOff host commands routed via dedicated channel - Pure render functions against generic DrawTarget (no hardware coupling) - Display on/off control bypasses radio task entirely Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
17624 files changed
+4564928
-38
No files matched your search
+1
@@ -0,0 +1 @@
|
||||
{"files":{".gitlab-ci.yml":"14fd0fb0f07ef573385136519c5c55d80a667de176fca9c2105db3cfa02f71b4","CHANGELOG.md":"9e8c30d623942564eed5c8aa899a0228e404020f91e45eb8adc39f94b3c8b3ef","CONTRIBUTING.md":"ecfef03eafb0017f536bc02512d02bf6d8a7000d1513627a9e1a04475931fba3","Cargo.toml":"23cb4a42b1d39409730cf0959906612a45b6ec9cbef4e62bbdfe55efa21885b5","LICENSE-APACHE":"b40930bbcf80744c86c46a12bc9da056641d722716c378f5659b9e555ef833e1","LICENSE-MIT":"3eb5be24f096e481085210033dcb221e86a77f2cd4f146dd79cf8b3ee7603998","README.md":"6cd0b211fbe5fdc495b8ea395d5048dd142bf252208abe23a3f804a185cda2ac","src/lib.rs":"3f625fe1bbf60ce76c81ea9fd34f51a33c4cdc5fe104f856daf6083dd2f74289","tests/roundtrip.rs":"6dca86566ec595c6669aee8a52a1263f581ee83e9d5f8ee03e35cb3afc33309f"},"package":"f3e6ad23b128192ed337dfa4f1b8099ced0c2bf30d61e551b65fda5916dbb850"}
|
||||
Vendored
+26
@@ -0,0 +1,26 @@
|
||||
build:
|
||||
image: registry.gitlab.com/robigalia/runner:latest
|
||||
cache:
|
||||
key: $CACHE_KEY
|
||||
paths:
|
||||
- /root/.cargo
|
||||
script:
|
||||
- "xargo build --target i686-sel4-robigalia --no-default-features"
|
||||
- "xargo build --target arm-sel4-robigalia --no-default-features"
|
||||
- "cargo build --no-default-features"
|
||||
- "cargo test"
|
||||
- "cargo test --release"
|
||||
# - "kcov --exclude-pattern=/.cargo,/usr/lib --verify target/cov/ target/debug/roundtrip-*[^\\.d]"
|
||||
# - "bash <(curl -s https://codecov.io/bash) -t 84321081-ebe2-447a-bdf1-a69eee2bdcf7"
|
||||
# - echo "Percent coverage: $(jq .percent_covered target/cov/rn-*/coverage.json)%"
|
||||
|
||||
verify_signatures:
|
||||
image: ubuntu:17.04
|
||||
script:
|
||||
- apt update
|
||||
- apt install --assume-yes python-pexpect gnupg2 git
|
||||
- git clone --depth=1 https://gitlab.com/robigalia/meta
|
||||
- gpg2 --list-keys # to force it to create GNUPGHOME instead of racing in the pipe below
|
||||
- (gpg2 --export --no-default-keyring --keyring ./meta/.gnupg/pubring.kbx || true) | gpg2 --import
|
||||
- gpg2 --list-keys --fingerprint --fingerprint --verbose
|
||||
- ./meta/verify-signatures.py HEAD ./meta/.mailmap
|
||||
Vendored
+17
@@ -0,0 +1,17 @@
|
||||
# `ssmarshal` changelog
|
||||
|
||||
This project follows semantic versioning.
|
||||
|
||||
## v1.0.0 (2017-05-13)
|
||||
|
||||
- [added] `cargo-fuzz` based fuzz testing
|
||||
- [added] random datatype-based testing
|
||||
- [removed] afl.rs based fuzz testing
|
||||
- [changed] expanded quickcheck-based testing to more interesting types
|
||||
- [changed] Ported to serde 1.0
|
||||
- [removed] Support for serializing values which can't be deserialized (slices etc)
|
||||
- [fixed] Documented the high-level details of the format and limitations.
|
||||
|
||||
## v0.0.1 (2017-01-03)
|
||||
|
||||
- First release.
|
||||
Vendored
+187
@@ -0,0 +1,187 @@
|
||||
Thank you for your interest in contributing to the Robigalia project! There
|
||||
are many ways to contribute, and we appreciate all of them.
|
||||
|
||||
If you have questions, please send an email to
|
||||
[robigalia-dev@lists.robigalia.org][list] or hop on [#robigalia][irc].
|
||||
|
||||
As a reminder, all participants in our community are expected to honor our
|
||||
[code of conduct][conduct].
|
||||
|
||||
[list]: mailto:robigalia-dev@lists.robigalia.org
|
||||
[irc]: https://webchat.freenode.net/?channels=%23robigalia
|
||||
[conduct]: https://robigalia.org/conduct.html
|
||||
|
||||
## Feature Proposal
|
||||
|
||||
To propose a change to the way that Robigo or any other Robigalia codebase
|
||||
works, please open an issue directly on the relevant repository. Include the
|
||||
text, `[feature-proposal]` in the title and someone will come by and re-tag
|
||||
the issue appropriately. Here's a template you can use to file a feature
|
||||
proposal, though it's not necessary to use it exactly:
|
||||
|
||||
```
|
||||
One-paragraph summary
|
||||
|
||||
# Details
|
||||
|
||||
As many details as you think are necessary to communicate how the feature
|
||||
should work.
|
||||
|
||||
# Benefits
|
||||
|
||||
Benefits of the feature.
|
||||
|
||||
# Drawbacks
|
||||
|
||||
Drawbacks of the feature.
|
||||
```
|
||||
|
||||
There is [an example feature proposal][prop] if you want to see what it looks
|
||||
like.
|
||||
|
||||
[prop]: https://gitlab.com/robigalia/meta/issues/1
|
||||
|
||||
## Bug Reports
|
||||
|
||||
While bugs are unfortunate, they're a reality in software. We can't fix what we
|
||||
don't know about, so please report liberally. If you're not sure if something
|
||||
is a bug or not, feel free to file a bug anyway.
|
||||
|
||||
**If you believe reporting your bug publicly represents a security risk to
|
||||
users of software developed by the Robigalia project,
|
||||
please follow our [instructions for reporting security
|
||||
vulnerabilities](https://robigalia.org/security.html)**.
|
||||
|
||||
If you have the chance, before reporting a bug, please [search existing
|
||||
issues](https://gitlab.com/groups/robigalia/issues) as it's possible that
|
||||
someone else has already reported your error. This doesn't always work, and
|
||||
sometimes it's hard to know what to search for, so consider this extra credit.
|
||||
We won't mind if you accidentally file a duplicate report, we'll typically
|
||||
close it and point out which issue is a duplicate of.
|
||||
|
||||
Opening an issue is as easy as following [this
|
||||
link](https://gitlab.com/groups/robigalia/issues) and filling out the fields
|
||||
after clicking "New Issue". Here's a template that you can use to file a bug,
|
||||
though it's not necessary to use it exactly:
|
||||
|
||||
<short summary of the bug>
|
||||
|
||||
I tried this:
|
||||
|
||||
<testcase demonstrating the bug>
|
||||
|
||||
I expected to see this happen: <explanation>
|
||||
|
||||
Instead, this happened: <explanation>
|
||||
|
||||
All three components are important: what you did, what you expected, what
|
||||
happened instead.
|
||||
|
||||
|
||||
## Submitting code
|
||||
|
||||
To coordinate changes to our software, we use a feature of GitLab called
|
||||
"merge requests". Alternatively, you can email patches (ideally with git
|
||||
send-email) to [the development mailing list][list].
|
||||
|
||||
### Commit messages
|
||||
|
||||
Commit messages should be self-contained and describe the motivation for a
|
||||
change. The subject line should be short, with any elaborations in the body.
|
||||
If the commit closes an issue, write "Closes #issuenumber" at the end of the
|
||||
body.
|
||||
|
||||
For some guidelines, read [this][chrisbeams] for the basics, and [this][pro]
|
||||
to become a pro.
|
||||
|
||||
[chrisbeams]: http://chris.beams.io/posts/git-commit/
|
||||
[pro]: https://robots.thoughtbot.com/5-useful-tips-for-a-better-commit-message
|
||||
|
||||
### Commit signing
|
||||
|
||||
For a [variety of reasons][horror], we use commit signing to verify the
|
||||
authenticity and authorship of changes to our projects. If you don't already
|
||||
have one, create a PGP key. There are many ways to do this. [This blog
|
||||
post][keygen] by Alex Cabal has a decent explanation of how to do it and some
|
||||
of the tradeoffs involved.
|
||||
|
||||
Once you have a keypair and it is uploaded to the keyservers, [learn how to
|
||||
use gpg with git][horror2]. Please sign all commits you intend to include in a
|
||||
merge request.
|
||||
|
||||
[horror]: https://mikegerwitz.com/papers/git-horror-story
|
||||
[horror2]: https://mikegerwitz.com/papers/git-horror-story#trust-ensure
|
||||
[keygen]: https://alexcabal.com/creating-the-perfect-gpg-keypair/
|
||||
|
||||
### Forking the repositories.
|
||||
|
||||
We use a custom system for managing our large amount of repositories. We have
|
||||
one "master" repository, [devbox][devbox], which contains scripts for fetching
|
||||
the other repositories. It also includes a [Vagrant][vagrant] configuration,
|
||||
which is the easiest way to get started with Rust and seL4. See the readme in
|
||||
that repository to see how to use the scripts and, optionally, set up Vagrant.
|
||||
Part of the setup is running the `add_remotes.sh` script, which will rename
|
||||
the official repo to `upstream` and add an `origin` remote pointing to a repo
|
||||
under your own username.
|
||||
|
||||
To fork a repository, navigate to it on GitLab and click the "Fork project"
|
||||
button", which is to the left of the repository URL:
|
||||
|
||||
![fork project button location image][forkimg]
|
||||
|
||||
Then, you can interact with that repository using the `origin` remote in the
|
||||
relevant sub-repository of `devbox`.
|
||||
|
||||
[devbox]: https://gitlab.com/robigalia/devbox
|
||||
[vagrant]: https://www.vagrantup.com/
|
||||
[forkimg]: https://gitlab.com/robigalia/meta/raw/master/fork_button.png
|
||||
|
||||
### Signing the Developer Certificate of Origin
|
||||
|
||||
Before we can accept code you have written into our repositories, you must
|
||||
agree to the Developers Certificate of Origin. You can view it [in this
|
||||
repository][DCO]. It is quite minimalist, and is also used by the Linux
|
||||
kernel. To do this, fork the meta repository and run a command like:
|
||||
|
||||
gpg --sign --armor < DCO.txt > agreements/${your_gitlab_username}
|
||||
|
||||
Create a new commit with that file and send a merge request.
|
||||
|
||||
[DCO]: https://gitlab.com/robigalia/meta/blob/master/DCO.txt
|
||||
|
||||
### Submitting a merge request
|
||||
|
||||
To submit a merge request, push the commits you want to include to a branch on
|
||||
your fork of the relevant repository. Refer to the [official GitLab
|
||||
documentation][mergereq] to see how to proceed from there.
|
||||
|
||||
Please open merge requests against the `master` branch. If needed, we will
|
||||
merge into a separate branch manually.
|
||||
|
||||
[mergereq]: http://doc.gitlab.com/ce/gitlab-basics/add-merge-request.html
|
||||
|
||||
All merge requests are reviewed by another person.
|
||||
|
||||
If you want to request that a specific person reviews your pull request, you
|
||||
can specify the "assignee" when you create the pull request. For example,
|
||||
Corey usually reviews changes to sel4-sys, so you could specify him as the
|
||||
assignee if you send a merge request to sel4-sys.
|
||||
|
||||
## Writing Documentation
|
||||
|
||||
Documentation improvements are very welcome. The source of robigalia.org is
|
||||
in its [own repository][web], and individual crate documentation is hosted on
|
||||
[doc.robigalia.org](https://doc.robigalia.org).
|
||||
|
||||
Documentation merge requests function in the same way as other merge requests.
|
||||
|
||||
[web]: https://gitlab.com/robigalia/robigalia.org
|
||||
|
||||
## Helpful Links and Information
|
||||
|
||||
- The [seL4 website](https://sel4.systems/) has things like the manual and how
|
||||
to build seL4.
|
||||
- The [Rust website](https://rust-lang.org/) for all your Rust-related
|
||||
questions.
|
||||
|
||||
*This document adapted from <https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md>*
|
||||
Vendored
+28
@@ -0,0 +1,28 @@
|
||||
# Copyright (c) 2017 The Robigalia Project Developers
|
||||
# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT
|
||||
# or http://opensource.org/licenses/MIT>, at your option. All files in the
|
||||
# project carrying such notice may not be copied, modified, or distributed
|
||||
# except according to those terms.
|
||||
[package]
|
||||
name = "ssmarshal"
|
||||
version = "1.0.0"
|
||||
authors = ["Corey Richardson <corey@octayn.net>"]
|
||||
description = "Stupid simple value-only marshaling using serde"
|
||||
documentation = "https://docs.rs/ssmarshal"
|
||||
repository = "https://gitlab.com/robigalia/ssmarshal"
|
||||
readme = "README.md"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = ["serde/std"]
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1.0", default-features = false }
|
||||
encode_unicode = { version = "0.3", default-features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
quickcheck = "0.4"
|
||||
quickcheck_macros = "0.4"
|
||||
serde_derive = "1.0"
|
||||
Vendored
+201
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
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.
|
||||
Vendored
+25
@@ -0,0 +1,25 @@
|
||||
Copyright (c) 2015 The Robigalia Developers
|
||||
|
||||
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.
|
||||
Vendored
+69
@@ -0,0 +1,69 @@
|
||||
# ssmarshal
|
||||
|
||||
[](https://crates.io/crates/ssmarshal)
|
||||
|
||||
[Documentation](https://docs.rs/ssmarshal)
|
||||
|
||||
ssmarshal ("stupid simple marshaling") is a serde-based de/serialization
|
||||
library. It is somewhat like [bincode](https://github.com/TyOverby/bincode),
|
||||
but doesn't support String/Vec - this library is entirely zero-allocation, for
|
||||
use in extremely limited `no_std` contexts. The key invariant is that the
|
||||
encoding space for any value of a type is `<= size_of::<T>()`. This allows
|
||||
easy reasoning about limited buffer sizes, and how much is always enough.
|
||||
|
||||
## Limitations
|
||||
|
||||
These sorts of types are not supported:
|
||||
|
||||
- Any non-core type (eg, `Vec`, `HashMap`)
|
||||
- Slices
|
||||
- Strings
|
||||
- Types containing references or pointers
|
||||
- Enums with more than 256 variants
|
||||
|
||||
All enums MUST be `#[repr(C)]` in order for the size invariant to be upheld.
|
||||
Note that this excludes using `Option`, especially with `NonZero` types!
|
||||
You can use this crate with non-`#[repr(C)]` enums, but you should thoroughly
|
||||
test (I recommend quickcheck, see [the tests](https://gitlab.com/robigalia/ssmarshal/blob/master/tests/roundtrip.rs)
|
||||
for an example) de/serializing values of that type to be assured the size
|
||||
invariant holds for the type.
|
||||
|
||||
## Details of the format
|
||||
|
||||
The format is not incredibly compact, but doesn't add extra fluff, and is
|
||||
quick to en/decode.
|
||||
|
||||
- `bool` is serialized as a byte, 1 if true, 0 is false.
|
||||
- the integer types are encoded in their little-endian form.
|
||||
- f32 is bitcast to a u32 then encoded as a u32. likewise, f64 and u64.
|
||||
- inhabited enums are serialized as 1 byte for the discriminant, and then the fields.
|
||||
- structs are serialized as just their fields.
|
||||
- the unit type and uninhabited enums are not serialized at all.
|
||||
- tuples are serialized as the fields, in order.
|
||||
|
||||
There is no padding.
|
||||
|
||||
As you might see, this format is not self-describing. To successfully
|
||||
deserialize a value, the exact layout must be known ahead-of-time.
|
||||
|
||||
## Alternatives
|
||||
|
||||
This is designed for doing IPC in a microkernel, with a stable ABI, not saving
|
||||
to disk or transferring over the network. It may be useful for those cases,
|
||||
although you'll likely want a format which can handle data evolution, like
|
||||
Cap'n Proto.
|
||||
|
||||
If you care about truly minimizing encoding space, you might look into ASN.1
|
||||
PER.
|
||||
|
||||
If you need more features (for example, slices or references), but still don't
|
||||
care about data evolution, I recommend using bincode.
|
||||
|
||||
## Testing
|
||||
|
||||
The `roundtrip` test suite exercises almost all of the functionality of
|
||||
ssmarshal, with coverage over 95% (missing mostly some of the error cases for
|
||||
invalid/unsupported types).
|
||||
|
||||
This library is extensively fuzz tested with `cargo-fuzz` (libFuzzer) before
|
||||
every release. See the `fuzz` directory for the scripts used.
|
||||
Vendored
+784
@@ -0,0 +1,784 @@
|
||||
// Copyright (c) 2017 The Robigalia Project Developers Licensed under the Apache License, Version
|
||||
// 2.0 <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option. All files in the project
|
||||
// carrying such notice may not be copied, modified, or distributed except according to those
|
||||
// terms.
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
extern crate core;
|
||||
|
||||
extern crate serde;
|
||||
extern crate encode_unicode;
|
||||
|
||||
use core::intrinsics::transmute;
|
||||
|
||||
use serde::{Serialize, Deserialize};
|
||||
use serde::de::{Visitor, DeserializeSeed, DeserializeOwned, IntoDeserializer};
|
||||
//use serde::de::value::ValueDeserializer;
|
||||
use encode_unicode::CharExt;
|
||||
use encode_unicode::Utf8Char;
|
||||
|
||||
use core::fmt::Display;
|
||||
|
||||
const NS: &'static str = "not support";
|
||||
|
||||
#[inline(never)]
|
||||
#[cold]
|
||||
fn ns<T>() -> Result<T, Error> {
|
||||
if cfg!(debug_assertions) {
|
||||
panic!(NS)
|
||||
}
|
||||
Err(Error::NotSupported)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
EndOfStream,
|
||||
InvalidRepresentation,
|
||||
MoreElements,
|
||||
TooManyVariants,
|
||||
NotSupported,
|
||||
ApplicationError(&'static str),
|
||||
#[cfg(not(feature = "std"))]
|
||||
Custom,
|
||||
#[cfg(feature = "std")]
|
||||
Custom(String),
|
||||
}
|
||||
|
||||
|
||||
impl core::fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
#[cfg(feature = "std")]
|
||||
use std::error::Error;
|
||||
|
||||
match self {
|
||||
&::Error::ApplicationError(s) => write!(f, "application error: {}", s),
|
||||
_ => f.write_str(self.description()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
impl serde::de::Error for Error {
|
||||
fn custom<T: Display>(_msg: T) -> Error {
|
||||
Error::Custom
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
impl serde::ser::Error for Error {
|
||||
fn custom<T: Display>(_msg: T) -> Error {
|
||||
Error::Custom
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl serde::de::Error for Error {
|
||||
fn custom<T: Display>(msg: T) -> Error {
|
||||
Error::Custom(format!("{}", msg))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl serde::ser::Error for Error {
|
||||
fn custom<T: Display>(msg: T) -> Error {
|
||||
Error::Custom(format!("{}", msg))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
impl Error {
|
||||
fn description(&self) -> &str {
|
||||
match self {
|
||||
&Error::EndOfStream => "end of stream reached but more data was needed",
|
||||
&Error::InvalidRepresentation => "invalid representation for a value",
|
||||
&Error::MoreElements => "there are more elements of the sequence remaining",
|
||||
&Error::TooManyVariants => "too many variants, only up to 256 are supported",
|
||||
&Error::NotSupported => "feature not supported",
|
||||
&Error::ApplicationError(s) => s,
|
||||
&Error::Custom => "some custom error that couldn't be reported",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl std::error::Error for Error {
|
||||
fn description(&self) -> &str {
|
||||
match self {
|
||||
&Error::EndOfStream => "end of stream reached but more data was needed",
|
||||
&Error::InvalidRepresentation => "invalid representation for a value",
|
||||
&Error::MoreElements => "there are more elements of the sequence remaining",
|
||||
&Error::TooManyVariants => "too many variants, only up to 256 are supported",
|
||||
&Error::NotSupported => "feature not supported",
|
||||
&Error::ApplicationError(s) => s,
|
||||
&Error::Custom(ref s) => &s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Serialize a value into a buffer. Returns the number of bytes used.
|
||||
pub fn serialize<T: Serialize>(buf: &mut [u8], val: &T) -> SerializeResult<usize> {
|
||||
let mut serializer = Serializer { buf: buf, idx: 0 };
|
||||
T::serialize(val, &mut serializer)?;
|
||||
debug_assert!(serializer.idx <= core::mem::size_of::<T>(), "{} <=? {}", serializer.idx, core::mem::size_of::<T>());
|
||||
Ok(serializer.idx)
|
||||
}
|
||||
|
||||
/// Deserialize a value from a buffer. Returns the number of bytes used.
|
||||
pub fn deserialize<T: DeserializeOwned>(buf: &[u8]) -> SerializeResult<(T, usize)> {
|
||||
let mut deserializer = Deserializer { buf: buf, idx: 0 };
|
||||
let val = T::deserialize(&mut deserializer)?;
|
||||
debug_assert!(deserializer.idx <= core::mem::size_of::<T>());
|
||||
Ok((val, deserializer.idx))
|
||||
}
|
||||
|
||||
struct Serializer<'a> {
|
||||
buf: &'a mut [u8],
|
||||
idx: usize,
|
||||
}
|
||||
|
||||
impl<'a> Serializer<'a> {
|
||||
#[inline]
|
||||
fn check_bounds(&self, len: usize) -> Result<(), Error> {
|
||||
if let Some(val) = self.idx.checked_add(len) {
|
||||
if val <= self.buf.len() {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
debug_assert!(false, "ran out of space serializing value; fix your buffer size");
|
||||
Err(Error::EndOfStream)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_u8(&mut self, val: u8) -> Result<(), Error> {
|
||||
self.check_bounds(1)?;
|
||||
unsafe {
|
||||
*self.buf.get_unchecked_mut(self.idx) = val;
|
||||
}
|
||||
self.idx += 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_u16(&mut self, val: u16) -> Result<(), Error> {
|
||||
self.check_bounds(2)?;
|
||||
unsafe {
|
||||
*self.buf.get_unchecked_mut(self.idx) = (val & 0xFF) as u8;
|
||||
*self.buf.get_unchecked_mut(self.idx + 1) = (val >> 8 & 0xFF) as u8;
|
||||
}
|
||||
self.idx += 2;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_u32(&mut self, val: u32) -> Result<(), Error> {
|
||||
self.check_bounds(4)?;
|
||||
unsafe {
|
||||
*self.buf.get_unchecked_mut(self.idx) = (val & 0xFF) as u8;
|
||||
*self.buf.get_unchecked_mut(self.idx + 1) = (val >> 8 & 0xFF) as u8;
|
||||
*self.buf.get_unchecked_mut(self.idx + 2) = (val >> 16 & 0xFF) as u8;
|
||||
*self.buf.get_unchecked_mut(self.idx + 3) = (val >> 24 & 0xFF) as u8;
|
||||
}
|
||||
self.idx += 4;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_u64(&mut self, val: u64) -> Result<(), Error> {
|
||||
self.check_bounds(8)?;
|
||||
unsafe {
|
||||
*self.buf.get_unchecked_mut(self.idx) = (val & 0xFF) as u8;
|
||||
*self.buf.get_unchecked_mut(self.idx + 1) = (val >> 8 & 0xFF) as u8;
|
||||
*self.buf.get_unchecked_mut(self.idx + 2) = (val >> 16 & 0xFF) as u8;
|
||||
*self.buf.get_unchecked_mut(self.idx + 3) = (val >> 24 & 0xFF) as u8;
|
||||
*self.buf.get_unchecked_mut(self.idx + 4) = (val >> 32 & 0xFF) as u8;
|
||||
*self.buf.get_unchecked_mut(self.idx + 5) = (val >> 40 & 0xFF) as u8;
|
||||
*self.buf.get_unchecked_mut(self.idx + 6) = (val >> 48 & 0xFF) as u8;
|
||||
*self.buf.get_unchecked_mut(self.idx + 7) = (val >> 56 & 0xFF) as u8;
|
||||
}
|
||||
self.idx += 8;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_usize(&mut self, val: usize) -> Result<(), Error> {
|
||||
self.write_u64(val as u64)
|
||||
}
|
||||
}
|
||||
|
||||
type SerializeResult<T> = Result<T, Error>;
|
||||
|
||||
impl<'b, 'a: 'b> serde::Serializer for &'b mut Serializer<'a> {
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
type SerializeSeq = Self;
|
||||
type SerializeTuple = Self;
|
||||
type SerializeTupleStruct = Self;
|
||||
type SerializeTupleVariant = Self;
|
||||
type SerializeMap = serde::ser::Impossible<(), Error>;
|
||||
type SerializeStruct = Self;
|
||||
type SerializeStructVariant = Self;
|
||||
|
||||
|
||||
#[inline]
|
||||
fn serialize_bool(self, v: bool) -> SerializeResult<()> {
|
||||
self.write_u8(if v { 1 } else { 0 })
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_u8(self, v: u8) -> SerializeResult<()> {
|
||||
self.write_u8(v)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_u16(self, v: u16) -> SerializeResult<()> {
|
||||
self.write_u16(v)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_u32(self, v: u32) -> SerializeResult<()> {
|
||||
self.write_u32(v)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_u64(self, v: u64) -> SerializeResult<()> {
|
||||
self.write_u64(v)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_i8(self, v: i8) -> SerializeResult<()> {
|
||||
self.write_u8(v as u8)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_i16(self, v: i16) -> SerializeResult<()> {
|
||||
self.write_u16(v as u16)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_i32(self, v: i32) -> SerializeResult<()> {
|
||||
self.write_u32(v as u32)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_i64(self, v: i64) -> SerializeResult<()> {
|
||||
self.write_u64(v as u64)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_f32(self, v: f32) -> SerializeResult<()> {
|
||||
self.write_u32(unsafe { transmute(v) })
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_f64(self, v: f64) -> SerializeResult<()> {
|
||||
self.write_u64(unsafe { transmute(v) })
|
||||
}
|
||||
|
||||
fn serialize_str(self, _: &str) -> SerializeResult<()> {
|
||||
ns()
|
||||
}
|
||||
|
||||
fn serialize_char(self, c: char) -> SerializeResult<()> {
|
||||
let (arr, sz) = c.to_utf8_array();
|
||||
self.check_bounds(sz)?;
|
||||
for (i, c) in arr[..sz].iter().enumerate() {
|
||||
unsafe {
|
||||
*self.buf.get_unchecked_mut(self.idx + i) = *c;
|
||||
}
|
||||
}
|
||||
self.idx += sz;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_bytes(self, _: &[u8]) -> SerializeResult<()> {
|
||||
ns()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_none(self) -> SerializeResult<()> {
|
||||
self.write_u8(0)
|
||||
}
|
||||
|
||||
fn serialize_some<T: Serialize + ?Sized>(self, v: &T) -> SerializeResult<()> {
|
||||
self.write_u8(1)?;
|
||||
v.serialize(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_unit(self) -> SerializeResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_unit_struct(self, _: &'static str) -> SerializeResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_unit_variant(self,
|
||||
_name: &'static str,
|
||||
variant_index: u32,
|
||||
_variant: &'static str)
|
||||
-> SerializeResult<()> {
|
||||
if variant_index > 255 {
|
||||
debug_assert!(false, "too many enum variants: {}", _name);
|
||||
return Err(Error::TooManyVariants)
|
||||
}
|
||||
self.write_u8(variant_index as u8)
|
||||
}
|
||||
|
||||
fn serialize_newtype_struct<T: Serialize + ?Sized>(self, _name: &'static str, value: &T) -> SerializeResult<()> {
|
||||
value.serialize(self)
|
||||
}
|
||||
|
||||
fn serialize_newtype_variant<T: Serialize + ?Sized>(self,
|
||||
_name: &'static str,
|
||||
variant_index: u32,
|
||||
_variant: &'static str,
|
||||
value: &T)
|
||||
-> SerializeResult<()> {
|
||||
if variant_index > 255 {
|
||||
debug_assert!(false, "too many enum variants: {}", _name);
|
||||
return Err(Error::TooManyVariants);
|
||||
}
|
||||
self.write_u8(variant_index as u8)?;
|
||||
value.serialize(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_seq(self, len: Option<usize>) -> SerializeResult<Self> {
|
||||
match len {
|
||||
None => {
|
||||
ns()
|
||||
},
|
||||
Some(len) => {
|
||||
self.write_usize(len)?;
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_tuple(self, _len: usize) -> SerializeResult<Self> {
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_tuple_struct(self, _name: &'static str, _len: usize) -> SerializeResult<Self> {
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_tuple_variant(self,
|
||||
_name: &'static str,
|
||||
variant_index: u32,
|
||||
_variant: &'static str,
|
||||
_len: usize)
|
||||
-> SerializeResult<Self> {
|
||||
if variant_index > 255 {
|
||||
debug_assert!(false, "too many enum variants: {}", _name);
|
||||
return Err(Error::TooManyVariants);
|
||||
}
|
||||
self.write_u8(variant_index as u8)?;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn serialize_map(self, _len: Option<usize>) -> SerializeResult<Self::SerializeMap> {
|
||||
ns()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_struct(self, _name: &'static str, _len: usize) -> SerializeResult<Self> {
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_struct_variant(self,
|
||||
_name: &'static str,
|
||||
variant_index: u32,
|
||||
_variant: &'static str,
|
||||
_len: usize)
|
||||
-> SerializeResult<Self> {
|
||||
if variant_index > 255 {
|
||||
debug_assert!(false, "too many enum variants: {}", _name);
|
||||
return Err(Error::TooManyVariants);
|
||||
}
|
||||
self.write_u8(variant_index as u8)?;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
fn collect_str<T: Display + ?Sized>(self, _value: &T) -> SerializeResult<()> {
|
||||
ns()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b, 'a: 'b> serde::ser::SerializeSeq for &'b mut Serializer<'a> {
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_element<V: Serialize + ?Sized>(&mut self, value: &V) -> SerializeResult<()> {
|
||||
value.serialize(&mut **self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn end(self) -> SerializeResult<()> { Ok(()) }
|
||||
}
|
||||
|
||||
impl<'b, 'a: 'b> serde::ser::SerializeTuple for &'b mut Serializer<'a> {
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_element<V: Serialize + ?Sized>(&mut self, value: &V) -> SerializeResult<()> {
|
||||
value.serialize(&mut **self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn end(self) -> SerializeResult<()> { Ok(()) }
|
||||
}
|
||||
|
||||
impl<'b, 'a: 'b> serde::ser::SerializeTupleStruct for &'b mut Serializer<'a> {
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<V: Serialize + ?Sized>(&mut self, value: &V) -> Result<(), Error> {
|
||||
value.serialize(&mut **self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn end(self) -> Result<(), Error> { Ok(()) }
|
||||
}
|
||||
|
||||
impl<'b, 'a: 'b> serde::ser::SerializeTupleVariant for &'b mut Serializer<'a> {
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<V: Serialize + ?Sized>(&mut self, value: &V) -> Result<(), Error> {
|
||||
value.serialize(&mut **self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn end(self) -> Result<(), Error> { Ok(()) }
|
||||
}
|
||||
|
||||
impl<'b, 'a: 'b> serde::ser::SerializeStruct for &'b mut Serializer<'a> {
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<V: Serialize + ?Sized>(&mut self, _: &'static str, value: &V) -> Result<(), Error> {
|
||||
value.serialize(&mut **self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn end(self) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b, 'a: 'b> serde::ser::SerializeStructVariant for &'b mut Serializer<'a> {
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<V: Serialize + ?Sized>(&mut self, _: &'static str, value: &V) -> Result<(), Error> {
|
||||
value.serialize(&mut **self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn end(self) -> Result<(), Error> { Ok(()) }
|
||||
}
|
||||
|
||||
struct Deserializer<'a> {
|
||||
buf: &'a [u8],
|
||||
idx: usize,
|
||||
}
|
||||
|
||||
impl<'a> Deserializer<'a> {
|
||||
#[inline]
|
||||
fn check_bounds(&self, len: usize) -> Result<(), Error> {
|
||||
if let Some(val) = self.idx.checked_add(len) {
|
||||
if val <= self.buf.len() {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
debug_assert!(false, "ran out of space deserializing value; fix your buffer size");
|
||||
Err(Error::EndOfStream)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_u8(&mut self) -> Result<u8, Error> {
|
||||
self.check_bounds(1)?;
|
||||
let val = unsafe {
|
||||
*self.buf.get_unchecked(self.idx)
|
||||
};
|
||||
self.idx += 1;
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_u16(&mut self) -> Result<u16, Error> {
|
||||
self.check_bounds(2)?;
|
||||
let mut val;
|
||||
unsafe {
|
||||
val = *self.buf.get_unchecked(self.idx) as u16;
|
||||
val |= (*self.buf.get_unchecked(self.idx + 1) as u16) << 8;
|
||||
}
|
||||
self.idx += 2;
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_u32(&mut self) -> Result<u32, Error> {
|
||||
self.check_bounds(4)?;
|
||||
let mut val;
|
||||
unsafe {
|
||||
val = *self.buf.get_unchecked(self.idx) as u32;
|
||||
val |= (*self.buf.get_unchecked(self.idx + 1) as u32) << 8;
|
||||
val |= (*self.buf.get_unchecked(self.idx + 2) as u32) << 16;
|
||||
val |= (*self.buf.get_unchecked(self.idx + 3) as u32) << 24;
|
||||
}
|
||||
self.idx += 4;
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_u64(&mut self) -> Result<u64, Error> {
|
||||
self.check_bounds(8)?;
|
||||
let mut val;
|
||||
unsafe {
|
||||
val = *self.buf.get_unchecked(self.idx) as u64;
|
||||
val |= (*self.buf.get_unchecked(self.idx + 1) as u64) << 8;
|
||||
val |= (*self.buf.get_unchecked(self.idx + 2) as u64) << 16;
|
||||
val |= (*self.buf.get_unchecked(self.idx + 3) as u64) << 24;
|
||||
val |= (*self.buf.get_unchecked(self.idx + 4) as u64) << 32;
|
||||
val |= (*self.buf.get_unchecked(self.idx + 5) as u64) << 40;
|
||||
val |= (*self.buf.get_unchecked(self.idx + 6) as u64) << 48;
|
||||
val |= (*self.buf.get_unchecked(self.idx + 7) as u64) << 56;
|
||||
}
|
||||
self.idx += 8;
|
||||
Ok(val)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct SeqAccess<'a, 'b: 'a> {
|
||||
deserializer: &'a mut Deserializer<'b>,
|
||||
len: usize,
|
||||
}
|
||||
|
||||
impl<'a, 'b: 'a> serde::de::SeqAccess<'b> for SeqAccess<'a, 'b> {
|
||||
type Error = Error;
|
||||
|
||||
fn next_element_seed<V: DeserializeSeed<'b>>(&mut self, seed: V) -> Result<Option<V::Value>, Error> {
|
||||
if self.len > 0 {
|
||||
self.len -= 1;
|
||||
Ok(Some(DeserializeSeed::deserialize(seed, &mut *self.deserializer)?))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> Option<usize> {
|
||||
Some(self.len)
|
||||
}
|
||||
}
|
||||
|
||||
type DeserializeResult<T> = Result<T, Error>;
|
||||
|
||||
impl<'b, 'de: 'b> serde::Deserializer<'de> for &'b mut Deserializer<'de> {
|
||||
type Error = Error;
|
||||
|
||||
fn deserialize_any<V: Visitor<'de>>(self, _visitor: V) -> DeserializeResult<V::Value> {
|
||||
ns()
|
||||
}
|
||||
|
||||
fn deserialize_bool<V: Visitor<'de>>(self, visitor: V) -> DeserializeResult<V::Value> {
|
||||
let value: u8 = Deserialize::deserialize(self)?;
|
||||
match value {
|
||||
0 => visitor.visit_bool(false),
|
||||
1 => visitor.visit_bool(true),
|
||||
_ => Err(Error::InvalidRepresentation),
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_u8<V: Visitor<'de>>(self, visitor: V) -> DeserializeResult<V::Value> {
|
||||
visitor.visit_u8(self.read_u8()?)
|
||||
}
|
||||
|
||||
fn deserialize_u16<V: Visitor<'de>>(self, visitor: V) -> DeserializeResult<V::Value> {
|
||||
visitor.visit_u16(self.read_u16()?)
|
||||
}
|
||||
|
||||
fn deserialize_u32<V: Visitor<'de>>(self, visitor: V) -> DeserializeResult<V::Value> {
|
||||
visitor.visit_u32(self.read_u32()?)
|
||||
}
|
||||
|
||||
fn deserialize_u64<V: Visitor<'de>>(self, visitor: V) -> DeserializeResult<V::Value> {
|
||||
visitor.visit_u64(self.read_u64()?)
|
||||
}
|
||||
|
||||
fn deserialize_i8<V: Visitor<'de>>(self, visitor: V) -> DeserializeResult<V::Value> {
|
||||
visitor.visit_i8(self.read_u8()? as i8)
|
||||
}
|
||||
|
||||
fn deserialize_i16<V: Visitor<'de>>(self, visitor: V) -> DeserializeResult<V::Value> {
|
||||
visitor.visit_i16(self.read_u16()? as i16)
|
||||
}
|
||||
|
||||
fn deserialize_i32<V: Visitor<'de>>(self, visitor: V) -> DeserializeResult<V::Value> {
|
||||
visitor.visit_i32(self.read_u32()? as i32)
|
||||
}
|
||||
|
||||
fn deserialize_i64<V: Visitor<'de>>(self, visitor: V) -> DeserializeResult<V::Value> {
|
||||
visitor.visit_i64(self.read_u64()? as i64)
|
||||
}
|
||||
|
||||
fn deserialize_f32<V: Visitor<'de>>(self, visitor: V) -> DeserializeResult<V::Value> {
|
||||
visitor.visit_f32(unsafe { transmute(self.read_u32()?) })
|
||||
}
|
||||
fn deserialize_f64<V: Visitor<'de>>(self, visitor: V) -> DeserializeResult<V::Value> {
|
||||
visitor.visit_f64(unsafe { transmute(self.read_u64()?) })
|
||||
}
|
||||
|
||||
fn deserialize_char<V: Visitor<'de>>(self, visitor: V) -> DeserializeResult<V::Value> {
|
||||
match Utf8Char::from_slice_start(&self.buf[self.idx..]) {
|
||||
Ok((c, count)) => {
|
||||
// this ought to be correct, if it weren't how did from_slice_start do its thing?
|
||||
self.idx = self.idx.wrapping_add(count);
|
||||
visitor.visit_char(c.to_char())
|
||||
},
|
||||
Err(_) => Err(Error::InvalidRepresentation),
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_str<V: Visitor<'de>>(self, _visitor: V) -> DeserializeResult<V::Value> {
|
||||
ns()
|
||||
}
|
||||
|
||||
fn deserialize_string<V: Visitor<'de>>(self, _visitor: V) -> DeserializeResult<V::Value> {
|
||||
ns()
|
||||
}
|
||||
|
||||
fn deserialize_bytes<V: Visitor<'de>>(self, _visitor: V) -> DeserializeResult<V::Value> {
|
||||
ns()
|
||||
}
|
||||
|
||||
fn deserialize_byte_buf<V: Visitor<'de>>(self, _visitor: V) -> DeserializeResult<V::Value> {
|
||||
ns()
|
||||
}
|
||||
|
||||
fn deserialize_option<V: Visitor<'de>>(self, visitor: V) -> DeserializeResult<V::Value> {
|
||||
let value: u8 = Deserialize::deserialize(&mut *self)?;
|
||||
match value {
|
||||
0 => visitor.visit_none(),
|
||||
1 => visitor.visit_some(self),
|
||||
_ => Err(Error::InvalidRepresentation),
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_unit<V: Visitor<'de>>(self, visitor: V) -> DeserializeResult<V::Value> {
|
||||
visitor.visit_unit()
|
||||
}
|
||||
|
||||
fn deserialize_unit_struct<V: Visitor<'de>>(self,
|
||||
_name: &'static str,
|
||||
visitor: V)
|
||||
-> DeserializeResult<V::Value> {
|
||||
visitor.visit_unit()
|
||||
}
|
||||
|
||||
fn deserialize_newtype_struct<V: Visitor<'de>>(self,
|
||||
_name: &str,
|
||||
visitor: V)
|
||||
-> DeserializeResult<V::Value> {
|
||||
visitor.visit_newtype_struct(self)
|
||||
}
|
||||
|
||||
fn deserialize_seq<V: Visitor<'de>>(self, visitor: V) -> DeserializeResult<V::Value> {
|
||||
let len = Deserialize::deserialize(&mut *self)?;
|
||||
|
||||
visitor.visit_seq(SeqAccess {
|
||||
deserializer: self,
|
||||
len: len,
|
||||
})
|
||||
}
|
||||
|
||||
fn deserialize_tuple<V: Visitor<'de>>(self, len: usize, visitor: V) -> DeserializeResult<V::Value> {
|
||||
visitor.visit_seq(SeqAccess { deserializer: self, len: len })
|
||||
}
|
||||
|
||||
fn deserialize_tuple_struct<V: Visitor<'de>>(self,
|
||||
_name: &'static str,
|
||||
len: usize,
|
||||
visitor: V)
|
||||
-> DeserializeResult<V::Value> {
|
||||
self.deserialize_tuple(len, visitor)
|
||||
}
|
||||
|
||||
fn deserialize_map<V: Visitor<'de>>(self, _visitor: V) -> DeserializeResult<V::Value> {
|
||||
Err(Error::NotSupported)
|
||||
}
|
||||
|
||||
fn deserialize_struct<V: Visitor<'de>>(self,
|
||||
_name: &str,
|
||||
fields: &'static [&'static str],
|
||||
visitor: V)
|
||||
-> DeserializeResult<V::Value> {
|
||||
self.deserialize_tuple(fields.len(), visitor)
|
||||
}
|
||||
|
||||
fn deserialize_enum<V: Visitor<'de>>(self,
|
||||
_enum: &'static str,
|
||||
_variants: &'static [&'static str],
|
||||
visitor: V)
|
||||
-> Result<V::Value, Error> {
|
||||
visitor.visit_enum(self)
|
||||
}
|
||||
|
||||
|
||||
fn deserialize_identifier<V: Visitor<'de>>(self, _visitor: V) -> DeserializeResult<V::Value> {
|
||||
// not the panic because it seems noone cares about these?
|
||||
Err(Error::NotSupported)
|
||||
}
|
||||
|
||||
fn deserialize_ignored_any<V: Visitor<'de>>(self, _visitor: V) -> DeserializeResult<V::Value> {
|
||||
ns()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b, 'de: 'b> serde::de::VariantAccess<'de> for &'b mut Deserializer<'de> {
|
||||
type Error = Error;
|
||||
|
||||
fn unit_variant(self) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn newtype_variant_seed<V: DeserializeSeed<'de>>(self, seed: V) -> DeserializeResult<V::Value> {
|
||||
DeserializeSeed::deserialize(seed, self)
|
||||
}
|
||||
|
||||
fn tuple_variant<V: Visitor<'de>>(self, len: usize, visitor: V) -> DeserializeResult<V::Value> {
|
||||
serde::de::Deserializer::deserialize_tuple(self, len, visitor)
|
||||
}
|
||||
|
||||
fn struct_variant<V: Visitor<'de>>(self,
|
||||
fields: &'static [&'static str],
|
||||
visitor: V)
|
||||
-> Result<V::Value, Error> {
|
||||
serde::de::Deserializer::deserialize_tuple(self, fields.len(), visitor)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b, 'de: 'b> serde::de::EnumAccess<'de> for &'b mut Deserializer<'de> {
|
||||
type Error = Error;
|
||||
type Variant = Self;
|
||||
|
||||
fn variant_seed<V: DeserializeSeed<'de>>(self, seed: V) -> DeserializeResult<(V::Value, Self)> {
|
||||
let x: u8 = Deserialize::deserialize(&mut *self)?;
|
||||
let v = DeserializeSeed::deserialize(seed, (x as u32).into_deserializer())?;
|
||||
Ok((v, self))
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+239
@@ -0,0 +1,239 @@
|
||||
// Copyright (c) 2017 The Robigalia Project Developers Licensed under the Apache License, Version
|
||||
// 2.0 <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option. All files in the project
|
||||
// carrying such notice may not be copied, modified, or distributed except according to those
|
||||
// terms.
|
||||
|
||||
#![feature(proc_macro, plugin)]
|
||||
#![plugin(quickcheck_macros)]
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
extern crate serde;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
extern crate quickcheck;
|
||||
|
||||
extern crate ssmarshal;
|
||||
|
||||
use serde::{Serialize};
|
||||
use serde::de::DeserializeOwned;
|
||||
|
||||
use ssmarshal::{serialize, deserialize};
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
struct U8(u8);
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
struct U16(u16);
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
struct U32(u32);
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
struct U64(u64);
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
struct USize(usize);
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
struct I8(i8);
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
struct I16(i16);
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
struct I32(i32);
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
struct I64(i64);
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
struct ISize(isize);
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
|
||||
struct F32(f32);
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
|
||||
struct F64(f64);
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
|
||||
struct Simple {
|
||||
a: u8,
|
||||
b: u16,
|
||||
c: u32,
|
||||
d: u64,
|
||||
e: i8,
|
||||
f: f32,
|
||||
g: u8,
|
||||
h: f64,
|
||||
}
|
||||
|
||||
impl quickcheck::Arbitrary for Simple {
|
||||
fn arbitrary<G: quickcheck::Gen>(g: &mut G) -> Simple {
|
||||
Simple {
|
||||
a: g.gen(),
|
||||
b: g.gen(),
|
||||
c: g.gen(),
|
||||
d: g.gen(),
|
||||
e: g.gen(),
|
||||
f: g.gen(),
|
||||
g: g.gen(),
|
||||
h: g.gen(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
|
||||
struct Unit;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
|
||||
struct Complex {
|
||||
a: Simple,
|
||||
e: Unit,
|
||||
b: Simple,
|
||||
c: [u8; 7],
|
||||
d: (),
|
||||
f: [Unit; 3],
|
||||
}
|
||||
|
||||
impl quickcheck::Arbitrary for Complex {
|
||||
fn arbitrary<G: quickcheck::Gen>(g: &mut G) -> Complex {
|
||||
Complex {
|
||||
a: Simple::arbitrary(g),
|
||||
b: Simple::arbitrary(g),
|
||||
c: g.gen(),
|
||||
d: (),
|
||||
e: Unit,
|
||||
f: [Unit; 3],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[repr(C)]
|
||||
enum ComplexEnum {
|
||||
A,
|
||||
B(Simple),
|
||||
C(u8, u16),
|
||||
D(isize),
|
||||
E {
|
||||
foo: Simple
|
||||
},
|
||||
F {
|
||||
bar: Complex,
|
||||
baz: Simple,
|
||||
qux: char
|
||||
}
|
||||
}
|
||||
|
||||
impl quickcheck::Arbitrary for ComplexEnum {
|
||||
fn arbitrary<G: quickcheck::Gen>(g: &mut G) -> ComplexEnum {
|
||||
match g.gen_range(0, 6) {
|
||||
0 => {
|
||||
ComplexEnum::A
|
||||
},
|
||||
1 => {
|
||||
ComplexEnum::B(Simple::arbitrary(g))
|
||||
},
|
||||
2 => {
|
||||
ComplexEnum::C(g.gen(), g.gen())
|
||||
},
|
||||
3 => {
|
||||
ComplexEnum::D(g.gen())
|
||||
},
|
||||
4 => {
|
||||
ComplexEnum::E { foo: Simple::arbitrary(g) }
|
||||
},
|
||||
5 => {
|
||||
ComplexEnum::F { bar: Complex::arbitrary(g), baz: Simple::arbitrary(g), qux: g.gen() }
|
||||
},
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
|
||||
struct TupleStruct(u8, u64, Simple, Option<ComplexEnum>);
|
||||
|
||||
impl quickcheck::Arbitrary for TupleStruct {
|
||||
fn arbitrary<G: quickcheck::Gen>(g: &mut G) -> TupleStruct {
|
||||
TupleStruct(g.gen(), g.gen(), quickcheck::Arbitrary::arbitrary(g), quickcheck::Arbitrary::arbitrary(g))
|
||||
}
|
||||
}
|
||||
|
||||
fn rt_val<T: Serialize + DeserializeOwned + PartialEq+std::fmt::Debug>(val: &T) -> bool {
|
||||
let mut buf = vec![0; std::mem::size_of::<T>()];
|
||||
serialize(&mut buf, val).unwrap();
|
||||
let new_val: T = deserialize(&buf).unwrap().0;
|
||||
println!("\n\nOld: {:?}\nNew: {:?}", val, new_val);
|
||||
val == &new_val
|
||||
}
|
||||
|
||||
#[quickcheck]
|
||||
fn rt_simple(val: Simple) -> bool {
|
||||
rt_val(&val)
|
||||
}
|
||||
|
||||
#[quickcheck]
|
||||
fn rt_complex(val: Complex) -> bool {
|
||||
rt_val(&val)
|
||||
}
|
||||
|
||||
#[quickcheck]
|
||||
fn rt_complexenum(val: ComplexEnum) -> bool {
|
||||
rt_val(&val)
|
||||
}
|
||||
|
||||
#[quickcheck]
|
||||
fn rt_tuplestruct(val: TupleStruct) -> bool {
|
||||
rt_val(&val)
|
||||
}
|
||||
|
||||
macro_rules! rt {
|
||||
($(($name:ident, $t:ty)),*) => {
|
||||
$(#[quickcheck] fn $name(val: $t) -> bool {
|
||||
rt_val::<$t>(&val)
|
||||
})*
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! rt_wrap {
|
||||
($(($name:ident, $t:ty, $n:ident)),*) => {
|
||||
$(#[quickcheck] fn $name(val: $t) -> bool {
|
||||
rt_val::<$n>(&$n(val))
|
||||
})*
|
||||
}
|
||||
}
|
||||
|
||||
rt! {
|
||||
(rt_i8, i8),
|
||||
(rt_i16, i16),
|
||||
(rt_i32, i32),
|
||||
(rt_i64, i64),
|
||||
(rt_u8, u8),
|
||||
(rt_u16, u16),
|
||||
(rt_u32, u32),
|
||||
(rt_u64, u64),
|
||||
(rt_usize, usize),
|
||||
(rt_isize, isize),
|
||||
(rt_f32, f32),
|
||||
(rt_f64, f64),
|
||||
(rt_tp1, (u8, u8, u8)),
|
||||
(rt_tp2, (u8, i16, u8)),
|
||||
(rt_tp3, (u8, i16, u8)),
|
||||
(rt_tp4, (usize, i16, isize)),
|
||||
(rt_char, char)
|
||||
}
|
||||
|
||||
rt_wrap! {
|
||||
(rt_I8, i8 , I8),
|
||||
(rt_I16, i16, I16),
|
||||
(rt_I32, i32, I32),
|
||||
(rt_I64, i64, I64),
|
||||
(rt_U8, u8 , U8),
|
||||
(rt_U16, u16, U16),
|
||||
(rt_U32, u32, U32),
|
||||
(rt_U64, u64, U64),
|
||||
(rt_F32, f32, F32),
|
||||
(rt_F64, f64, F64)
|
||||
}
|
||||
Reference in new issue
Block a user