├── .gitignore ├── .travis.yml ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md └── src ├── error.rs ├── lib.rs ├── methods.rs ├── plain.rs └── tests.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | /src/*.bk 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | rust: 3 | - stable 4 | - beta 5 | - nightly 6 | 7 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "plain" 3 | version = "0.2.3" 4 | authors = ["jzr"] 5 | license = "MIT/Apache-2.0" 6 | readme = "README.md" 7 | repository = "https://github.com/randomites/plain" 8 | homepage = "https://github.com/randomites/plain" 9 | documentation = "https://docs.rs/plain" 10 | description = "A small Rust library that allows users to reinterpret data of certain types safely." 11 | 12 | categories = ["no-std", "data-structures", "parsing"] 13 | keywords = ["plain", "pod", "ffi", "memory"] 14 | 15 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Plain contributors 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # libplain 2 | 3 | [![Build Status](https://travis-ci.org/randomites/plain.svg?branch=master)](https://travis-ci.org/randomites/plain) 4 | [![Current Crates.io Version](https://img.shields.io/crates/v/plain.svg)](https://crates.io/crates/plain) 5 | [![Current Documentation](https://docs.rs/plain/badge.svg)](https://docs.rs/plain) 6 | 7 | A small Rust library that allows users to interpret arrays of bytes 8 | as certain kinds of structures safely. 9 | 10 | This crate provides an unsafe trait [`Plain`](https://docs.rs/plain/0.2.0/plain/trait.Plain.html), which the user 11 | of the crate uses to mark types for which operations of this library are safe. 12 | See [`Plain`](https://docs.rs/plain/0.2.0/plain/trait.Plain.html) for the contractual obligation. 13 | 14 | Other than that, everything else in this crate is perfectly safe to use as long 15 | as the `Plain` trait is not implemented on inadmissible types (similar to how 16 | `Send` and `Sync` in the standard library work). 17 | 18 | # Purpose 19 | 20 | In low level systems development, it is sometimes necessary to 21 | interpret locations in memory as data structures. Functions of 22 | this crate serve to avoid pitfalls associated with that, without 23 | having to resort to big, full-featured (de)serialization libraries. 24 | 25 | On the other hand, this crate contains no provisions when it comes 26 | to handling differences in encoding and byte ordering between 27 | platforms. As such, it is entirely unsuitable for processing 28 | external data such as file contents or network packets. 29 | 30 | # Examples 31 | 32 | To start using the crate, simply do `extern crate plain;`. 33 | 34 | If you want your plain types to have methods from this crate, also include `use plain.Plain;`. 35 | 36 | Then it's just a matter of marking the right types and using them. 37 | 38 | ```rust 39 | extern crate plain; 40 | use plain::Plain; 41 | use std::mem; 42 | 43 | 44 | #[repr(C)] 45 | #[derive(Default)] 46 | struct ELF64Header { 47 | pub e_ident: [u8; 16], 48 | pub e_type: u16, 49 | pub e_machine: u16, 50 | pub e_version: u32, 51 | pub e_entry: u64, 52 | pub e_phoff: u64, 53 | pub e_shoff: u64, 54 | pub e_flags: u32, 55 | pub e_ehsize: u16, 56 | pub e_phentsize: u16, 57 | pub e_phnum: u16, 58 | pub e_shentsize: u16, 59 | pub e_shnum: u16, 60 | pub e_shstrndx: u16, 61 | } 62 | 63 | // SAFE: ELF64Header satisfies all the requirements of `Plain`. 64 | unsafe impl Plain for ELF64Header {} 65 | 66 | impl ELF64Header { 67 | fn from_bytes(buf: &[u8]) -> &ELF64Header { 68 | plain::from_bytes(buf).expect("The buffer is either too short or not aligned!") 69 | } 70 | 71 | fn from_mut_bytes(buf: &mut [u8]) -> &mut ELF64Header { 72 | plain::from_mut_bytes(buf).expect("The buffer is either too short or not aligned!") 73 | } 74 | 75 | fn copy_from_bytes(buf: &[u8]) -> ELF64Header { 76 | let mut h = ELF64Header::default(); 77 | h.copy_from_bytes(buf).expect("The buffer is too short!"); 78 | h 79 | } 80 | } 81 | 82 | # fn process_elf(elf: &ELF64Header) {} 83 | 84 | // Conditional copying for ultimate hackery. 85 | fn opportunistic_elf_processing(buf: &[u8]) { 86 | if plain::is_aligned::(buf) { 87 | // No copy necessary. 88 | let elf_ref = ELF64Header::from_bytes(buf); 89 | process_elf(elf_ref); 90 | } else { 91 | // Not aligned properly, copy to stack first. 92 | let elf = ELF64Header::copy_from_bytes(buf); 93 | process_elf(&elf); 94 | } 95 | } 96 | 97 | #[repr(C)] 98 | #[derive(Default, Copy, Clone)] 99 | struct ArrayEntry { 100 | pub name: [u8; 32], 101 | pub tag: u32, 102 | pub score: u32, 103 | } 104 | 105 | // SAFE: ArrayEntry satisfies all the requirements of `Plain`. 106 | unsafe impl Plain for ArrayEntry {} 107 | 108 | fn array_from_bytes(buf: &[u8]) -> &[ArrayEntry] { 109 | // NOTE: length is not a concern here, 110 | // since slice_from_bytes() can return empty slice. 111 | 112 | match plain::slice_from_bytes(buf) { 113 | Err(_) => panic!("The buffer is not aligned!"), 114 | Ok(arr) => arr, 115 | } 116 | } 117 | 118 | fn array_from_unaligned_bytes(buf: &[u8]) -> Vec { 119 | let length = buf.len() / mem::size_of::(); 120 | let mut result = vec![ArrayEntry::default(); length]; 121 | (&mut result).copy_from_bytes(buf).expect("Cannot fail here."); 122 | result 123 | } 124 | 125 | # fn main() {} 126 | ``` 127 | 128 | # Comparison to [`pod`](https://crates.io/crates/pod) 129 | 130 | [`pod`](https://crates.io/crates/pod) is another crate created to help working with plain data. 131 | The major difference between `pod` and `plain` is scope. 132 | 133 | `plain` currently provides only a few functions (+method wrappers) and its implementation 134 | involves very few lines of unsafe code. It can be used in `no_std` code. Also, it doesn't 135 | deal with [endianness](https://en.wikipedia.org/wiki/Endianness) in any way, 136 | so it is only suitable for certain kinds of low-level work. 137 | 138 | `pod`, on the other hand, provides a wide arsenal 139 | of various methods, most of which may be unnecessary for a given use case. 140 | It has dependencies on `std` as well as other crates, but among other things 141 | it provides tools to handle endianness properly. 142 | 143 | In short, `plain` is much, much _plainer_... 144 | 145 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] 2 | pub enum Error { 3 | TooShort, 4 | BadAlignment, 5 | } 6 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A small Rust library that allows users to interpret arrays of bytes 2 | //! as certain kinds of structures safely. 3 | //! 4 | //! This crate provides an unsafe trait [`Plain`](trait.Plain.html), which the user 5 | //! of the crate uses to mark types for which operations of this library are safe. 6 | //! See [`Plain`](trait.Plain.html) for the contractual obligation. 7 | //! 8 | //! Other than that, everything else in this crate is perfectly safe to use as long 9 | //! as the `Plain` trait is not implemented on inadmissible types (similar to how 10 | //! `Send` and `Sync` in the standard library work). 11 | //! 12 | //! # Purpose 13 | //! 14 | //! In low level systems development, it is sometimes necessary to 15 | //! interpret locations in memory as data structures. Functions of 16 | //! this crate serve to avoid pitfalls associated with that, without 17 | //! having to resort to big, full-featured (de)serialization libraries. 18 | //! 19 | //! On the other hand, this crate contains no provisions when it comes 20 | //! to handling differences in encoding and byte ordering between 21 | //! platforms. As such, it is entirely unsuitable for processing 22 | //! external data such as file contents or network packets. 23 | //! 24 | //! # Examples 25 | //! 26 | //! To start using the crate, simply do `extern crate plain;`. 27 | //! 28 | //! If you want your plain types to have methods from this crate, also include `use plain.Plain;`. 29 | //! 30 | //! Then it's just a matter of marking the right types and using them. 31 | //! 32 | //! ``` 33 | //! 34 | //! extern crate plain; 35 | //! use plain::Plain; 36 | //! use std::mem; 37 | //! 38 | //! 39 | //! #[repr(C)] 40 | //! #[derive(Default)] 41 | //! struct ELF64Header { 42 | //! pub e_ident: [u8; 16], 43 | //! pub e_type: u16, 44 | //! pub e_machine: u16, 45 | //! pub e_version: u32, 46 | //! pub e_entry: u64, 47 | //! pub e_phoff: u64, 48 | //! pub e_shoff: u64, 49 | //! pub e_flags: u32, 50 | //! pub e_ehsize: u16, 51 | //! pub e_phentsize: u16, 52 | //! pub e_phnum: u16, 53 | //! pub e_shentsize: u16, 54 | //! pub e_shnum: u16, 55 | //! pub e_shstrndx: u16, 56 | //! } 57 | //! 58 | //! // SAFE: ELF64Header satisfies all the requirements of `Plain`. 59 | //! unsafe impl Plain for ELF64Header {} 60 | //! 61 | //! impl ELF64Header { 62 | //! fn from_bytes(buf: &[u8]) -> &ELF64Header { 63 | //! plain::from_bytes(buf).expect("The buffer is either too short or not aligned!") 64 | //! } 65 | //! 66 | //! fn from_mut_bytes(buf: &mut [u8]) -> &mut ELF64Header { 67 | //! plain::from_mut_bytes(buf).expect("The buffer is either too short or not aligned!") 68 | //! } 69 | //! 70 | //! fn copy_from_bytes(buf: &[u8]) -> ELF64Header { 71 | //! let mut h = ELF64Header::default(); 72 | //! h.copy_from_bytes(buf).expect("The buffer is too short!"); 73 | //! h 74 | //! } 75 | //! } 76 | //! 77 | //! # fn process_elf(elf: &ELF64Header) {} 78 | //! 79 | //! // Conditional copying for ultimate hackery. 80 | //! fn opportunistic_elf_processing(buf: &[u8]) { 81 | //! if plain::is_aligned::(buf) { 82 | //! // No copy necessary. 83 | //! let elf_ref = ELF64Header::from_bytes(buf); 84 | //! process_elf(elf_ref); 85 | //! } else { 86 | //! // Not aligned properly, copy to stack first. 87 | //! let elf = ELF64Header::copy_from_bytes(buf); 88 | //! process_elf(&elf); 89 | //! } 90 | //! } 91 | //! 92 | //! #[repr(C)] 93 | //! #[derive(Default, Copy, Clone)] 94 | //! struct ArrayEntry { 95 | //! pub name: [u8; 32], 96 | //! pub tag: u32, 97 | //! pub score: u32, 98 | //! } 99 | //! 100 | //! // SAFE: ArrayEntry satisfies all the requirements of `Plain`. 101 | //! unsafe impl Plain for ArrayEntry {} 102 | //! 103 | //! fn array_from_bytes(buf: &[u8]) -> &[ArrayEntry] { 104 | //! // NOTE: length is not a concern here, 105 | //! // since slice_from_bytes() can return empty slice. 106 | //! 107 | //! match plain::slice_from_bytes(buf) { 108 | //! Err(_) => panic!("The buffer is not aligned!"), 109 | //! Ok(arr) => arr, 110 | //! } 111 | //! } 112 | //! 113 | //! fn array_from_unaligned_bytes(buf: &[u8]) -> Vec { 114 | //! let length = buf.len() / mem::size_of::(); 115 | //! let mut result = vec![ArrayEntry::default(); length]; 116 | //! (&mut result).copy_from_bytes(buf).expect("Cannot fail here."); 117 | //! result 118 | //! } 119 | //! 120 | //! # fn main() {} 121 | //! 122 | //! ``` 123 | //! 124 | //! # Comparison to [`pod`](https://crates.io/crates/pod) 125 | //! 126 | //! [`pod`](https://crates.io/crates/pod) is another crate created to help working with plain data. 127 | //! The major difference between `pod` and `plain` is scope. 128 | //! 129 | //! `plain` currently provides only a few functions (+method wrappers) and its implementation 130 | //! involves very few lines of unsafe code. It can be used in `no_std` code. Also, it doesn't 131 | //! deal with [endianness](https://en.wikipedia.org/wiki/Endianness) in any way, 132 | //! so it is only suitable for certain kinds of low-level work. 133 | //! 134 | //! `pod`, on the other hand, provides a wide arsenal 135 | //! of various methods, most of which may be unnecessary for a given use case. 136 | //! It has dependencies on `std` as well as other crates, but among other things 137 | //! it provides tools to handle endianness properly. 138 | //! 139 | //! In short, `plain` is much, much _plainer_... 140 | #![no_std] 141 | 142 | mod error; 143 | pub use error::Error; 144 | 145 | mod plain; 146 | pub use plain::Plain; 147 | 148 | mod methods; 149 | pub use methods::{as_bytes, as_mut_bytes, copy_from_bytes, from_bytes, from_mut_bytes, is_aligned, 150 | slice_from_bytes, slice_from_bytes_len, slice_from_mut_bytes, 151 | slice_from_mut_bytes_len}; 152 | 153 | #[cfg(test)] 154 | #[macro_use] 155 | extern crate std; 156 | 157 | #[cfg(test)] 158 | mod tests; 159 | -------------------------------------------------------------------------------- /src/methods.rs: -------------------------------------------------------------------------------- 1 | use core::{mem, slice}; 2 | 3 | use {Error, Plain}; 4 | 5 | /// Check if a byte slice is aligned suitably for type T. 6 | #[inline] 7 | pub fn is_aligned(bytes: &[u8]) -> bool { 8 | ((bytes.as_ptr() as usize) % mem::align_of::()) == 0 9 | } 10 | 11 | #[inline(always)] 12 | fn check_alignment(bytes: &[u8]) -> Result<(), Error> { 13 | if is_aligned::(bytes) { 14 | Ok(()) 15 | } else { 16 | Err(Error::BadAlignment) 17 | } 18 | } 19 | 20 | #[inline(always)] 21 | fn check_length(bytes: &[u8], len: usize) -> Result<(), Error> { 22 | if mem::size_of::() > 0 && (bytes.len() / mem::size_of::()) < len { 23 | Err(Error::TooShort) 24 | } else { 25 | Ok(()) 26 | } 27 | } 28 | 29 | /// Interpret data as bytes. Not safe for data with padding. 30 | #[inline(always)] 31 | pub unsafe fn as_bytes(s: &S) -> &[u8] 32 | where 33 | S: ?Sized, 34 | { 35 | let bptr = s as *const S as *const u8; 36 | let bsize = mem::size_of_val(s); 37 | slice::from_raw_parts(bptr, bsize) 38 | } 39 | 40 | /// Interpret data as mutable bytes. 41 | /// Reading is not safe for data with padding. Writing is ok. 42 | #[inline(always)] 43 | pub unsafe fn as_mut_bytes(s: &mut S) -> &mut [u8] 44 | where 45 | S: Plain + ?Sized, 46 | { 47 | let bptr = s as *mut S as *mut u8; 48 | let bsize = mem::size_of_val(s); 49 | slice::from_raw_parts_mut(bptr, bsize) 50 | } 51 | 52 | /// Safely converts a byte slice to a reference. 53 | /// 54 | /// However, if the byte slice is not long enough 55 | /// to contain target type, or if it doesn't 56 | /// satisfy the type's alignment requirements, 57 | /// the function returns an error. 58 | /// 59 | /// The function will not fail when the 60 | /// byte slice is longer than necessary, since it is 61 | /// a common practice to interpret the beginning of 62 | /// a slice as a fixed-size header. 63 | /// 64 | /// In many cases it is preferrable to allocate 65 | /// a value/slice of the target type and use 66 | /// [`copy_from_bytes()`](fn.copy_from_bytes.html) to copy 67 | /// data instead. That way, any issues with alignment 68 | /// are implicitly avoided. 69 | /// 70 | #[inline] 71 | pub fn from_bytes(bytes: &[u8]) -> Result<&T, Error> 72 | where 73 | T: Plain, 74 | { 75 | try!(check_alignment::(bytes)); 76 | try!(check_length::(bytes, 1)); 77 | Ok(unsafe { &*(bytes.as_ptr() as *const T) }) 78 | } 79 | 80 | /// Similar to [`from_bytes()`](fn.from_bytes.html), 81 | /// except that the output is a slice of T, instead 82 | /// of a reference to a single T. All concerns about 83 | /// alignment also apply here, but size is handled 84 | /// differently. 85 | /// 86 | /// The result slice's length is set to be 87 | /// `bytes.len() / size_of::()`, and there 88 | /// are no requirements for input size. I.e. 89 | /// the result may be empty slice, and the input 90 | /// slice doesn't necessarily have to end on `T`'s 91 | /// boundary. The latter has pragmatic reasons: If the 92 | /// length of the array is not known in advance, 93 | /// e.g. if it's terminated by a special element, 94 | /// it's perfectly legal to turn the whole rest 95 | /// of data into `&[T]` and set the proper length 96 | /// after inspecting the array. 97 | /// 98 | /// In many cases it is preferrable to allocate 99 | /// a value/slice of the target type and use 100 | /// [`copy_from_bytes()`](fn.copy_from_bytes.html) to copy 101 | /// data instead. That way, any issues with alignment 102 | /// are implicitly avoided. 103 | /// 104 | #[inline] 105 | pub fn slice_from_bytes(bytes: &[u8]) -> Result<&[T], Error> 106 | where 107 | T: Plain, 108 | { 109 | let len = bytes.len() / mem::size_of::(); 110 | slice_from_bytes_len(bytes, len) 111 | } 112 | 113 | 114 | /// Same as [`slice_from_bytes()`](fn.slice_from_bytes.html), 115 | /// except that it takes explicit length of the result slice. 116 | /// 117 | /// If the input slice cannot satisfy the length, returns error. 118 | /// The input slice is allowed to be longer than necessary. 119 | /// 120 | #[inline] 121 | pub fn slice_from_bytes_len(bytes: &[u8], len: usize) -> Result<&[T], Error> 122 | where 123 | T: Plain, 124 | { 125 | try!(check_alignment::(bytes)); 126 | try!(check_length::(bytes, len)); 127 | Ok(unsafe { 128 | slice::from_raw_parts(bytes.as_ptr() as *const T, len) 129 | }) 130 | } 131 | 132 | /// See [`from_bytes()`](fn.from_bytes.html). 133 | /// 134 | /// Does the same, except with mutable references. 135 | /// 136 | #[inline] 137 | pub fn from_mut_bytes(bytes: &mut [u8]) -> Result<&mut T, Error> 138 | where 139 | T: Plain, 140 | { 141 | try!(check_alignment::(bytes)); 142 | try!(check_length::(bytes, 1)); 143 | Ok(unsafe { &mut *(bytes.as_mut_ptr() as *mut T) }) 144 | } 145 | 146 | /// See [`slice_from_bytes()`](fn.slice_from_bytes.html). 147 | /// 148 | /// Does the same, except with mutable references. 149 | /// 150 | #[inline] 151 | pub fn slice_from_mut_bytes(bytes: &mut [u8]) -> Result<&mut [T], Error> 152 | where 153 | T: Plain, 154 | { 155 | let len = bytes.len() / mem::size_of::(); 156 | slice_from_mut_bytes_len(bytes, len) 157 | } 158 | 159 | /// See [`slice_from_bytes_len()`](fn.slice_from_bytes_len.html). 160 | /// 161 | /// Does the same, except with mutable references. 162 | /// 163 | #[inline] 164 | pub fn slice_from_mut_bytes_len(bytes: &mut [u8], len: usize) -> Result<&mut [T], Error> 165 | where 166 | T: Plain, 167 | { 168 | try!(check_alignment::(bytes)); 169 | try!(check_length::(bytes, len)); 170 | Ok(unsafe { 171 | slice::from_raw_parts_mut(bytes.as_ptr() as *mut T, len) 172 | }) 173 | } 174 | 175 | /// Copies data from a byte slice into existing memory. 176 | /// Suitable when [`from_bytes()`](fn.from_bytes.html) would normally 177 | /// be used, but the data is not aligned properly in memory. 178 | /// 179 | /// For an example how to use it, see crate-level documentation. 180 | /// 181 | #[inline] 182 | pub fn copy_from_bytes(into: &mut T, bytes: &[u8]) -> Result<(), Error> 183 | where 184 | T: Plain + ?Sized, 185 | { 186 | let sz = mem::size_of_val(into); 187 | 188 | if bytes.len() < sz { 189 | return Err(Error::TooShort); 190 | } 191 | 192 | unsafe { 193 | as_mut_bytes(into).copy_from_slice(&bytes[..sz]); 194 | } 195 | 196 | Ok(()) 197 | } 198 | -------------------------------------------------------------------------------- /src/plain.rs: -------------------------------------------------------------------------------- 1 | use Error; 2 | 3 | /// A trait for plain data types that can be safely read from a byte slice. 4 | /// 5 | /// A type can be [`Plain`](trait.Plain.html) if it is `#repr(C)` and only contains 6 | /// data with no possible invalid values. Types that _can't_ be `Plain` 7 | /// include, but are not limited to, `bool`, `char`, `enum`s, tuples, 8 | /// pointers and references. 9 | /// 10 | /// At this moment, `Drop` types are also not legal, because 11 | /// compiler adds a special "drop flag" into the type. This is slated 12 | /// to change in the future. 13 | /// 14 | /// On the other hand, arrays of a `Plain` type, and 15 | /// structures where all members are plain (and not `Drop`), are okay. 16 | /// 17 | /// Structures that are not `#repr(C)`, while not necessarily illegal 18 | /// in principle, are largely useless because they don't have a stable 19 | /// layout. For example, the compiler is allowed to reorder fields 20 | /// arbitrarily. 21 | /// 22 | /// All methods of this trait are implemented automatically as wrappers 23 | /// for crate-level funtions. 24 | /// 25 | pub unsafe trait Plain { 26 | #[inline(always)] 27 | fn from_bytes(bytes: &[u8]) -> Result<&Self, Error> 28 | where 29 | Self: Sized, 30 | { 31 | ::from_bytes(bytes) 32 | } 33 | 34 | #[inline(always)] 35 | fn slice_from_bytes(bytes: &[u8]) -> Result<&[Self], Error> 36 | where 37 | Self: Sized, 38 | { 39 | ::slice_from_bytes(bytes) 40 | } 41 | 42 | #[inline(always)] 43 | fn slice_from_bytes_len(bytes: &[u8], len: usize) -> Result<&[Self], Error> 44 | where 45 | Self: Sized, 46 | { 47 | ::slice_from_bytes_len(bytes, len) 48 | } 49 | 50 | #[inline(always)] 51 | fn from_mut_bytes(bytes: &mut [u8]) -> Result<&mut Self, Error> 52 | where 53 | Self: Sized, 54 | { 55 | ::from_mut_bytes(bytes) 56 | } 57 | 58 | #[inline(always)] 59 | fn slice_from_mut_bytes(bytes: &mut [u8]) -> Result<&mut [Self], Error> 60 | where 61 | Self: Sized, 62 | { 63 | ::slice_from_mut_bytes(bytes) 64 | } 65 | 66 | #[inline(always)] 67 | fn slice_from_mut_bytes_len(bytes: &mut [u8], len: usize) -> Result<&mut [Self], Error> 68 | where 69 | Self: Sized, 70 | { 71 | ::slice_from_mut_bytes_len(bytes, len) 72 | } 73 | 74 | #[inline(always)] 75 | fn copy_from_bytes(&mut self, bytes: &[u8]) -> Result<(), Error> { 76 | ::copy_from_bytes(self, bytes) 77 | } 78 | } 79 | 80 | unsafe impl Plain for u8 {} 81 | unsafe impl Plain for u16 {} 82 | unsafe impl Plain for u32 {} 83 | unsafe impl Plain for u64 {} 84 | unsafe impl Plain for usize {} 85 | 86 | unsafe impl Plain for i8 {} 87 | unsafe impl Plain for i16 {} 88 | unsafe impl Plain for i32 {} 89 | unsafe impl Plain for i64 {} 90 | unsafe impl Plain for isize {} 91 | 92 | unsafe impl Plain for [S] 93 | where 94 | S: Plain, 95 | { 96 | } 97 | 98 | macro_rules! impl_array { 99 | ($($n:tt)+) => { 100 | $(unsafe impl Plain for [A; $n] {})+ 101 | } 102 | } 103 | 104 | impl_array! {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 64 128 256 1024} 105 | -------------------------------------------------------------------------------- /src/tests.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | use ::*; 4 | use core::mem; 5 | 6 | #[repr(C)] 7 | #[derive(Debug, Default, Copy, Eq, Clone, PartialEq)] 8 | struct Dummy1 { 9 | field1: u64, 10 | field2: u32, 11 | field3: u16, 12 | field4: u8, 13 | field5: u8, 14 | } 15 | 16 | unsafe impl Plain for Dummy1 {} 17 | 18 | #[repr(C)] 19 | #[derive(Debug, Default, Copy, Eq, Clone, PartialEq)] 20 | struct Dummy2 { 21 | field1: u8, 22 | field2: u8, 23 | field3: u16, 24 | field4: u32, 25 | field5: u64, 26 | } 27 | 28 | unsafe impl Plain for Dummy2 {} 29 | 30 | fn as_bytes(r: &T) -> &[u8] { 31 | unsafe { methods::as_bytes(r) } 32 | } 33 | 34 | fn as_mut_bytes(r: &mut T) -> &mut [u8] { 35 | unsafe { methods::as_mut_bytes(r) } 36 | } 37 | 38 | #[test] 39 | fn one_too_short() { 40 | let b = vec![0u8; mem::size_of::() - 1]; 41 | 42 | let r = Dummy1::from_bytes(&b); 43 | assert!(r == Err(Error::TooShort)); 44 | } 45 | 46 | #[test] 47 | fn unaligned() { 48 | let b = vec![0u8; mem::size_of::() + 1]; 49 | let b = &b[1..]; 50 | 51 | let r = Dummy1::from_bytes(&b); 52 | assert!(r == Err(Error::BadAlignment)); 53 | } 54 | 55 | #[test] 56 | fn copy_test() { 57 | let t1 = Dummy1 { 58 | field1: 0xaaaaaaaaaaaaaaaau64, 59 | field2: 0xbbbbbbbbu32, 60 | field3: 0xccccu16, 61 | field4: 0xddu8, 62 | field5: 0xeeu8, 63 | }; 64 | 65 | let mut t2 = Dummy2::default(); 66 | 67 | assert!(t2.copy_from_bytes(as_bytes(&t1)) == Ok(())); 68 | 69 | assert!(t2.field1 == 0xaau8); 70 | assert!(t2.field2 == 0xaau8); 71 | assert!(t2.field3 == 0xaaaau16); 72 | assert!(t2.field4 == 0xaaaaaaaau32); 73 | assert!(t2.field5 == 0xbbbbbbbbccccddeeu64 || t2.field5 == 0xeeddccccbbbbbbbbu64); 74 | 75 | let sz = mem::size_of::(); 76 | assert!(t2.copy_from_bytes(&as_bytes(&t1)[..sz - 1]) == Err(Error::TooShort)); 77 | } 78 | 79 | #[test] 80 | fn huge_array() { 81 | // Issue #2: Integer overflow in multiplication. 82 | 83 | let huge: usize = 1 << (mem::size_of::() * 8 - 1); 84 | 85 | let mut b = vec![0u8]; 86 | assert_eq!(slice_from_bytes_len::(&b, huge), Err(Error::TooShort)); 87 | 88 | assert_eq!( 89 | slice_from_mut_bytes_len::(&mut b, huge), 90 | Err(Error::TooShort) 91 | ); 92 | } 93 | 94 | #[test] 95 | fn basic_function() { 96 | let t1 = Dummy1 { 97 | field1: 0xaaaaaaaaaaaaaaaau64, 98 | field2: 0xbbbbbbbbu32, 99 | field3: 0xccccu16, 100 | field4: 0xddu8, 101 | field5: 0xeeu8, 102 | }; 103 | 104 | let r1: &Dummy2 = from_bytes(as_bytes(&t1)).unwrap(); 105 | 106 | assert!(r1.field1 == 0xaau8); 107 | assert!(r1.field2 == 0xaau8); 108 | assert!(r1.field3 == 0xaaaau16); 109 | assert!(r1.field4 == 0xaaaaaaaau32); 110 | assert!(r1.field5 == 0xbbbbbbbbccccddeeu64 || r1.field5 == 0xeeddccccbbbbbbbbu64); 111 | 112 | let r2 = as_bytes(r1); 113 | assert!(r2.len() == mem::size_of::()); 114 | assert!(r2[5] == 0xaa); 115 | 116 | let size = r2.len(); 117 | let r3 = as_bytes(r2); 118 | assert!(r3.len() == size); 119 | 120 | let r4 = Dummy1::from_bytes(r3).unwrap(); 121 | 122 | let r5 = from_bytes::(as_bytes(r4)).unwrap(); 123 | 124 | { 125 | let r6 = slice_from_bytes::(as_bytes(r5)).unwrap(); 126 | 127 | assert!(r6.len() == 1); 128 | assert!(t1 == r6[0]); 129 | } 130 | 131 | let r7 = slice_from_bytes::(as_bytes(r5)).unwrap(); 132 | assert!(r7.len() == 2); 133 | 134 | assert!(r7[0] == 0xaaaaaaaaaaaaaaaau64); 135 | assert!(r7[1] == 0xbbbbbbbbccccddeeu64 || r7[1] == 0xeeddccccbbbbbbbbu64); 136 | } 137 | 138 | #[test] 139 | fn arrays() { 140 | fn is_plain(_: T) {} 141 | 142 | is_plain([0u8; 1]); 143 | is_plain([0u8; 17]); 144 | is_plain([0u8; 32]); 145 | } 146 | --------------------------------------------------------------------------------