summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDongHun Kwak <dh0128.kwak@samsung.com>2023-04-26 15:25:39 +0900
committerDongHun Kwak <dh0128.kwak@samsung.com>2023-04-26 15:25:39 +0900
commit17a5b5d427ccda706b7ccf57af1cd5c04ced079b (patch)
treeb8842b5f8510632159eb281386063e412e0c2b8a
downloadrust-serde_bytes-upstream.tar.gz
rust-serde_bytes-upstream.tar.bz2
rust-serde_bytes-upstream.zip
Import serde_bytes 0.11.9upstream/0.11.9upstream
-rw-r--r--.cargo_vcs_info.json6
-rw-r--r--.github/workflows/ci.yml52
-rw-r--r--.gitignore3
-rw-r--r--Cargo.toml56
-rw-r--r--Cargo.toml.orig31
-rw-r--r--LICENSE-APACHE176
-rw-r--r--LICENSE-MIT23
-rw-r--r--README.md54
-rw-r--r--src/bytebuf.rs253
-rw-r--r--src/bytes.rs172
-rw-r--r--src/de.rs229
-rw-r--r--src/lib.rs113
-rw-r--r--src/ser.rs136
-rw-r--r--tests/test_derive.rs101
-rw-r--r--tests/test_partialeq.rs15
-rw-r--r--tests/test_serde.rs59
16 files changed, 1479 insertions, 0 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
new file mode 100644
index 0000000..555ba05
--- /dev/null
+++ b/.cargo_vcs_info.json
@@ -0,0 +1,6 @@
+{
+ "git": {
+ "sha1": "c3c83935d3943224c17ab11a7796db8e2810b7dc"
+ },
+ "path_in_vcs": ""
+} \ No newline at end of file
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..66b119c
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,52 @@
+name: CI
+
+on:
+ push:
+ pull_request:
+ schedule: [cron: "40 1 * * *"]
+
+permissions:
+ contents: read
+
+env:
+ RUSTFLAGS: -Dwarnings
+
+jobs:
+ test:
+ name: Rust ${{matrix.rust}}
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ rust: [nightly, beta, stable, 1.36.0, 1.34.0, 1.31.0]
+ timeout-minutes: 45
+ steps:
+ - uses: actions/checkout@v3
+ - uses: dtolnay/rust-toolchain@master
+ with:
+ toolchain: ${{matrix.rust}}
+ - run: cargo test
+ if: matrix.rust != '1.31.0' && matrix.rust != '1.34.0'
+ - run: cargo check --no-default-features
+ - run: cargo check --no-default-features --features alloc
+ if: matrix.rust != '1.31.0' && matrix.rust != '1.34.0'
+
+ clippy:
+ name: Clippy
+ runs-on: ubuntu-latest
+ if: github.event_name != 'pull_request'
+ timeout-minutes: 45
+ steps:
+ - uses: actions/checkout@v3
+ - uses: dtolnay/rust-toolchain@clippy
+ - run: cargo clippy --tests -- -Dclippy::all -Dclippy::pedantic
+
+ outdated:
+ name: Outdated
+ runs-on: ubuntu-latest
+ if: github.event_name != 'pull_request'
+ timeout-minutes: 45
+ steps:
+ - uses: actions/checkout@v3
+ - uses: dtolnay/install@cargo-outdated
+ - run: cargo outdated --workspace --exit-code 1
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..4308d82
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+target/
+**/*.rs.bk
+Cargo.lock
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..2e75f8a
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,56 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2018"
+rust-version = "1.31"
+name = "serde_bytes"
+version = "0.11.9"
+authors = ["David Tolnay <dtolnay@gmail.com>"]
+description = "Optimized handling of `&[u8]` and `Vec<u8>` for Serde"
+documentation = "https://docs.rs/serde_bytes"
+readme = "README.md"
+keywords = [
+ "serde",
+ "serialization",
+ "no_std",
+ "bytes",
+]
+categories = [
+ "encoding",
+ "no-std",
+]
+license = "MIT OR Apache-2.0"
+repository = "https://github.com/serde-rs/bytes"
+
+[package.metadata.docs.rs]
+targets = ["x86_64-unknown-linux-gnu"]
+
+[lib]
+doc-scrape-examples = false
+
+[dependencies.serde]
+version = "1.0"
+default-features = false
+
+[dev-dependencies.bincode]
+version = "1.0"
+
+[dev-dependencies.serde_derive]
+version = "1.0"
+
+[dev-dependencies.serde_test]
+version = "1.0"
+
+[features]
+alloc = ["serde/alloc"]
+default = ["std"]
+std = ["serde/std"]
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
new file mode 100644
index 0000000..241a337
--- /dev/null
+++ b/Cargo.toml.orig
@@ -0,0 +1,31 @@
+[package]
+name = "serde_bytes"
+version = "0.11.9" # remember to update html_root_url
+authors = ["David Tolnay <dtolnay@gmail.com>"]
+categories = ["encoding", "no-std"]
+description = "Optimized handling of `&[u8]` and `Vec<u8>` for Serde"
+documentation = "https://docs.rs/serde_bytes"
+edition = "2018"
+keywords = ["serde", "serialization", "no_std", "bytes"]
+license = "MIT OR Apache-2.0"
+repository = "https://github.com/serde-rs/bytes"
+rust-version = "1.31"
+
+[features]
+default = ["std"]
+std = ["serde/std"]
+alloc = ["serde/alloc"]
+
+[dependencies]
+serde = { version = "1.0", default-features = false }
+
+[dev-dependencies]
+bincode = "1.0"
+serde_derive = "1.0"
+serde_test = "1.0"
+
+[lib]
+doc-scrape-examples = false
+
+[package.metadata.docs.rs]
+targets = ["x86_64-unknown-linux-gnu"]
diff --git a/LICENSE-APACHE b/LICENSE-APACHE
new file mode 100644
index 0000000..1b5ec8b
--- /dev/null
+++ b/LICENSE-APACHE
@@ -0,0 +1,176 @@
+ 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
diff --git a/LICENSE-MIT b/LICENSE-MIT
new file mode 100644
index 0000000..31aa793
--- /dev/null
+++ b/LICENSE-MIT
@@ -0,0 +1,23 @@
+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.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..e0a0e9f
--- /dev/null
+++ b/README.md
@@ -0,0 +1,54 @@
+# serde\_bytes [![Build Status](https://img.shields.io/github/actions/workflow/status/serde-rs/bytes/ci.yml?branch=master)](https://github.com/serde-rs/bytes/actions?query=branch%3Amaster) [![Latest Version](https://img.shields.io/crates/v/serde_bytes.svg)](https://crates.io/crates/serde_bytes)
+
+Wrapper types to enable optimized handling of `&[u8]` and `Vec<u8>`.
+
+```toml
+[dependencies]
+serde_bytes = "0.11"
+```
+
+## Explanation
+
+Without specialization, Rust forces Serde to treat `&[u8]` just like any
+other slice and `Vec<u8>` just like any other vector. In reality this
+particular slice and vector can often be serialized and deserialized in a
+more efficient, compact representation in many formats.
+
+When working with such a format, you can opt into specialized handling of
+`&[u8]` by wrapping it in `serde_bytes::Bytes` and `Vec<u8>` by wrapping it
+in `serde_bytes::ByteBuf`.
+
+Additionally this crate supports the Serde `with` attribute to enable efficient
+handling of `&[u8]` and `Vec<u8>` in structs without needing a wrapper type.
+
+## Example
+
+```rust
+use serde::{Deserialize, Serialize};
+
+#[derive(Deserialize, Serialize)]
+struct Efficient<'a> {
+ #[serde(with = "serde_bytes")]
+ bytes: &'a [u8],
+
+ #[serde(with = "serde_bytes")]
+ byte_buf: Vec<u8>,
+}
+```
+
+<br>
+
+#### License
+
+<sup>
+Licensed under either of <a href="LICENSE-APACHE">Apache License, Version
+2.0</a> or <a href="LICENSE-MIT">MIT license</a> at your option.
+</sup>
+
+<br>
+
+<sub>
+Unless you explicitly state otherwise, any contribution intentionally submitted
+for inclusion in this crate by you, as defined in the Apache-2.0 license, shall
+be dual licensed as above, without any additional terms or conditions.
+</sub>
diff --git a/src/bytebuf.rs b/src/bytebuf.rs
new file mode 100644
index 0000000..0c2daa6
--- /dev/null
+++ b/src/bytebuf.rs
@@ -0,0 +1,253 @@
+use core::borrow::{Borrow, BorrowMut};
+use core::cmp::{self, Ordering};
+use core::fmt::{self, Debug};
+use core::hash::{Hash, Hasher};
+use core::ops::{Deref, DerefMut};
+
+#[cfg(feature = "alloc")]
+use alloc::boxed::Box;
+#[cfg(feature = "alloc")]
+use alloc::string::String;
+#[cfg(feature = "alloc")]
+use alloc::vec::Vec;
+
+use serde::de::{Deserialize, Deserializer, Error, SeqAccess, Visitor};
+use serde::ser::{Serialize, Serializer};
+
+use crate::Bytes;
+
+/// Wrapper around `Vec<u8>` to serialize and deserialize efficiently.
+///
+/// ```
+/// use std::collections::HashMap;
+/// use std::io;
+///
+/// use serde_bytes::ByteBuf;
+///
+/// fn deserialize_bytebufs() -> bincode::Result<()> {
+/// let example_data = [
+/// 2, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 116,
+/// 119, 111, 1, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 111, 110, 101];
+///
+/// let map: HashMap<u32, ByteBuf> = bincode::deserialize(&example_data[..])?;
+///
+/// println!("{:?}", map);
+///
+/// Ok(())
+/// }
+/// #
+/// # fn main() {
+/// # deserialize_bytebufs().unwrap();
+/// # }
+/// ```
+#[derive(Clone, Default, Eq, Ord)]
+pub struct ByteBuf {
+ bytes: Vec<u8>,
+}
+
+impl ByteBuf {
+ /// Construct a new, empty `ByteBuf`.
+ pub fn new() -> Self {
+ ByteBuf::from(Vec::new())
+ }
+
+ /// Construct a new, empty `ByteBuf` with the specified capacity.
+ pub fn with_capacity(cap: usize) -> Self {
+ ByteBuf::from(Vec::with_capacity(cap))
+ }
+
+ /// Wrap existing bytes in a `ByteBuf`.
+ pub fn from<T: Into<Vec<u8>>>(bytes: T) -> Self {
+ ByteBuf {
+ bytes: bytes.into(),
+ }
+ }
+
+ /// Unwrap the vector of byte underlying this `ByteBuf`.
+ pub fn into_vec(self) -> Vec<u8> {
+ self.bytes
+ }
+
+ #[allow(missing_docs)]
+ pub fn into_boxed_bytes(self) -> Box<Bytes> {
+ self.bytes.into_boxed_slice().into()
+ }
+
+ // This would hit "cannot move out of borrowed content" if invoked through
+ // the Deref impl; make it just work.
+ #[doc(hidden)]
+ pub fn into_boxed_slice(self) -> Box<[u8]> {
+ self.bytes.into_boxed_slice()
+ }
+
+ #[doc(hidden)]
+ #[allow(clippy::should_implement_trait)]
+ pub fn into_iter(self) -> <Vec<u8> as IntoIterator>::IntoIter {
+ self.bytes.into_iter()
+ }
+}
+
+impl Debug for ByteBuf {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ Debug::fmt(&self.bytes, f)
+ }
+}
+
+impl AsRef<[u8]> for ByteBuf {
+ fn as_ref(&self) -> &[u8] {
+ &self.bytes
+ }
+}
+
+impl AsMut<[u8]> for ByteBuf {
+ fn as_mut(&mut self) -> &mut [u8] {
+ &mut self.bytes
+ }
+}
+
+impl Deref for ByteBuf {
+ type Target = Vec<u8>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.bytes
+ }
+}
+
+impl DerefMut for ByteBuf {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.bytes
+ }
+}
+
+impl Borrow<Bytes> for ByteBuf {
+ fn borrow(&self) -> &Bytes {
+ Bytes::new(&self.bytes)
+ }
+}
+
+impl BorrowMut<Bytes> for ByteBuf {
+ fn borrow_mut(&mut self) -> &mut Bytes {
+ unsafe { &mut *(&mut self.bytes as &mut [u8] as *mut [u8] as *mut Bytes) }
+ }
+}
+
+impl<Rhs> PartialEq<Rhs> for ByteBuf
+where
+ Rhs: ?Sized + AsRef<[u8]>,
+{
+ fn eq(&self, other: &Rhs) -> bool {
+ self.as_ref().eq(other.as_ref())
+ }
+}
+
+impl<Rhs> PartialOrd<Rhs> for ByteBuf
+where
+ Rhs: ?Sized + AsRef<[u8]>,
+{
+ fn partial_cmp(&self, other: &Rhs) -> Option<Ordering> {
+ self.as_ref().partial_cmp(other.as_ref())
+ }
+}
+
+impl Hash for ByteBuf {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ self.bytes.hash(state);
+ }
+}
+
+impl IntoIterator for ByteBuf {
+ type Item = u8;
+ type IntoIter = <Vec<u8> as IntoIterator>::IntoIter;
+
+ fn into_iter(self) -> Self::IntoIter {
+ self.bytes.into_iter()
+ }
+}
+
+impl<'a> IntoIterator for &'a ByteBuf {
+ type Item = &'a u8;
+ type IntoIter = <&'a [u8] as IntoIterator>::IntoIter;
+
+ fn into_iter(self) -> Self::IntoIter {
+ self.bytes.iter()
+ }
+}
+
+impl<'a> IntoIterator for &'a mut ByteBuf {
+ type Item = &'a mut u8;
+ type IntoIter = <&'a mut [u8] as IntoIterator>::IntoIter;
+
+ fn into_iter(self) -> Self::IntoIter {
+ self.bytes.iter_mut()
+ }
+}
+
+impl Serialize for ByteBuf {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ serializer.serialize_bytes(&self.bytes)
+ }
+}
+
+struct ByteBufVisitor;
+
+impl<'de> Visitor<'de> for ByteBufVisitor {
+ type Value = ByteBuf;
+
+ fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ formatter.write_str("byte array")
+ }
+
+ fn visit_seq<V>(self, mut visitor: V) -> Result<ByteBuf, V::Error>
+ where
+ V: SeqAccess<'de>,
+ {
+ let len = cmp::min(visitor.size_hint().unwrap_or(0), 4096);
+ let mut bytes = Vec::with_capacity(len);
+
+ while let Some(b) = visitor.next_element()? {
+ bytes.push(b);
+ }
+
+ Ok(ByteBuf::from(bytes))
+ }
+
+ fn visit_bytes<E>(self, v: &[u8]) -> Result<ByteBuf, E>
+ where
+ E: Error,
+ {
+ Ok(ByteBuf::from(v))
+ }
+
+ fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<ByteBuf, E>
+ where
+ E: Error,
+ {
+ Ok(ByteBuf::from(v))
+ }
+
+ fn visit_str<E>(self, v: &str) -> Result<ByteBuf, E>
+ where
+ E: Error,
+ {
+ Ok(ByteBuf::from(v))
+ }
+
+ fn visit_string<E>(self, v: String) -> Result<ByteBuf, E>
+ where
+ E: Error,
+ {
+ Ok(ByteBuf::from(v))
+ }
+}
+
+impl<'de> Deserialize<'de> for ByteBuf {
+ fn deserialize<D>(deserializer: D) -> Result<ByteBuf, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ deserializer.deserialize_byte_buf(ByteBufVisitor)
+ }
+}
diff --git a/src/bytes.rs b/src/bytes.rs
new file mode 100644
index 0000000..ad0902b
--- /dev/null
+++ b/src/bytes.rs
@@ -0,0 +1,172 @@
+use core::cmp::Ordering;
+use core::fmt::{self, Debug};
+use core::hash::{Hash, Hasher};
+use core::ops::{Deref, DerefMut};
+
+#[cfg(feature = "alloc")]
+use alloc::borrow::ToOwned;
+
+#[cfg(feature = "alloc")]
+use alloc::boxed::Box;
+
+#[cfg(any(feature = "std", feature = "alloc"))]
+use crate::ByteBuf;
+
+use serde::de::{Deserialize, Deserializer};
+use serde::ser::{Serialize, Serializer};
+
+/// Wrapper around `[u8]` to serialize and deserialize efficiently.
+///
+/// ```
+/// use std::collections::HashMap;
+/// use std::io;
+///
+/// use serde_bytes::Bytes;
+///
+/// fn print_encoded_cache() -> bincode::Result<()> {
+/// let mut cache = HashMap::new();
+/// cache.insert(3, Bytes::new(b"three"));
+/// cache.insert(2, Bytes::new(b"two"));
+/// cache.insert(1, Bytes::new(b"one"));
+///
+/// bincode::serialize_into(&mut io::stdout(), &cache)
+/// }
+/// #
+/// # fn main() {
+/// # print_encoded_cache().unwrap();
+/// # }
+/// ```
+#[derive(Eq, Ord)]
+#[cfg_attr(not(doc), repr(transparent))]
+pub struct Bytes {
+ bytes: [u8],
+}
+
+impl Bytes {
+ /// Wrap an existing `&[u8]`.
+ pub fn new(bytes: &[u8]) -> &Self {
+ unsafe { &*(bytes as *const [u8] as *const Bytes) }
+ }
+}
+
+impl Debug for Bytes {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ Debug::fmt(&self.bytes, f)
+ }
+}
+
+impl AsRef<[u8]> for Bytes {
+ fn as_ref(&self) -> &[u8] {
+ &self.bytes
+ }
+}
+
+impl AsMut<[u8]> for Bytes {
+ fn as_mut(&mut self) -> &mut [u8] {
+ &mut self.bytes
+ }
+}
+
+impl Deref for Bytes {
+ type Target = [u8];
+
+ fn deref(&self) -> &Self::Target {
+ &self.bytes
+ }
+}
+
+impl DerefMut for Bytes {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.bytes
+ }
+}
+
+#[cfg(any(feature = "std", feature = "alloc"))]
+impl ToOwned for Bytes {
+ type Owned = ByteBuf;
+
+ fn to_owned(&self) -> Self::Owned {
+ ByteBuf::from(&self.bytes)
+ }
+}
+
+#[cfg(any(feature = "std", feature = "alloc"))]
+impl From<Box<[u8]>> for Box<Bytes> {
+ fn from(bytes: Box<[u8]>) -> Self {
+ unsafe { Box::from_raw(Box::into_raw(bytes) as *mut Bytes) }
+ }
+}
+
+impl<'a> Default for &'a Bytes {
+ fn default() -> Self {
+ Bytes::new(&[])
+ }
+}
+
+#[cfg(any(feature = "std", feature = "alloc"))]
+impl Default for Box<Bytes> {
+ fn default() -> Self {
+ ByteBuf::new().into_boxed_bytes()
+ }
+}
+
+impl<Rhs> PartialEq<Rhs> for Bytes
+where
+ Rhs: ?Sized + AsRef<[u8]>,
+{
+ fn eq(&self, other: &Rhs) -> bool {
+ self.as_ref().eq(other.as_ref())
+ }
+}
+
+impl<Rhs> PartialOrd<Rhs> for Bytes
+where
+ Rhs: ?Sized + AsRef<[u8]>,
+{
+ fn partial_cmp(&self, other: &Rhs) -> Option<Ordering> {
+ self.as_ref().partial_cmp(other.as_ref())
+ }
+}
+
+impl Hash for Bytes {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ self.bytes.hash(state);
+ }
+}
+
+impl<'a> IntoIterator for &'a Bytes {
+ type Item = &'a u8;
+ type IntoIter = <&'a [u8] as IntoIterator>::IntoIter;
+
+ fn into_iter(self) -> Self::IntoIter {
+ self.bytes.iter()
+ }
+}
+
+impl<'a> IntoIterator for &'a mut Bytes {
+ type Item = &'a mut u8;
+ type IntoIter = <&'a mut [u8] as IntoIterator>::IntoIter;
+
+ fn into_iter(self) -> Self::IntoIter {
+ self.bytes.iter_mut()
+ }
+}
+
+impl Serialize for Bytes {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ serializer.serialize_bytes(&self.bytes)
+ }
+}
+
+impl<'a, 'de: 'a> Deserialize<'de> for &'a Bytes {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ // serde::Deserialize for &[u8] is already optimized, so simply forward to that.
+ Deserialize::deserialize(deserializer).map(Bytes::new)
+ }
+}
diff --git a/src/de.rs b/src/de.rs
new file mode 100644
index 0000000..e4a4732
--- /dev/null
+++ b/src/de.rs
@@ -0,0 +1,229 @@
+use crate::Bytes;
+use core::fmt;
+use core::marker::PhantomData;
+use serde::de::{Error, Visitor};
+use serde::Deserializer;
+
+#[cfg(any(feature = "std", feature = "alloc"))]
+use crate::ByteBuf;
+
+#[cfg(any(feature = "std", feature = "alloc"))]
+use core::cmp;
+
+#[cfg(feature = "alloc")]
+use alloc::borrow::Cow;
+#[cfg(all(feature = "std", not(feature = "alloc")))]
+use std::borrow::Cow;
+
+#[cfg(feature = "alloc")]
+use alloc::boxed::Box;
+#[cfg(feature = "alloc")]
+use alloc::string::String;
+#[cfg(feature = "alloc")]
+use alloc::vec::Vec;
+
+#[cfg(any(feature = "std", feature = "alloc"))]
+use serde::de::SeqAccess;
+
+/// Types that can be deserialized via `#[serde(with = "serde_bytes")]`.
+pub trait Deserialize<'de>: Sized {
+ #[allow(missing_docs)]
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: Deserializer<'de>;
+}
+
+impl<'de: 'a, 'a> Deserialize<'de> for &'a [u8] {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ // serde::Deserialize for &[u8] is already optimized, so simply forward to that.
+ serde::Deserialize::deserialize(deserializer)
+ }
+}
+
+#[cfg(any(feature = "std", feature = "alloc"))]
+impl<'de> Deserialize<'de> for Vec<u8> {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ Deserialize::deserialize(deserializer).map(ByteBuf::into_vec)
+ }
+}
+
+impl<'de: 'a, 'a> Deserialize<'de> for &'a Bytes {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ // serde::Deserialize for &[u8] is already optimized, so simply forward to that.
+ serde::Deserialize::deserialize(deserializer).map(Bytes::new)
+ }
+}
+
+#[cfg(any(feature = "std", feature = "alloc"))]
+impl<'de> Deserialize<'de> for ByteBuf {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ // Via the serde::Deserialize impl for ByteBuf.
+ serde::Deserialize::deserialize(deserializer)
+ }
+}
+
+#[cfg(any(feature = "std", feature = "alloc"))]
+impl<'de: 'a, 'a> Deserialize<'de> for Cow<'a, [u8]> {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ struct CowVisitor;
+
+ impl<'de> Visitor<'de> for CowVisitor {
+ type Value = Cow<'de, [u8]>;
+
+ fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ formatter.write_str("a byte array")
+ }
+
+ fn visit_borrowed_bytes<E>(self, v: &'de [u8]) -> Result<Self::Value, E>
+ where
+ E: Error,
+ {
+ Ok(Cow::Borrowed(v))
+ }
+
+ fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
+ where
+ E: Error,
+ {
+ Ok(Cow::Borrowed(v.as_bytes()))
+ }
+
+ fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
+ where
+ E: Error,
+ {
+ Ok(Cow::Owned(v.to_vec()))
+ }
+
+ fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
+ where
+ E: Error,
+ {
+ Ok(Cow::Owned(v.as_bytes().to_vec()))
+ }
+
+ fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<Self::Value, E>
+ where
+ E: Error,
+ {
+ Ok(Cow::Owned(v))
+ }
+
+ fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
+ where
+ E: Error,
+ {
+ Ok(Cow::Owned(v.into_bytes()))
+ }
+
+ fn visit_seq<V>(self, mut visitor: V) -> Result<Self::Value, V::Error>
+ where
+ V: SeqAccess<'de>,
+ {
+ let len = cmp::min(visitor.size_hint().unwrap_or(0), 4096);
+ let mut bytes = Vec::with_capacity(len);
+
+ while let Some(b) = visitor.next_element()? {
+ bytes.push(b);
+ }
+
+ Ok(Cow::Owned(bytes))
+ }
+ }
+
+ deserializer.deserialize_bytes(CowVisitor)
+ }
+}
+
+#[cfg(any(feature = "std", feature = "alloc"))]
+impl<'de: 'a, 'a> Deserialize<'de> for Cow<'a, Bytes> {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ let cow: Cow<[u8]> = Deserialize::deserialize(deserializer)?;
+ match cow {
+ Cow::Borrowed(bytes) => Ok(Cow::Borrowed(Bytes::new(bytes))),
+ Cow::Owned(bytes) => Ok(Cow::Owned(ByteBuf::from(bytes))),
+ }
+ }
+}
+
+#[cfg(any(feature = "std", feature = "alloc"))]
+impl<'de> Deserialize<'de> for Box<[u8]> {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ Deserialize::deserialize(deserializer).map(Vec::into_boxed_slice)
+ }
+}
+
+#[cfg(any(feature = "std", feature = "alloc"))]
+impl<'de> Deserialize<'de> for Box<Bytes> {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ let bytes: Box<[u8]> = Deserialize::deserialize(deserializer)?;
+ Ok(bytes.into())
+ }
+}
+
+impl<'de, T> Deserialize<'de> for Option<T>
+where
+ T: Deserialize<'de>,
+{
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ struct BytesVisitor<T> {
+ out: PhantomData<T>,
+ }
+
+ impl<'de, T> Visitor<'de> for BytesVisitor<T>
+ where
+ T: Deserialize<'de>,
+ {
+ type Value = Option<T>;
+
+ fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.write_str("optional byte array")
+ }
+
+ fn visit_unit<E: Error>(self) -> Result<Self::Value, E> {
+ Ok(None)
+ }
+
+ fn visit_none<E: Error>(self) -> Result<Self::Value, E> {
+ Ok(None)
+ }
+
+ fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ T::deserialize(deserializer).map(Some)
+ }
+ }
+
+ let visitor = BytesVisitor { out: PhantomData };
+ deserializer.deserialize_option(visitor)
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..871d6cb
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,113 @@
+//! Wrapper types to enable optimized handling of `&[u8]` and `Vec<u8>`.
+//!
+//! Without specialization, Rust forces Serde to treat `&[u8]` just like any
+//! other slice and `Vec<u8>` just like any other vector. In reality this
+//! particular slice and vector can often be serialized and deserialized in a
+//! more efficient, compact representation in many formats.
+//!
+//! When working with such a format, you can opt into specialized handling of
+//! `&[u8]` by wrapping it in `serde_bytes::Bytes` and `Vec<u8>` by wrapping it
+//! in `serde_bytes::ByteBuf`.
+//!
+//! Additionally this crate supports the Serde `with` attribute to enable
+//! efficient handling of `&[u8]` and `Vec<u8>` in structs without needing a
+//! wrapper type.
+//!
+//! ```
+//! # use serde_derive::{Deserialize, Serialize};
+//! use serde::{Deserialize, Serialize};
+//!
+//! #[derive(Deserialize, Serialize)]
+//! struct Efficient<'a> {
+//! #[serde(with = "serde_bytes")]
+//! bytes: &'a [u8],
+//!
+//! #[serde(with = "serde_bytes")]
+//! byte_buf: Vec<u8>,
+//! }
+//! ```
+
+#![doc(html_root_url = "https://docs.rs/serde_bytes/0.11.9")]
+#![cfg_attr(not(feature = "std"), no_std)]
+#![deny(missing_docs)]
+#![allow(
+ clippy::missing_errors_doc,
+ clippy::must_use_candidate,
+ clippy::needless_doctest_main
+)]
+
+mod bytes;
+mod de;
+mod ser;
+
+#[cfg(any(feature = "std", feature = "alloc"))]
+mod bytebuf;
+
+#[cfg(feature = "alloc")]
+extern crate alloc;
+
+#[cfg(any(feature = "std", feature = "alloc"))]
+use serde::Deserializer;
+
+use serde::Serializer;
+
+pub use crate::bytes::Bytes;
+pub use crate::de::Deserialize;
+pub use crate::ser::Serialize;
+
+#[cfg(any(feature = "std", feature = "alloc"))]
+pub use crate::bytebuf::ByteBuf;
+
+/// Serde `serialize_with` function to serialize bytes efficiently.
+///
+/// This function can be used with either of the following Serde attributes:
+///
+/// - `#[serde(with = "serde_bytes")]`
+/// - `#[serde(serialize_with = "serde_bytes::serialize")]`
+///
+/// ```
+/// # use serde_derive::Serialize;
+/// use serde::Serialize;
+///
+/// #[derive(Serialize)]
+/// struct Efficient<'a> {
+/// #[serde(with = "serde_bytes")]
+/// bytes: &'a [u8],
+///
+/// #[serde(with = "serde_bytes")]
+/// byte_buf: Vec<u8>,
+/// }
+/// ```
+pub fn serialize<T, S>(bytes: &T, serializer: S) -> Result<S::Ok, S::Error>
+where
+ T: ?Sized + Serialize,
+ S: Serializer,
+{
+ Serialize::serialize(bytes, serializer)
+}
+
+/// Serde `deserialize_with` function to deserialize bytes efficiently.
+///
+/// This function can be used with either of the following Serde attributes:
+///
+/// - `#[serde(with = "serde_bytes")]`
+/// - `#[serde(deserialize_with = "serde_bytes::deserialize")]`
+///
+/// ```
+/// # use serde_derive::Deserialize;
+/// use serde::Deserialize;
+///
+/// #[derive(Deserialize)]
+/// struct Packet {
+/// #[serde(with = "serde_bytes")]
+/// payload: Vec<u8>,
+/// }
+/// ```
+#[cfg(any(feature = "std", feature = "alloc"))]
+pub fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
+where
+ T: Deserialize<'de>,
+ D: Deserializer<'de>,
+{
+ Deserialize::deserialize(deserializer)
+}
diff --git a/src/ser.rs b/src/ser.rs
new file mode 100644
index 0000000..1cbed9f
--- /dev/null
+++ b/src/ser.rs
@@ -0,0 +1,136 @@
+use crate::Bytes;
+use serde::Serializer;
+
+#[cfg(any(feature = "std", feature = "alloc"))]
+use crate::ByteBuf;
+
+#[cfg(feature = "alloc")]
+use alloc::borrow::Cow;
+#[cfg(all(feature = "std", not(feature = "alloc")))]
+use std::borrow::Cow;
+
+#[cfg(feature = "alloc")]
+use alloc::boxed::Box;
+
+#[cfg(feature = "alloc")]
+use alloc::vec::Vec;
+
+/// Types that can be serialized via `#[serde(with = "serde_bytes")]`.
+pub trait Serialize {
+ #[allow(missing_docs)]
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer;
+}
+
+impl Serialize for [u8] {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ serializer.serialize_bytes(self)
+ }
+}
+
+#[cfg(any(feature = "std", feature = "alloc"))]
+impl Serialize for Vec<u8> {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ serializer.serialize_bytes(self)
+ }
+}
+
+impl Serialize for Bytes {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ serializer.serialize_bytes(self)
+ }
+}
+
+#[cfg(any(feature = "std", feature = "alloc"))]
+impl Serialize for ByteBuf {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ serializer.serialize_bytes(self)
+ }
+}
+
+#[cfg(any(feature = "std", feature = "alloc"))]
+impl<'a> Serialize for Cow<'a, [u8]> {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ serializer.serialize_bytes(self)
+ }
+}
+
+#[cfg(any(feature = "std", feature = "alloc"))]
+impl<'a> Serialize for Cow<'a, Bytes> {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ serializer.serialize_bytes(self)
+ }
+}
+
+impl<'a, T> Serialize for &'a T
+where
+ T: ?Sized + Serialize,
+{
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ (**self).serialize(serializer)
+ }
+}
+
+#[cfg(any(feature = "std", feature = "alloc"))]
+impl<T> Serialize for Box<T>
+where
+ T: ?Sized + Serialize,
+{
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ (**self).serialize(serializer)
+ }
+}
+
+impl<T> Serialize for Option<T>
+where
+ T: Serialize,
+{
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ struct AsBytes<T>(T);
+
+ impl<T> serde::Serialize for AsBytes<T>
+ where
+ T: Serialize,
+ {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ self.0.serialize(serializer)
+ }
+ }
+
+ match self {
+ Some(b) => serializer.serialize_some(&AsBytes(b)),
+ None => serializer.serialize_none(),
+ }
+ }
+}
diff --git a/tests/test_derive.rs b/tests/test_derive.rs
new file mode 100644
index 0000000..c7e97f1
--- /dev/null
+++ b/tests/test_derive.rs
@@ -0,0 +1,101 @@
+#![allow(clippy::derive_partial_eq_without_eq, clippy::ref_option_ref)]
+
+use serde_bytes::{ByteBuf, Bytes};
+use serde_derive::{Deserialize, Serialize};
+use serde_test::{assert_tokens, Token};
+use std::borrow::Cow;
+
+#[derive(Serialize, Deserialize, PartialEq, Debug)]
+struct Test<'a> {
+ #[serde(with = "serde_bytes")]
+ slice: &'a [u8],
+
+ #[serde(with = "serde_bytes")]
+ vec: Vec<u8>,
+
+ #[serde(with = "serde_bytes")]
+ bytes: &'a Bytes,
+
+ #[serde(with = "serde_bytes")]
+ byte_buf: ByteBuf,
+
+ #[serde(with = "serde_bytes")]
+ cow_slice: Cow<'a, [u8]>,
+
+ #[serde(with = "serde_bytes")]
+ cow_bytes: Cow<'a, Bytes>,
+
+ #[serde(with = "serde_bytes")]
+ boxed_slice: Box<[u8]>,
+
+ #[serde(with = "serde_bytes")]
+ boxed_bytes: Box<Bytes>,
+
+ #[serde(with = "serde_bytes")]
+ opt_slice: Option<&'a [u8]>,
+
+ #[serde(with = "serde_bytes")]
+ opt_vec: Option<Vec<u8>>,
+
+ #[serde(with = "serde_bytes")]
+ opt_cow_slice: Option<Cow<'a, [u8]>>,
+}
+
+#[derive(Serialize)]
+struct Dst {
+ #[serde(with = "serde_bytes")]
+ bytes: [u8],
+}
+
+#[test]
+fn test() {
+ let test = Test {
+ slice: b"...",
+ vec: b"...".to_vec(),
+ bytes: Bytes::new(b"..."),
+ byte_buf: ByteBuf::from(b"...".as_ref()),
+ cow_slice: Cow::Borrowed(b"..."),
+ cow_bytes: Cow::Borrowed(Bytes::new(b"...")),
+ boxed_slice: b"...".to_vec().into_boxed_slice(),
+ boxed_bytes: ByteBuf::from(b"...".as_ref()).into_boxed_bytes(),
+ opt_slice: Some(b"..."),
+ opt_vec: Some(b"...".to_vec()),
+ opt_cow_slice: Some(Cow::Borrowed(b"...")),
+ };
+
+ assert_tokens(
+ &test,
+ &[
+ Token::Struct {
+ name: "Test",
+ len: 11,
+ },
+ Token::Str("slice"),
+ Token::BorrowedBytes(b"..."),
+ Token::Str("vec"),
+ Token::Bytes(b"..."),
+ Token::Str("bytes"),
+ Token::BorrowedBytes(b"..."),
+ Token::Str("byte_buf"),
+ Token::Bytes(b"..."),
+ Token::Str("cow_slice"),
+ Token::BorrowedBytes(b"..."),
+ Token::Str("cow_bytes"),
+ Token::BorrowedBytes(b"..."),
+ Token::Str("boxed_slice"),
+ Token::Bytes(b"..."),
+ Token::Str("boxed_bytes"),
+ Token::Bytes(b"..."),
+ Token::Str("opt_slice"),
+ Token::Some,
+ Token::BorrowedBytes(b"..."),
+ Token::Str("opt_vec"),
+ Token::Some,
+ Token::Bytes(b"..."),
+ Token::Str("opt_cow_slice"),
+ Token::Some,
+ Token::BorrowedBytes(b"..."),
+ Token::StructEnd,
+ ],
+ );
+}
diff --git a/tests/test_partialeq.rs b/tests/test_partialeq.rs
new file mode 100644
index 0000000..a57d194
--- /dev/null
+++ b/tests/test_partialeq.rs
@@ -0,0 +1,15 @@
+#![allow(clippy::needless_pass_by_value)]
+
+use serde_bytes::{ByteBuf, Bytes};
+
+fn _bytes_eq_slice(bytes: &Bytes, slice: &[u8]) -> bool {
+ bytes == slice
+}
+
+fn _bytebuf_eq_vec(bytebuf: ByteBuf, vec: Vec<u8>) -> bool {
+ bytebuf == vec
+}
+
+fn _bytes_eq_bytestring(bytes: &Bytes) -> bool {
+ bytes == b"..."
+}
diff --git a/tests/test_serde.rs b/tests/test_serde.rs
new file mode 100644
index 0000000..d48e3fd
--- /dev/null
+++ b/tests/test_serde.rs
@@ -0,0 +1,59 @@
+use serde_bytes::{ByteBuf, Bytes};
+use serde_test::{assert_de_tokens, assert_ser_tokens, assert_tokens, Token};
+
+#[test]
+fn test_bytes() {
+ let empty = Bytes::new(&[]);
+ assert_tokens(&empty, &[Token::BorrowedBytes(b"")]);
+ assert_ser_tokens(&empty, &[Token::Bytes(b"")]);
+ assert_ser_tokens(&empty, &[Token::ByteBuf(b"")]);
+ assert_de_tokens(&empty, &[Token::BorrowedStr("")]);
+
+ let buf = vec![65, 66, 67];
+ let bytes = Bytes::new(&buf);
+ assert_tokens(&bytes, &[Token::BorrowedBytes(b"ABC")]);
+ assert_ser_tokens(&bytes, &[Token::Bytes(b"ABC")]);
+ assert_ser_tokens(&bytes, &[Token::ByteBuf(b"ABC")]);
+ assert_de_tokens(&bytes, &[Token::BorrowedStr("ABC")]);
+}
+
+#[test]
+fn test_byte_buf() {
+ let empty = ByteBuf::new();
+ assert_tokens(&empty, &[Token::BorrowedBytes(b"")]);
+ assert_tokens(&empty, &[Token::Bytes(b"")]);
+ assert_tokens(&empty, &[Token::ByteBuf(b"")]);
+ assert_de_tokens(&empty, &[Token::BorrowedStr("")]);
+ assert_de_tokens(&empty, &[Token::Str("")]);
+ assert_de_tokens(&empty, &[Token::String("")]);
+ assert_de_tokens(&empty, &[Token::Seq { len: None }, Token::SeqEnd]);
+ assert_de_tokens(&empty, &[Token::Seq { len: Some(0) }, Token::SeqEnd]);
+
+ let buf = ByteBuf::from(vec![65, 66, 67]);
+ assert_tokens(&buf, &[Token::BorrowedBytes(b"ABC")]);
+ assert_tokens(&buf, &[Token::Bytes(b"ABC")]);
+ assert_tokens(&buf, &[Token::ByteBuf(b"ABC")]);
+ assert_de_tokens(&buf, &[Token::BorrowedStr("ABC")]);
+ assert_de_tokens(&buf, &[Token::Str("ABC")]);
+ assert_de_tokens(&buf, &[Token::String("ABC")]);
+ assert_de_tokens(
+ &buf,
+ &[
+ Token::Seq { len: None },
+ Token::U8(65),
+ Token::U8(66),
+ Token::U8(67),
+ Token::SeqEnd,
+ ],
+ );
+ assert_de_tokens(
+ &buf,
+ &[
+ Token::Seq { len: Some(3) },
+ Token::U8(65),
+ Token::U8(66),
+ Token::U8(67),
+ Token::SeqEnd,
+ ],
+ );
+}