From e1ed30218282d0089a9d0165b468368accf68388 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Fri, 21 Mar 2025 12:02:21 +0100 Subject: [PATCH] init commit --- .github/workflows/ci.yml | 72 ++++++++ .gitignore | 2 + CHANGELOG.md | 15 ++ Cargo.toml | 15 ++ LICENSE-APACHE | 201 +++++++++++++++++++++ LICENSE-MIT | 21 +++ README.md | 9 + docs.sh | 3 + src/lib.rs | 375 +++++++++++++++++++++++++++++++++++++++ 9 files changed, 713 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 .gitignore create mode 100644 CHANGELOG.md create mode 100644 Cargo.toml create mode 100644 LICENSE-APACHE create mode 100644 LICENSE-MIT create mode 100644 README.md create mode 100755 docs.sh create mode 100644 src/lib.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..44ed7b5 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,72 @@ +name: ci +on: [push, pull_request] + +jobs: + check: + name: Check build + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - run: cargo check --release + + test: + name: Run Tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - name: Install nextest + uses: taiki-e/install-action@nextest + - run: cargo nextest run --all-features + - run: cargo test --doc + + msrv: + name: Check MSRV + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@1.85.1 + - run: cargo check --release + + cross-check: + name: Check Cross-Compilation + runs-on: ubuntu-latest + strategy: + matrix: + target: + - armv7-unknown-linux-gnueabihf + - thumbv7em-none-eabihf + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + with: + targets: "armv7-unknown-linux-gnueabihf, thumbv7em-none-eabihf" + - run: cargo check --release --target=${{matrix.target}} --no-default-features + + fmt: + name: Check formatting + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - run: cargo fmt --all -- --check + + docs: + name: Check Documentation Build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@nightly + - run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc --all-features + + clippy: + name: Clippy + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - run: cargo clippy -- -D warnings diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4fffb2f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +/Cargo.lock diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..347635f --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,15 @@ +Change Log +======= + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/) +and this project adheres to [Semantic Versioning](http://semver.org/). + +# [unreleased] + +# [v0.1.0] + +Initial release + +[v0.1.0]: https://egit.irs.uni-stuttgart.de/rust/raw-slice/releases/tag/v0.1.0 diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..fd88b74 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "rawslice" +version = "0.1.0" +edition = "2024" +rust-version = "1.85.1" +authors = ["Robin Mueller "] +description = "Generic low-level raw slice types" +homepage = "https://egit.irs.uni-stuttgart.de/rust/raw-slice" +repository = "https://egit.irs.uni-stuttgart.de/rust/raw-slice" +license = "Apache-2.0 OR MIT" +keywords = ["no-std", "slice", "embedded", "dma", "pointer"] +categories = ["no-std", "hardware-support", "embedded", "embedded::no-std", "data-structures"] + +[dependencies] +embedded-dma = "0.2" diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/LICENSE-APACHE @@ -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. diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 0000000..8311204 --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Robin A. Mueller + +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..e7ab5a0 --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +[![Crates.io](https://img.shields.io/crates/v/rawslice)](https://crates.io/crates/rawslice) +[![docs.rs](https://img.shields.io/docsrs/rawslice)](https://docs.rs/rawslice) +[![ci](https://github.com/us-irs/raw-slice/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/us-irs/raw-slice/actions/workflows/ci.yml) + +`rawslice` - Generic raw slice types +====== + +This crate provides generic raw slice type, which allows erasing the lifetime of a borrowed slice. +The documentation provides more details on the motivation of this crate and how it can be used. diff --git a/docs.sh b/docs.sh new file mode 100755 index 0000000..37563d2 --- /dev/null +++ b/docs.sh @@ -0,0 +1,3 @@ +#!/bin/sh +export RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" +cargo +nightly doc --all-features --open diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..f1a3c27 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,375 @@ +//! # Raw Slice Types +//! +//! This crate provides two generic raw slice type, [RawSlice] and [RawSliceMut], which allow +//! erasing the lifetime of a borrowed slice. +//! +//! ## Motivation +//! +//! In Rust, lifetimes are a powerful tool for ensuring memory safety at compile time. +//! However, there are cases where lifetimes become too restrictive, such as when working +//! with borrowed data across asynchronous or interrupt-driven contexts. +//! +//! This data structure is particularly useful in embedded systems, where data may be +//! passed to asynchronous peripherals such as serial TX drivers using interrupts or DMA. +//! The data may be static, but it could also reside on the stack. By using a shared [RawBufSlice], +//! you can pass borrowed data to a driver **without** needing to explicitly manage lifetimes. +//! +//! ## Safety Considerations +//! +//! - **No Lifetime Tracking:** Since [RawSlice] erases lifetimes, **the caller must ensure** +//! that the referenced data remains valid while the [RawSlice] is in use. +//! - **Concurrency Risks:** Accessing the same underlying data from multiple contexts +//! (e.g., an ISR and a task) requires proper synchronization. +//! - **Immutability:** [RawSlice] provides a **read-only view** of the data. If you need +//! mutability, [RawSliceMut] can be used. +//! +//! ## Usage Example +//! +//! ```rust +//! use rawslice::RawBufSlice; +//! +//! static DATA: &[u8] = &[1, 2, 3, 4]; +//! +//! let raw_buf = unsafe { RawBufSlice::new(DATA) }; +//! +//! // Later, in an ISR or different context +//! unsafe { +//! if let Some(slice) = raw_buf.get() { +//! // Process the data, e.g. send it via serial interface +//! // self.rx.write(slice); +//! } +//! } +//! ``` +//! +//! ## API Design +//! +//! While this crate provides methods to interact with the stored data, most of these operations +//! remain `unsafe` due to the compiler's inability to enforce lifetimes after erasure. Users should +//! carefully ensure the referenced data remains valid for the required duration. In addition +//! to the concept of a slice being empty, a raw slice can also be NULL. +//! +//! ## Embedded DMA Support +//! +//! - The [RawBufSlice] structure implements the [embedded_dma::ReadBuffer] trait +//! - The [RawBufSliceMut] structure implements the [embedded_dma::WriteBuffer] trait +#![no_std] + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct RawSlice { + data: *const T, + len: usize, +} + +/// Safety: This type MUST be used with mutex to ensure concurrent access is valid. +unsafe impl Send for RawSlice {} + +impl RawSlice { + /// Creates a new `RawSlice` from a slice reference. + /// + /// # Safety + /// + /// - The caller **must** ensure that the slice outlives this `RawSlice`. + /// - The original slice **must not** be mutated while this `RawSlice` is used. + #[allow(dead_code)] + pub const unsafe fn new(data: &[T]) -> Self { + Self { + data: data.as_ptr(), + len: data.len(), + } + } + + /// Creates an empty `RawSlice`, equivalent to a null pointer with zero length. + pub const fn new_nulled() -> Self { + Self { + data: core::ptr::null(), + len: 0, + } + } + + /// Updates the raw pointer and length to point to a new slice. + /// + /// # Safety + /// + /// - The caller **must** ensure that the slice outlives this `RawSlice`. + /// - The original slice **must not** be mutated while this `RawSlice` is used. + pub const unsafe fn set(&mut self, data: &[T]) { + self.data = data.as_ptr(); + self.len = data.len(); + } + + /// Set the internal data pointer to NULL and also clears the data length. + pub const fn set_null(&mut self) { + self.data = core::ptr::null(); + self.len = 0; + } + + /// Check whether the internal data pointer is NULL. + pub const fn is_null(&self) -> bool { + self.data.is_null() + } + + /// Converts the raw pointer into a slice. + /// + /// Returns [None] if the pointer is null. + /// + /// # Safety + /// + /// - The caller **must** ensure that the underlying memory is still valid. + /// - Using this function after the original slice is dropped results in UB. + pub const unsafe fn get(&self) -> Option<&[T]> { + if self.data.is_null() { + return None; + } + Some(unsafe { core::slice::from_raw_parts(self.data, self.len) }) + } + + /// Returns [None] if the pointer is null and whether [Self::len] is 0 otherwise. + pub const fn is_empty(&self) -> Option { + if self.is_null() { + return None; + } + Some(self.len == 0) + } + + /// Returns [None] if the pointer is null and the length of the raw slice otherwise. + pub const fn len(&self) -> Option { + if self.is_null() { + return None; + } + Some(self.len) + } +} + +impl Default for RawSlice { + fn default() -> Self { + Self::new_nulled() + } +} + +pub type RawBufSlice = RawU8Slice; +pub type RawU8Slice = RawSlice; +pub type RawU16Slice = RawSlice; +pub type RawU32Slice = RawSlice; + +macro_rules! impl_dma_read_buf { + ($slice_type:ident, $ty:ident) => { + /// This allows using [Self] in DMA APIs which expect a [embedded_dma::ReadBuffer]. + /// + /// However, the user still must ensure that any alignment rules for DMA buffers required by + /// the hardware are met and than any MPU/MMU configuration necessary is also performed for this + /// to work properly. + unsafe impl embedded_dma::ReadBuffer for $slice_type { + type Word = $ty; + + unsafe fn read_buffer(&self) -> (*const Self::Word, usize) { + (self.data, self.len) + } + } + }; +} + +impl_dma_read_buf!(RawBufSlice, u8); +impl_dma_read_buf!(RawU16Slice, u16); +impl_dma_read_buf!(RawU32Slice, u32); + +#[derive(Debug, Copy, Clone)] +pub struct RawSliceMut { + data: *mut T, + len: usize, +} + +/// Safety: This type MUST be used with mutex to ensure concurrent access is valid. +unsafe impl Send for RawSliceMut {} + +impl RawSliceMut { + /// Creates a new `RawSlice` from a slice reference. + /// + /// # Safety + /// + /// - The caller **must** ensure that the slice outlives this `RawSlice`. + /// - The original slice **must not** be mutated while this `RawSlice` is used. + #[allow(dead_code)] + pub const unsafe fn new(data: &mut [T]) -> Self { + Self { + data: data.as_mut_ptr(), + len: data.len(), + } + } + + /// Creates an empty `RawSlice`, equivalent to a null pointer with zero length. + pub const fn new_nulled() -> Self { + Self { + data: core::ptr::null_mut(), + len: 0, + } + } + + /// Updates the raw pointer and length to point to a new slice. + /// + /// # Safety + /// + /// - The caller **must** ensure that the slice outlives this `RawSlice`. + /// - The original slice **must not** be mutated while this `RawSlice` is used. + pub const unsafe fn set(&mut self, data: &mut [T]) { + self.data = data.as_mut_ptr(); + self.len = data.len(); + } + + /// Converts the raw pointer into a slice. + /// + /// Returns [None] if the pointer is null. + /// + /// # Safety + /// + /// - The caller **must** ensure that the underlying memory is still valid. + /// - Using this function after the original slice is dropped results in UB. + pub const unsafe fn get<'slice>(&self) -> Option<&'slice [T]> { + if self.data.is_null() { + return None; + } + Some(unsafe { core::slice::from_raw_parts(self.data, self.len) }) + } + + /// Converts the raw pointer into a mutable slice. + /// + /// Returns [None] if the pointer is null. + /// + /// # Safety + /// + /// - The caller **must** ensure that the underlying memory is still valid. + /// - Using this function after the original slice is dropped results in UB. + pub const unsafe fn get_mut<'slice>(&mut self) -> Option<&'slice mut [T]> { + if self.data.is_null() { + return None; + } + Some(unsafe { core::slice::from_raw_parts_mut(self.data, self.len) }) + } + + pub const fn set_null(&mut self) { + self.data = core::ptr::null_mut(); + self.len = 0; + } + + pub const fn is_null(&self) -> bool { + self.data.is_null() + } + + /// Returns [None] if the pointer is null and whether [Self::len] is 0 otherwise. + pub const fn is_empty(&self) -> Option { + if self.is_null() { + return None; + } + Some(self.len == 0) + } + + /// Returns [None] if the pointer is null and the length of the raw slice otherwise. + pub const fn len(&self) -> Option { + if self.is_null() { + return None; + } + Some(self.len) + } +} + +impl Default for RawSliceMut { + fn default() -> Self { + Self::new_nulled() + } +} + +pub type RawBufSliceMut = RawU8SliceMut; +pub type RawU8SliceMut = RawSliceMut; +pub type RawU16SliceMut = RawSliceMut; +pub type RawU32SliceMut = RawSliceMut; + +macro_rules! impl_dma_write_buf { + ($slice_type:ident, $ty:ident) => { + /// This allows using [Self] in DMA APIs which expect a [embedded_dma::WriteBuffer]. + /// + /// However, the user still must ensure that any alignment rules for DMA buffers required by + /// the hardware are met and than any MPU/MMU configuration necessary was also performed. + unsafe impl embedded_dma::WriteBuffer for $slice_type { + type Word = $ty; + + unsafe fn write_buffer(&mut self) -> (*mut Self::Word, usize) { + (self.data, self.len) + } + } + }; +} + +impl_dma_write_buf!(RawBufSliceMut, u8); +impl_dma_write_buf!(RawU16SliceMut, u16); +impl_dma_write_buf!(RawU32SliceMut, u32); + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + pub fn test_basic() { + let slice = [1, 2, 3, 4]; + let mut slice_raw = unsafe { RawBufSlice::new(&slice) }; + assert_eq!(slice_raw.len().unwrap(), 4); + assert!(!slice_raw.is_null()); + assert!(!slice_raw.is_empty().unwrap()); + assert_eq!(slice_raw.len().unwrap(), 4); + let slice_read_back = unsafe { slice_raw.get().unwrap() }; + assert_eq!(slice_read_back, slice); + slice_raw.set_null(); + generic_empty_test(&slice_raw); + } + + #[test] + pub fn test_empty() { + let empty = RawBufSlice::new_nulled(); + generic_empty_test(&empty); + } + + #[test] + pub fn test_empty_mut() { + let mut empty = RawBufSliceMut::new_nulled(); + generic_empty_test_mut(&mut empty); + } + + #[test] + pub fn test_clonable() { + let slice = [1, 2, 3, 4]; + let slice_raw = unsafe { RawBufSlice::new(&slice) }; + let slice_copied = slice_raw; + assert_eq!(slice_copied, slice_raw); + } + + #[test] + pub fn test_basic_mut() { + let mut slice = [1, 2, 3, 4]; + let mut slice_raw = unsafe { RawBufSliceMut::new(&mut slice) }; + assert_eq!(slice_raw.len().unwrap(), 4); + assert!(!slice_raw.is_null()); + assert!(!slice_raw.is_empty().unwrap()); + assert_eq!(slice_raw.len().unwrap(), 4); + let slice_read_back = unsafe { slice_raw.get().unwrap() }; + assert_eq!(slice_read_back, slice); + let mut_slice_read_back = unsafe { slice_raw.get_mut().unwrap() }; + assert_eq!(slice_read_back, mut_slice_read_back); + mut_slice_read_back[0] = 5; + assert_eq!(slice[0], 5); + slice_raw.set_null(); + generic_empty_test_mut(&mut slice_raw); + } + + fn generic_empty_test(slice: &RawBufSlice) { + assert!(slice.is_null()); + assert!(slice.is_empty().is_none()); + assert!(slice.len().is_none()); + assert!(unsafe { slice.get() }.is_none()); + } + + fn generic_empty_test_mut(slice: &mut RawBufSliceMut) { + assert!(slice.is_null()); + assert!(slice.is_empty().is_none()); + assert!(slice.len().is_none()); + assert!(unsafe { slice.get() }.is_none()); + assert!(unsafe { slice.get_mut() }.is_none()); + } +}