├── .github └── workflows │ ├── coverage.yml │ └── main.yml ├── .gitignore ├── .gitlab-ci.yml ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── README.tpl ├── ensure_no_std ├── .gitignore ├── Cargo.toml ├── README.md └── src │ └── main.rs ├── examples ├── pcap2dlt.rs ├── print_dlt_file.rs ├── print_pcap_dlt_msgs.rs └── print_verbose_manual.rs ├── proptest-regressions ├── control │ └── mod.txt ├── dlt_packet_slice.txt ├── lib.txt └── verbose │ ├── field_slicer.txt │ └── values │ ├── array_bool.txt │ ├── array_f16.txt │ ├── array_i128.txt │ ├── array_i64.txt │ ├── array_u128.txt │ ├── array_u16.txt │ ├── array_u32.txt │ ├── array_u64.txt │ ├── array_u8.txt │ ├── bool_value.txt │ ├── f128_value.txt │ ├── f16_value.txt │ ├── f32_value.txt │ ├── f64_value.txt │ ├── i16_value.txt │ ├── i32_value.txt │ ├── i8_value.txt │ ├── raw_f16.txt │ ├── raw_value.txt │ ├── string_value.txt │ ├── struct_value.txt │ └── u8_value.txt ├── scripts ├── README.md └── coverage.bash └── src ├── control └── mod.rs ├── dlt_extended_header.rs ├── dlt_header.rs ├── dlt_message_info.rs ├── dlt_packet_slice.rs ├── dlt_slice_iterator.rs ├── dlt_typed_payload.rs ├── error ├── dlt_message_length_too_small_error.rs ├── layer.rs ├── mod.rs ├── packet_slice_error.rs ├── range_error.rs ├── read_error.rs ├── storage_header_start_pattern_error.rs ├── typed_payload_error.rs ├── unexpected_end_of_slice_error.rs ├── unsupported_dlt_version_error.rs └── verbose_decode_error.rs ├── lib.rs ├── nv_payload.rs ├── proptest_generators └── mod.rs ├── storage ├── dlt_storage_reader.rs ├── dlt_storage_writer.rs ├── mod.rs ├── storage_header.rs └── storage_slice.rs └── verbose ├── field_slicer.rs ├── mod.rs ├── pre_checked_verbose_iter.rs ├── values ├── array_bool.rs ├── array_f128.rs ├── array_f16.rs ├── array_f32.rs ├── array_f64.rs ├── array_i128.rs ├── array_i16.rs ├── array_i32.rs ├── array_i64.rs ├── array_i8.rs ├── array_iteratable.rs ├── array_u128.rs ├── array_u16.rs ├── array_u32.rs ├── array_u64.rs ├── array_u8.rs ├── bool_value.rs ├── f128_value.rs ├── f16_value.rs ├── f32_value.rs ├── f64_value.rs ├── i128_value.rs ├── i16_value.rs ├── i32_value.rs ├── i64_value.rs ├── i8_value.rs ├── mod.rs ├── raw_f128.rs ├── raw_f16.rs ├── raw_value.rs ├── string_value.rs ├── struct_value.rs ├── trace_info_value.rs ├── u128_value.rs ├── u16_value.rs ├── u32_value.rs ├── u64_value.rs └── u8_value.rs ├── verbose_iter.rs └── verbose_value.rs /.github/workflows/coverage.yml: -------------------------------------------------------------------------------- 1 | name: codecoverage 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - main 8 | 9 | jobs: 10 | codecoverage_test: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | - uses: actions-rs/toolchain@v1 16 | with: 17 | toolchain: stable 18 | override: true 19 | components: llvm-tools-preview 20 | - uses: actions-rs/cargo@v1 21 | with: 22 | command: install 23 | args: cargo-binutils rustfilt 24 | - name: Install jq 25 | run: sudo apt install jq 26 | - name: Determine coverage 27 | run: scripts/coverage.bash 28 | shell: bash 29 | - name: Codecov 30 | uses: codecov/codecov-action@v4 31 | with: 32 | files: target/coverage/export.lcov.txt 33 | fail_ci_if_error: true 34 | token: ${{ secrets.CODECOV_TOKEN }} 35 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: main 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - main 8 | 9 | jobs: 10 | test: 11 | name: cargo build and test 12 | runs-on: ${{ matrix.os }} 13 | strategy: 14 | matrix: 15 | build: 16 | - linux-stable 17 | - linux-32bit-stable 18 | - linux-beta 19 | - linux-nightly 20 | - macos-stable 21 | - win-msvc-stable 22 | - win-gnu-stable 23 | include: 24 | - build: linux-stable 25 | os: ubuntu-latest 26 | rust: stable 27 | - build: linux-32bit-stable 28 | os: ubuntu-latest 29 | rust: stable 30 | target: i686-unknown-linux-gnu 31 | - build: linux-beta 32 | os: ubuntu-latest 33 | rust: beta 34 | - build: linux-nightly 35 | os: ubuntu-latest 36 | rust: nightly 37 | - build: macos-stable 38 | os: macos-latest 39 | rust: stable 40 | - build: win-msvc-stable 41 | os: windows-latest 42 | rust: stable 43 | - build: win-gnu-stable 44 | os: windows-latest 45 | rust: stable-x86_64-gnu 46 | steps: 47 | 48 | - uses: actions/checkout@v2 49 | 50 | - uses: actions-rs/toolchain@v1 51 | with: 52 | profile: minimal 53 | toolchain: ${{ matrix.rust }} 54 | override: true 55 | 56 | - name: cargo build 57 | if: matrix.target == '' 58 | uses: actions-rs/cargo@v1 59 | with: 60 | command: build 61 | 62 | - name: cross build 63 | if: matrix.target != '' 64 | uses: actions-rs/cargo@v1 65 | with: 66 | use-cross: true 67 | command: build 68 | args: --target ${{ matrix.target }} 69 | 70 | - name: cargo test 71 | if: matrix.target == '' 72 | uses: actions-rs/cargo@v1 73 | with: 74 | command: test 75 | 76 | - name: cross test 77 | if: matrix.target != '' 78 | uses: actions-rs/cargo@v1 79 | with: 80 | use-cross: true 81 | command: test 82 | args: --target ${{ matrix.target }} 83 | 84 | - name: cargo build --no-default-features 85 | if: matrix.target == '' 86 | uses: actions-rs/cargo@v1 87 | with: 88 | command: build 89 | args: --no-default-features 90 | 91 | - name: cross build --no-default-features 92 | if: matrix.target != '' 93 | uses: actions-rs/cargo@v1 94 | with: 95 | use-cross: true 96 | command: build 97 | args: --target ${{ matrix.target }} --no-default-features 98 | 99 | - name: cargo test --no-default-features 100 | if: matrix.target == '' 101 | uses: actions-rs/cargo@v1 102 | with: 103 | command: test 104 | args: --no-default-features 105 | 106 | - name: cross test --no-default-features 107 | if: matrix.target != '' 108 | uses: actions-rs/cargo@v1 109 | with: 110 | use-cross: true 111 | command: test 112 | args: --target ${{ matrix.target }} --no-default-features 113 | 114 | - name: cargo build --features serde 115 | if: matrix.target == '' 116 | uses: actions-rs/cargo@v1 117 | with: 118 | command: build 119 | args: --features serde 120 | 121 | - name: cross build --features serde 122 | if: matrix.target != '' 123 | uses: actions-rs/cargo@v1 124 | with: 125 | use-cross: true 126 | command: build 127 | args: --target ${{ matrix.target }} --features serde 128 | 129 | - name: cargo test --features serde 130 | if: matrix.target == '' 131 | uses: actions-rs/cargo@v1 132 | with: 133 | command: test 134 | args: --features serde 135 | 136 | - name: cross test --features serde 137 | if: matrix.target != '' 138 | uses: actions-rs/cargo@v1 139 | with: 140 | use-cross: true 141 | command: test 142 | args: --target ${{ matrix.target }} --features serde 143 | 144 | no_std_build: 145 | name: no_std build 146 | runs-on: ubuntu-latest 147 | defaults: 148 | run: 149 | working-directory: ./ensure_no_std 150 | steps: 151 | - uses: actions/checkout@v2 152 | - uses: actions-rs/toolchain@v1 153 | with: 154 | profile: minimal 155 | toolchain: stable 156 | target: x86_64-unknown-none 157 | override: true 158 | - run: cargo build --target x86_64-unknown-none 159 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | .vscode -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | # To contribute improvements to CI/CD templates, please follow the Development guide at: 2 | # https://docs.gitlab.com/ee/development/cicd/templates.html 3 | # This specific template is located at: 4 | # https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Rust.gitlab-ci.yml 5 | 6 | # Official language image. Look for the different tagged releases at: 7 | # https://hub.docker.com/r/library/rust/tags/ 8 | image: "rust:latest" 9 | 10 | # Optional: Pick zero or more services to be used on all builds. 11 | # Only needed when using a docker container to run your tests in. 12 | # Check out: http://docs.gitlab.com/ee/ci/docker/using_docker_images.html#what-is-a-service 13 | # services: 14 | # - mysql:latest 15 | # - redis:latest 16 | # - postgres:latest 17 | 18 | # Optional: Install a C compiler, cmake and git into the container. 19 | # You will often need this when you (or any of your dependencies) depends on C code. 20 | # before_script: 21 | # - apt-get update -yqq 22 | # - apt-get install -yqq --no-install-recommends build-essential 23 | 24 | # Use cargo to test the project 25 | test:cargo: 26 | script: 27 | - rustc --version && cargo --version # Print version info for debugging 28 | - cargo test --workspace --verbose 29 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dlt_parse" 3 | version = "0.10.0" 4 | authors = ["Julian Schmid "] 5 | edition = "2021" 6 | rust-version = "1.60" 7 | description = "A library for parsing the \"Diagnostic Log and Trace\" network protocol (currently without payload interpretation)." 8 | repository = "https://github.com/JulianSchmid/dlt-parse-rs" 9 | categories = ["network-programming", "parser-implementations", "no-std"] 10 | keywords = ["dlt", "autosar"] 11 | license = "MIT OR Apache-2.0" 12 | readme = "README.md" 13 | exclude = [ 14 | ".gitignore", 15 | ".travis.yml", 16 | ".github/*", 17 | ".gitlab-ci.yml", 18 | ".travis/*", 19 | "appveyor.yml" 20 | ] 21 | 22 | [features] 23 | default = ["std"] 24 | std = ["arrayvec/std"] 25 | serde = ["dep:serde", "arrayvec/serde"] 26 | 27 | [dependencies] 28 | arrayvec = { version = "0.7.4", default-features = false } 29 | serde = { version = "1.0", optional = true, features = ["derive"] } 30 | 31 | [dev-dependencies] 32 | assert_matches = "1.5.0" 33 | proptest = "1.2.0" 34 | serde_json = { version = "1.0" } 35 | 36 | # for examples 37 | etherparse = "0.13.0" 38 | structopt = "0.3.26" 39 | rpcap = "1.0.0" 40 | 41 | [[example]] 42 | name = "pcap2dlt" 43 | required-features = ["std"] 44 | 45 | [[example]] 46 | name = "print_dlt_file" 47 | required-features = ["std"] 48 | 49 | [[example]] 50 | name = "print_verbose_manual" 51 | required-features = ["std"] 52 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Julian Schmid 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Crates.io](https://img.shields.io/crates/v/dlt_parse.svg)](https://crates.io/crates/dlt_parse) 2 | [![docs.rs](https://docs.rs/dlt_parse/badge.svg)](https://docs.rs/dlt_parse) 3 | [![build status github](https://github.com/JulianSchmid/dlt-parse-rs/actions/workflows/main.yml/badge.svg?branch=main)](https://github.com/JulianSchmid/dlt-parse-rs/actions/workflows/main.yml) 4 | [![build status gitlab](https://gitlab.com/julian.schmid/dlt-parse-rs/badges/main/pipeline.svg)](https://gitlab.com/julian.schmid/dlt-parse-rs/-/commits/main) 5 | [![codecov](https://codecov.io/gh/JulianSchmid/dlt-parse-rs/branch/main/graph/badge.svg?token=D1LANr6nox)](https://codecov.io/gh/JulianSchmid/dlt-parse-rs) 6 | 7 | # dlt_parse 8 | 9 | A zero allocation rust library for basic parsing & writing DLT (Diagnostic Log and Trace) 10 | packets. Currently only the parsing and writing of the header is supported & parsing of verbose messages. 11 | 12 | ## Usage: 13 | 14 | By default `serde` is disabled and `std` is enabled if you add `dlt_parse` as dependency to your `Cargo.toml`: 15 | 16 | ```toml 17 | [dependencies] 18 | dlt_parse = "0.10.0" 19 | ``` 20 | 21 | If you additionally want `serde` support you will have to activate the `serde` feature: 22 | 23 | ```toml 24 | [dependencies] 25 | dlt_parse = { version = "0.10.0", features = ["serde"] } 26 | ``` 27 | 28 | If you want to use the crate in `no_std` mode you will have to disable the default features: 29 | 30 | ```toml 31 | [dependencies] 32 | dlt_parse = { version = "0.10.0", default-features = false } 33 | ``` 34 | 35 | ## What is dlt_parse? 36 | dlt_parse is a library that aims to provide serialisation & deserialisation funtions for DLT (Diagnostic Log and Trace) packets. 37 | It should make it possible to anlyse recordings of DLT packets as fast as possible, as well as writing servers 38 | that send DLT packets to the network. 39 | 40 | Some key points are: 41 | 42 | * It is completly written in Rust and thoroughly tested. 43 | * Special attention has been paid to not use allocations or syscalls. 44 | * It is possible to use the crate in an `no-std` environment. 45 | * The package is still in development and can & will still change. 46 | 47 | ## Example: Serializing & Slicing/Deserializing DLT Packets 48 | 49 | In this example a non verbose DLT packet is serialized and deserialized again. Specificly the serialized packet is 50 | converted into a DltPacketSlice. This has the advantage, that not all fields have to be deserialied to 51 | access the payload or specific fields in the header. Note that it is also possible to completely deserialize 52 | DLT headers with the DltHeader::read function. This can make sense, if most fields of the header are used anyways. 53 | 54 | ```rust 55 | use self::dlt_parse::{DltHeader, DltLogLevel, DltExtendedHeader, SliceIterator}; 56 | 57 | let header = { 58 | let mut header = DltHeader { 59 | is_big_endian: true, // payload & message id are encoded with big endian 60 | message_counter: 0, 61 | length: 0, 62 | ecu_id: None, 63 | session_id: None, 64 | timestamp: None, 65 | extended_header: Some(DltExtendedHeader::new_non_verbose_log( 66 | DltLogLevel::Debug, 67 | [b'a', b'p', b'p', b'i'],// application id 68 | [b'c', b't', b'x', b'i'],// context id 69 | )) 70 | }; 71 | header.length = header.header_len() + 4 + 4; // header + message id + payload 72 | 73 | header 74 | }; 75 | 76 | // buffer to store serialized header & payload 77 | let mut buffer = Vec::::with_capacity(usize::from(header.length)); 78 | buffer.extend_from_slice(&header.to_bytes()); 79 | 80 | // write payload (message id 1234 & non verbose payload) 81 | { 82 | // for write_all 83 | use std::io::Write; 84 | 85 | // write the message id & payload 86 | buffer.write_all(&1234u32.to_be_bytes()).unwrap(); // message id 87 | buffer.write_all(&[5,6,7,9]); // payload 88 | } 89 | 90 | // packets can contain multiple dlt messages, iterate through them 91 | for dlt_message in SliceIterator::new(&buffer) { 92 | match dlt_message { 93 | Ok(dlt_slice) => { 94 | // check what type of message was received 95 | match dlt_slice.typed_payload() { 96 | Ok(typed_payload) => { 97 | use dlt_parse::DltTypedPayload::*; 98 | match typed_payload { 99 | UnknownNv(p) => { 100 | println!( 101 | "non verbose message 0x{:x} (unknown) with {} bytes of payload.", 102 | p.msg_id, 103 | p.payload.len(), 104 | ); 105 | } 106 | LogNv(p) => { 107 | println!( 108 | "non verbose log message 0x{:x} with log level {:?} and {} bytes of payload.", 109 | p.msg_id, 110 | p.log_level, 111 | p.payload.len(), 112 | ); 113 | } 114 | LogV(p) => { 115 | println!( 116 | "verbose log message with log level {:?} and values:", 117 | p.log_level 118 | ); 119 | for value in p.iter { 120 | println!(" {:?}", value); 121 | } 122 | } 123 | TraceNv(p) => { 124 | println!( 125 | "non verbose trace message 0x{:x} of type {:?} and {} bytes of payload.", 126 | p.msg_id, 127 | p.trace_type, 128 | p.payload.len(), 129 | ); 130 | } 131 | TraceV(p) => { 132 | println!( 133 | "verbose trace message with of type {:?} and values:", 134 | p.trace_type 135 | ); 136 | for value in p.iter { 137 | println!(" {:?}", value); 138 | } 139 | } 140 | NetworkNv(p) => { 141 | println!( 142 | "non verbose network message 0x{:x} of type {:?} and {} bytes of payload.", 143 | p.msg_id, 144 | p.net_type, 145 | p.payload.len(), 146 | ); 147 | } 148 | NetworkV(p) => { 149 | println!( 150 | "verbose network message with of type {:?} and values:", 151 | p.net_type 152 | ); 153 | for value in p.iter { 154 | println!(" {:?}", value); 155 | } 156 | } 157 | ControlNv(p) => { 158 | println!("non verbose control message {:?} with service id: {} and {} bytes of payload.", p.msg_type, p.service_id, p.payload.len()); 159 | } 160 | ControlV(p) => { 161 | println!("verbose control message {:?} with values:", p.msg_type); 162 | for value in p.iter { 163 | println!(" {:?}", value); 164 | } 165 | } 166 | } 167 | } 168 | Err(err) => { 169 | println!("message with payload error received: {}", err); 170 | } 171 | } 172 | }, 173 | Err(err) => { 174 | //error parsing the dlt packet 175 | println!("ERROR: {:?}", err); 176 | } 177 | } 178 | } 179 | ``` 180 | 181 | An complete example which includes the parsing of the ethernet & udp headers can be found in [examples/print_messages_ids.rs](examples/print_messages_ids.rs) 182 | 183 | ## References 184 | * [Log and Trace Protocol Specification](https://www.autosar.org/fileadmin/standards/foundation/1-3/AUTOSAR_PRS_LogAndTraceProtocol.pdf) 185 | 186 | ## License 187 | Licensed under either of Apache License, Version 2.0 or MIT license at your option. The corresponding license texts can be found in the LICENSE-APACHE file and the LICENSE-MIT file. 188 | 189 | ### Contribution 190 | Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you shall be licensed as above, without any additional terms or conditions. 191 | -------------------------------------------------------------------------------- /README.tpl: -------------------------------------------------------------------------------- 1 | [![Crates.io](https://img.shields.io/crates/v/dlt_parse.svg)](https://crates.io/crates/dlt_parse) 2 | [![docs.rs](https://docs.rs/dlt_parse/badge.svg)](https://docs.rs/dlt_parse) 3 | [![build status github](https://github.com/JulianSchmid/dlt-parse-rs/actions/workflows/main.yml/badge.svg?branch=main)](https://github.com/JulianSchmid/dlt-parse-rs/actions/workflows/main.yml) 4 | [![build status gitlab](https://gitlab.com/julian.schmid/dlt-parse-rs/badges/main/pipeline.svg)](https://gitlab.com/julian.schmid/dlt-parse-rs/-/commits/main) 5 | [![codecov](https://codecov.io/gh/JulianSchmid/dlt-parse-rs/branch/main/graph/badge.svg?token=D1LANr6nox)](https://codecov.io/gh/JulianSchmid/dlt-parse-rs) 6 | 7 | # {{crate}} 8 | 9 | {{readme}} 10 | 11 | ## License 12 | Licensed under the BSD 3-Clause license. Please see the LICENSE file for more information. 13 | 14 | ### Contribution 15 | Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you shall be licensed as above, without any additional terms or conditions. -------------------------------------------------------------------------------- /ensure_no_std/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | .vscode -------------------------------------------------------------------------------- /ensure_no_std/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ensure_no_std" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | dlt_parse = { path = "..", default-features = false } 9 | 10 | [profile.dev] 11 | panic = "abort" 12 | 13 | [profile.release] 14 | panic = "abort" 15 | 16 | [workspace] -------------------------------------------------------------------------------- /ensure_no_std/README.md: -------------------------------------------------------------------------------- 1 | # ensure_no_std 2 | 3 | This a helper project to test if `etherparse` compiled in `no_std` 4 | mode does not contain any references to `std` or `alloc`. 5 | 6 | ## Prerequisite 7 | 8 | You have to have a toolchain installed for `no_std` testing: 9 | 10 | ``` 11 | rustup target add x86_64-unknown-none 12 | ``` 13 | 14 | ## How to test there are no `std` or `alloc` dependencies? 15 | 16 | ```sh 17 | cd /ensure_no_std 18 | cargo build --target x86_64-unknown-none 19 | ``` 20 | 21 | If the build passes you are good. -------------------------------------------------------------------------------- /ensure_no_std/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | use core::panic::PanicInfo; 5 | 6 | /// This function is called on panic. 7 | #[panic_handler] 8 | fn panic(_info: &PanicInfo) -> ! { 9 | loop {} 10 | } 11 | 12 | #[no_mangle] 13 | pub extern "C" fn _start() -> ! { 14 | loop {} 15 | } 16 | -------------------------------------------------------------------------------- /examples/pcap2dlt.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | fs::File, 3 | io::{BufReader, BufWriter}, 4 | path::PathBuf, 5 | time::UNIX_EPOCH, 6 | }; 7 | 8 | use dlt_parse::{ 9 | storage::{DltStorageWriter, StorageHeader}, 10 | SliceIterator, 11 | }; 12 | use etherparse::{SlicedPacket, TransportSlice::Udp}; 13 | use rpcap::read::PcapReader; 14 | use structopt::StructOpt; 15 | 16 | /// Expected command line arguments 17 | #[derive(StructOpt, Debug)] 18 | #[structopt(name = "pcap2dlt")] 19 | struct CommandLineArguments { 20 | /// Udp port on which dlt packets are send. 21 | #[structopt(short("u"), long("udp-dest-port"))] 22 | udp_dest_port: u16, 23 | 24 | /// Path to pcap file. 25 | #[structopt(short("o"), long("output-file"), parse(from_os_str))] 26 | output_file: PathBuf, 27 | 28 | /// Path to pcap file. 29 | #[structopt(parse(from_os_str))] 30 | pcap_file: PathBuf, 31 | } 32 | 33 | #[derive(Debug)] 34 | pub enum Error { 35 | IoError(std::io::Error), 36 | PcapError(rpcap::PcapError), 37 | } 38 | 39 | impl From for Error { 40 | fn from(err: std::io::Error) -> Error { 41 | Error::IoError(err) 42 | } 43 | } 44 | 45 | impl From for Error { 46 | fn from(err: rpcap::PcapError) -> Error { 47 | Error::PcapError(err) 48 | } 49 | } 50 | 51 | fn main() -> Result<(), Error> { 52 | let args = CommandLineArguments::from_args(); 53 | 54 | let dlt_file = File::create(args.output_file)?; 55 | let mut dlt_writer = DltStorageWriter::new(BufWriter::new(dlt_file)); 56 | 57 | let pcap_file = File::open(args.pcap_file)?; 58 | let (_, mut reader) = PcapReader::new(BufReader::new(pcap_file))?; 59 | 60 | while let Some(packet) = reader.next()? { 61 | // decode from ethernet to udp layer 62 | let sliced = match SlicedPacket::from_ethernet(packet.data) { 63 | Ok(value) => value, 64 | Err(err) => { 65 | eprintln!("Error parsing packet: {}", err); 66 | continue; 67 | } 68 | }; 69 | 70 | // verify the packet is an udp packet with the correct destination port 71 | if let Some(Udp(udp)) = sliced.transport { 72 | if udp.destination_port() != args.udp_dest_port { 73 | // skip packet if the port is not matching 74 | continue; 75 | } 76 | } else { 77 | // skip packet if not relevant 78 | continue; 79 | } 80 | 81 | // iterate over the dlt messages in the packet 82 | for dlt_packet in SliceIterator::new(sliced.payload) { 83 | let dlt_packet = match dlt_packet { 84 | Ok(value) => value, 85 | Err(err) => { 86 | eprintln!("Error parsing dlt: {}", err); 87 | break; 88 | } 89 | }; 90 | 91 | // determine ecu id 92 | let ecu_id = if let Some(ecu_id) = dlt_packet.header().ecu_id { 93 | ecu_id 94 | } else { 95 | // you might want to determine the ecu id via the ip here 96 | // if you have that option 97 | [0, 0, 0, 0] 98 | }; 99 | 100 | // determine utc time (unwrap is ok, as all pcap timestamps start at UNIX_EPOCH) 101 | let d = packet.time.duration_since(UNIX_EPOCH).unwrap(); 102 | 103 | // write the packet 104 | dlt_writer.write_slice( 105 | StorageHeader { 106 | timestamp_seconds: d.as_secs() as u32, 107 | timestamp_microseconds: d.subsec_micros(), 108 | ecu_id, 109 | }, 110 | dlt_packet, 111 | )?; 112 | } 113 | } 114 | 115 | Ok(()) 116 | } 117 | -------------------------------------------------------------------------------- /examples/print_dlt_file.rs: -------------------------------------------------------------------------------- 1 | use dlt_parse::{error::ReadError, storage::DltStorageReader}; 2 | use std::{fs::File, io::BufReader, path::PathBuf}; 3 | use structopt::StructOpt; 4 | 5 | /// Expected command line arguments 6 | #[derive(StructOpt, Debug)] 7 | #[structopt(name = "print_dlt_file")] 8 | struct CommandLineArguments { 9 | /// Path to pcap file. 10 | #[structopt(parse(from_os_str))] 11 | dlt_file: PathBuf, 12 | } 13 | 14 | fn main() -> Result<(), ReadError> { 15 | let args = CommandLineArguments::from_args(); 16 | 17 | let dlt_file = File::open(args.dlt_file)?; 18 | let mut reader = DltStorageReader::new(BufReader::new(dlt_file)); 19 | 20 | while let Some(msg) = reader.next_packet() { 21 | let msg = msg?; 22 | 23 | println!("{:?}", msg.storage_header); 24 | 25 | if let Some(extended_header) = msg.packet.extended_header() { 26 | use core::str::from_utf8; 27 | 28 | println!( 29 | "application_id: {:?}, context_id: {:?}", 30 | from_utf8(&extended_header.application_id), 31 | from_utf8(&extended_header.context_id) 32 | ); 33 | } 34 | 35 | // check what type of message was received 36 | match msg.packet.typed_payload() { 37 | Ok(typed_payload) => { 38 | use dlt_parse::DltTypedPayload::*; 39 | match typed_payload { 40 | UnknownNv(p) => { 41 | println!( 42 | "non verbose message 0x{:x} (unknown) with {} bytes of payload.", 43 | p.msg_id, 44 | p.payload.len(), 45 | ); 46 | } 47 | LogNv(p) => { 48 | println!( 49 | "non verbose log message 0x{:x} with log level {:?} and {} bytes of payload.", 50 | p.msg_id, 51 | p.log_level, 52 | p.payload.len(), 53 | ); 54 | } 55 | LogV(p) => { 56 | println!( 57 | "verbose log message with log level {:?} and values:", 58 | p.log_level 59 | ); 60 | for value in p.iter { 61 | println!(" {:?}", value); 62 | } 63 | } 64 | TraceNv(p) => { 65 | println!( 66 | "non verbose trace message 0x{:x} of type {:?} and {} bytes of payload.", 67 | p.msg_id, 68 | p.trace_type, 69 | p.payload.len(), 70 | ); 71 | } 72 | TraceV(p) => { 73 | println!( 74 | "verbose trace message with of type {:?} and values:", 75 | p.trace_type 76 | ); 77 | for value in p.iter { 78 | println!(" {:?}", value); 79 | } 80 | } 81 | NetworkNv(p) => { 82 | println!( 83 | "non verbose network message 0x{:x} of type {:?} and {} bytes of payload.", 84 | p.msg_id, 85 | p.net_type, 86 | p.payload.len(), 87 | ); 88 | } 89 | NetworkV(p) => { 90 | println!( 91 | "verbose network message with of type {:?} and values:", 92 | p.net_type 93 | ); 94 | for value in p.iter { 95 | println!(" {:?}", value); 96 | } 97 | } 98 | ControlNv(p) => { 99 | println!( 100 | "non verbose control message {:?} with service id: {} and {} bytes of payload.", 101 | p.msg_type, 102 | p.service_id, 103 | p.payload.len(), 104 | ); 105 | } 106 | ControlV(p) => { 107 | println!("verbose control message {:?} with values:", p.msg_type); 108 | for value in p.iter { 109 | println!(" {:?}", value); 110 | } 111 | } 112 | } 113 | } 114 | Err(err) => { 115 | println!("message with payload error received: {}", err); 116 | } 117 | } 118 | } 119 | 120 | Ok(()) 121 | } 122 | -------------------------------------------------------------------------------- /examples/print_pcap_dlt_msgs.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | use structopt::StructOpt; 3 | 4 | use self::etherparse::*; 5 | use etherparse; 6 | 7 | use rpcap::read::PcapReader; 8 | 9 | use std::fs::File; 10 | use std::io::BufReader; 11 | 12 | use dlt_parse::*; 13 | 14 | /// Expected command line arguments 15 | #[derive(StructOpt, Debug)] 16 | #[structopt(name = "print_messages_ids")] 17 | struct CommandLineArguments { 18 | /// Udp port on which dlt packets are send. 19 | #[structopt(short, long)] 20 | udp_port: u16, 21 | 22 | /// Path to pcap file. 23 | #[structopt(parse(from_os_str))] 24 | pcap_file: PathBuf, 25 | } 26 | 27 | fn main() -> Result<(), Error> { 28 | read(CommandLineArguments::from_args()) 29 | } 30 | 31 | #[derive(Debug)] 32 | pub enum Error { 33 | IoError(std::io::Error), 34 | PcapError(rpcap::PcapError), 35 | } 36 | 37 | impl From for Error { 38 | fn from(err: std::io::Error) -> Error { 39 | Error::IoError(err) 40 | } 41 | } 42 | 43 | impl From for Error { 44 | fn from(err: rpcap::PcapError) -> Error { 45 | Error::PcapError(err) 46 | } 47 | } 48 | 49 | fn read(arguments: CommandLineArguments) -> Result<(), Error> { 50 | let (_, mut reader) = PcapReader::new(BufReader::new(File::open(arguments.pcap_file)?))?; 51 | 52 | while let Some(packet) = reader.next()? { 53 | let sliced = SlicedPacket::from_ethernet(packet.data); 54 | 55 | //only use the packet if the parsing from ethernet layer to transport layer was error free 56 | if let Ok(sliced_packet) = sliced { 57 | use crate::TransportSlice::*; 58 | 59 | //check that it is an udp packet 60 | if let Some(Udp(udp_slice)) = sliced_packet.transport { 61 | //check the port 62 | if udp_slice.destination_port() != arguments.udp_port { 63 | // skip packet if the port is not matching 64 | continue; 65 | } 66 | } else { 67 | // skip packet if it is not an udp packet 68 | continue; 69 | } 70 | 71 | // parse the dlt message in the udp payload 72 | for dlt_slice in SliceIterator::new(sliced_packet.payload) { 73 | let dlt_slice = match dlt_slice { 74 | Ok(dlt_slice) => dlt_slice, 75 | Err(err) => { 76 | // error parsing the dlt packet 77 | println!("ERROR: {:?}", err); 78 | break; 79 | } 80 | }; 81 | 82 | // print application id & context id if available 83 | if let Some(extended_header) = dlt_slice.extended_header() { 84 | use core::str::from_utf8; 85 | 86 | println!( 87 | "application_id: {:?}, context_id: {:?}", 88 | from_utf8(&extended_header.application_id), 89 | from_utf8(&extended_header.context_id) 90 | ); 91 | } 92 | 93 | // check what type of message was received 94 | match dlt_slice.typed_payload() { 95 | Ok(typed_payload) => { 96 | use dlt_parse::DltTypedPayload::*; 97 | match typed_payload { 98 | UnknownNv(p) => { 99 | println!( 100 | "non verbose message 0x{:x} (unknown) with {} bytes of payload.", 101 | p.msg_id, 102 | p.payload.len(), 103 | ); 104 | } 105 | LogNv(p) => { 106 | println!( 107 | "non verbose log message 0x{:x} with log level {:?} and {} bytes of payload.", 108 | p.msg_id, 109 | p.log_level, 110 | p.payload.len(), 111 | ); 112 | } 113 | LogV(p) => { 114 | println!( 115 | "verbose log message with log level {:?} and values:", 116 | p.log_level 117 | ); 118 | for value in p.iter { 119 | println!(" {:?}", value); 120 | } 121 | } 122 | TraceNv(p) => { 123 | println!( 124 | "non verbose trace message 0x{:x} of type {:?} and {} bytes of payload.", 125 | p.msg_id, 126 | p.trace_type, 127 | p.payload.len(), 128 | ); 129 | } 130 | TraceV(p) => { 131 | println!( 132 | "verbose trace message with of type {:?} and values:", 133 | p.trace_type 134 | ); 135 | for value in p.iter { 136 | println!(" {:?}", value); 137 | } 138 | } 139 | NetworkNv(p) => { 140 | println!( 141 | "non verbose network message 0x{:x} of type {:?} and {} bytes of payload.", 142 | p.msg_id, 143 | p.net_type, 144 | p.payload.len(), 145 | ); 146 | } 147 | NetworkV(p) => { 148 | println!( 149 | "verbose network message with of type {:?} and values:", 150 | p.net_type 151 | ); 152 | for value in p.iter { 153 | println!(" {:?}", value); 154 | } 155 | } 156 | ControlNv(p) => { 157 | println!("non verbose control message {:?} with service id: {} and {} bytes of payload.", p.msg_type, p.service_id, p.payload.len()); 158 | } 159 | ControlV(p) => { 160 | println!("verbose control message {:?} with values:", p.msg_type); 161 | for value in p.iter { 162 | println!(" {:?}", value); 163 | } 164 | } 165 | } 166 | } 167 | Err(err) => { 168 | println!("message with payload error received: {}", err); 169 | } 170 | } 171 | } 172 | } 173 | } 174 | Ok(()) 175 | } 176 | -------------------------------------------------------------------------------- /examples/print_verbose_manual.rs: -------------------------------------------------------------------------------- 1 | use std::{fs::File, io::BufReader, path::PathBuf}; 2 | 3 | use dlt_parse::{ 4 | error::{ReadError, VerboseDecodeError}, 5 | storage::DltStorageReader, 6 | verbose::VerboseIter, 7 | }; 8 | use structopt::StructOpt; 9 | 10 | /// Expected command line arguments 11 | #[derive(StructOpt, Debug)] 12 | #[structopt(name = "print_dlt_file")] 13 | struct CommandLineArguments { 14 | /// Path to pcap file. 15 | #[structopt(parse(from_os_str))] 16 | dlt_file: PathBuf, 17 | } 18 | 19 | fn main() -> Result<(), ReadError> { 20 | let args = CommandLineArguments::from_args(); 21 | 22 | let dlt_file = File::open(args.dlt_file)?; 23 | let mut reader = DltStorageReader::new(BufReader::new(dlt_file)); 24 | 25 | while let Some(msg) = reader.next_packet() { 26 | let msg = msg?; 27 | 28 | println!("{:?}", msg.storage_header); 29 | println!("{:?}", msg.packet.message_type()); 30 | if let Some(extended_header) = msg.packet.extended_header() { 31 | use core::str::from_utf8; 32 | 33 | println!( 34 | "application_id: {:?}, context_id: {:?}", 35 | from_utf8(&extended_header.application_id), 36 | from_utf8(&extended_header.context_id) 37 | ); 38 | } 39 | 40 | if let Some(verb_iter) = msg.packet.verbose_value_iter() { 41 | let field_print_result = print_fields(verb_iter, 1); 42 | if let Err(err) = field_print_result { 43 | println!(" ERROR decoding value: {}", err); 44 | } 45 | } 46 | } 47 | 48 | Ok(()) 49 | } 50 | 51 | fn print_fields(iter: VerboseIter, indent: usize) -> Result<(), VerboseDecodeError> { 52 | let print_indent = || { 53 | for _ in 0..indent { 54 | print!(" "); 55 | } 56 | }; 57 | 58 | for value in iter { 59 | let value = value?; 60 | // print name 61 | if let Some(name) = value.name() { 62 | print_indent(); 63 | println!("name = '{}'", name); 64 | } 65 | if let Some(unit) = value.unit() { 66 | print_indent(); 67 | println!("unit = '{}'", unit); 68 | } 69 | 70 | // print value 71 | use dlt_parse::verbose::VerboseValue::*; 72 | 73 | print_indent(); 74 | 75 | match value { 76 | Bool(v) => println!("value = {}", v.value), 77 | Str(v) => println!("value = {}", v.value), 78 | TraceInfo(v) => println!("value = {}", v.value), 79 | I8(v) => println!("value = {}", v.value), 80 | I16(v) => println!("value = {}", v.value), 81 | I32(v) => println!("value = {}", v.value), 82 | I64(v) => println!("value = {}", v.value), 83 | I128(v) => println!("value = {}", v.value), 84 | U8(v) => println!("value = {}", v.value), 85 | U16(v) => println!("value = {}", v.value), 86 | U32(v) => println!("value = {}", v.value), 87 | U64(v) => println!("value = {}", v.value), 88 | U128(v) => println!("value = {}", v.value), 89 | F16(v) => println!("value = {}", v.value.to_f32()), 90 | F32(v) => println!("value = {}", v.value), 91 | F64(v) => println!("value = {}", v.value), 92 | F128(v) => println!("value = F128Bits({})", v.value.to_bits()), 93 | ArrBool(v) => print_arr(v.iter()), 94 | ArrI8(v) => print_arr(v.iter()), 95 | ArrI16(v) => print_arr(v.iter()), 96 | ArrI32(v) => print_arr(v.iter()), 97 | ArrI64(v) => print_arr(v.iter()), 98 | ArrI128(v) => print_arr(v.iter()), 99 | ArrU8(v) => print_arr(v.iter()), 100 | ArrU16(v) => print_arr(v.iter()), 101 | ArrU32(v) => print_arr(v.iter()), 102 | ArrU64(v) => print_arr(v.iter()), 103 | ArrU128(v) => print_arr(v.iter()), 104 | ArrF16(v) => print_arr(v.iter().map(|v| v.to_f32())), 105 | ArrF32(v) => print_arr(v.iter()), 106 | ArrF64(v) => print_arr(v.iter()), 107 | ArrF128(v) => print_arr(v.iter().map(|v| format!("RawF128(bits={})", v.to_bits()))), 108 | Struct(v) => print_fields(v.entries(), indent + 1)?, 109 | Raw(v) => { 110 | println!("raw = {:?}", v.data); 111 | } 112 | } 113 | } 114 | 115 | Ok(()) 116 | } 117 | 118 | fn print_arr(iter: impl Iterator) { 119 | print!("value = ["); 120 | for value in iter { 121 | print!("{}, ", value); 122 | } 123 | println!("]"); 124 | } 125 | -------------------------------------------------------------------------------- /proptest-regressions/control/mod.txt: -------------------------------------------------------------------------------- 1 | # Seeds for failure cases proptest has generated in the past. It is 2 | # automatically read and these particular cases re-run before any 3 | # novel cases are generated. 4 | # 5 | # It is recommended to check this file in to source control so that 6 | # everyone who runs the test benefits from these saved cases. 7 | cc c4d8b7c18867c805af9bb2f7af5f64f1a0fbacb15b5a2e7c2f460db5d0508bc7 # shrinks to unknown_id = 36, sw_injections_id = 4095 8 | -------------------------------------------------------------------------------- /proptest-regressions/dlt_packet_slice.txt: -------------------------------------------------------------------------------- 1 | # Seeds for failure cases proptest has generated in the past. It is 2 | # automatically read and these particular cases re-run before any 3 | # novel cases are generated. 4 | # 5 | # It is recommended to check this file in to source control so that 6 | # everyone who runs the test benefits from these saved cases. 7 | cc 3b51f0d9e6fab88939ee5a7ae049eb659354ebf994ab602913e3d84ed9ef1c21 # shrinks to is_big_endian = false, log_level = Fatal 8 | -------------------------------------------------------------------------------- /proptest-regressions/lib.txt: -------------------------------------------------------------------------------- 1 | # Seeds for failure cases proptest has generated in the past. It is 2 | # automatically read and these particular cases re-run before any 3 | # novel cases are generated. 4 | # 5 | # It is recommended to check this file in to source control so that 6 | # everyone who runs the test benefits from these saved cases. 7 | xs 4236928652 48947830 2096379890 2123546847 # shrinks to ref dlt_header = DltHeader { big_endian: false, version: 0, message_counter: 0, length: 0, ecu_id: None, session_id: None, timestamp: None, extended_header: None }, version = 7 8 | xs 1285569421 471825135 3568656765 1299307238 # shrinks to ref packet = (DltHeader { big_endian: false, version: 0, message_counter: 0, length: 4, ecu_id: None, session_id: None, timestamp: None, extended_header: None }, []) 9 | xs 4029682354 457593336 1619303629 3536177513 # shrinks to verbose = false, message_type0 = NetworkTrace(UserDefined(7)), message_type1 = Log(Fatal) 10 | xs 1858661651 1544208668 3005463608 1639642540 # shrinks to ref packet = (DltHeader { is_big_endian: false, version: 0, message_counter: 0, length: 277, ecu_id: None, session_id: None, timestamp: None, extended_header: Some(DltExtendedHeader { message_info: 4, number_of_arguments: 0, application_id: 0, context_id: 0 }) }, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) 11 | cc eea2b6d85a6453c2d7eca2610f6baa3b68bceef6f3c8b3d3ea5c48c628f0ca74 # shrinks to message_type = Log(Fatal), application_id = 0, context_id = 0, invalid_user_defined = 1 12 | -------------------------------------------------------------------------------- /proptest-regressions/verbose/field_slicer.txt: -------------------------------------------------------------------------------- 1 | # Seeds for failure cases proptest has generated in the past. It is 2 | # automatically read and these particular cases re-run before any 3 | # novel cases are generated. 4 | # 5 | # It is recommended to check this file in to source control so that 6 | # everyone who runs the test benefits from these saved cases. 7 | cc efaefdafa2a96caa83f62b4c690e1870441052c03518828488f5382f3b2831bf # shrinks to value = 0, slice_len = 1, offset = 0 8 | cc 27840e1fc55256e0b8c111e6fa6e7e50d109fd9b4776c3b65263e90a45664420 # shrinks to value = [0, 0], slice_len = 2, offset = 0, bad_len = 1 9 | cc 117b98858487300cf8a029aa942013060f478d06c6a5ab3678ead2f970120020 # shrinks to ref value = "", offset = 0, bad_len = 0, rest = [] 10 | cc 9190aa3debc9fdec866929782d9b3cbdff2daa32e20b613d091f1829c756ad08 # shrinks to ref name = "", ref unit = "", offset = 0, bad_len = 0, rest = [] 11 | -------------------------------------------------------------------------------- /proptest-regressions/verbose/values/array_bool.txt: -------------------------------------------------------------------------------- 1 | # Seeds for failure cases proptest has generated in the past. It is 2 | # automatically read and these particular cases re-run before any 3 | # novel cases are generated. 4 | # 5 | # It is recommended to check this file in to source control so that 6 | # everyone who runs the test benefits from these saved cases. 7 | cc e3c39c09dfece076d5404307f63da2356a7f0ab44166ea4c68b6084acb32849b # shrinks to value = 0, ref name = "", ref unit = "", dim_count = 0 8 | cc 704b7e719b4e07873e97ef2bd790ac54bf1b6912c2989e7c37a5f758244a57b7 # shrinks to value = 0, ref name = "", ref unit = "", dim_count = 1 9 | -------------------------------------------------------------------------------- /proptest-regressions/verbose/values/array_f16.txt: -------------------------------------------------------------------------------- 1 | # Seeds for failure cases proptest has generated in the past. It is 2 | # automatically read and these particular cases re-run before any 3 | # novel cases are generated. 4 | # 5 | # It is recommended to check this file in to source control so that 6 | # everyone who runs the test benefits from these saved cases. 7 | cc 72ef4aa9d25029f647eceb18b7be552619c4cf370f692287ebc10092352ed47a # shrinks to value0 = 1, value1 = 0 8 | -------------------------------------------------------------------------------- /proptest-regressions/verbose/values/array_i128.txt: -------------------------------------------------------------------------------- 1 | # Seeds for failure cases proptest has generated in the past. It is 2 | # automatically read and these particular cases re-run before any 3 | # novel cases are generated. 4 | # 5 | # It is recommended to check this file in to source control so that 6 | # everyone who runs the test benefits from these saved cases. 7 | cc 8913168ac4eea5625659aa5d5274ba3cf2e3536abe580cfd3019ec776d4f6521 # shrinks to dim_count = 1 8 | -------------------------------------------------------------------------------- /proptest-regressions/verbose/values/array_i64.txt: -------------------------------------------------------------------------------- 1 | # Seeds for failure cases proptest has generated in the past. It is 2 | # automatically read and these particular cases re-run before any 3 | # novel cases are generated. 4 | # 5 | # It is recommended to check this file in to source control so that 6 | # everyone who runs the test benefits from these saved cases. 7 | cc 9f98c0bc6536a64097780d051732e4de7c2a6818337e88b4aad28983ade44cf2 # shrinks to dim_count = 1 8 | -------------------------------------------------------------------------------- /proptest-regressions/verbose/values/array_u128.txt: -------------------------------------------------------------------------------- 1 | # Seeds for failure cases proptest has generated in the past. It is 2 | # automatically read and these particular cases re-run before any 3 | # novel cases are generated. 4 | # 5 | # It is recommended to check this file in to source control so that 6 | # everyone who runs the test benefits from these saved cases. 7 | cc 9b91b31879c1dab9c6c7aa355fe4431e4a4ac64af36ff4ec44027814ce8bcec9 # shrinks to ref name = "", ref unit = "", quantization = 0.0, offset = 0, dim_count = 0 8 | -------------------------------------------------------------------------------- /proptest-regressions/verbose/values/array_u16.txt: -------------------------------------------------------------------------------- 1 | # Seeds for failure cases proptest has generated in the past. It is 2 | # automatically read and these particular cases re-run before any 3 | # novel cases are generated. 4 | # 5 | # It is recommended to check this file in to source control so that 6 | # everyone who runs the test benefits from these saved cases. 7 | cc 086017d2f55eca7f9bc8c70da5ee6fb974e0cd111eef6a600d0dae51d34dca0e # shrinks to ref name = "", ref unit = "", quantization = 0.0, offset = 0, dim_count = 0 8 | -------------------------------------------------------------------------------- /proptest-regressions/verbose/values/array_u32.txt: -------------------------------------------------------------------------------- 1 | # Seeds for failure cases proptest has generated in the past. It is 2 | # automatically read and these particular cases re-run before any 3 | # novel cases are generated. 4 | # 5 | # It is recommended to check this file in to source control so that 6 | # everyone who runs the test benefits from these saved cases. 7 | cc 4bbcec1e96dc9ef5a828927177ad6ceaadf12860ea2a02afb0513c6b29b5fef9 # shrinks to ref name = "", ref unit = "", quantization = 0.0, offset = 0, dim_count = 0 8 | cc 1d7a4ca97e0605e8ed959442c49aeb07dcaaf246d675c2f960721912df754421 # shrinks to ref name = "", ref unit = "", quantization = 0.0, offset = 0, dim_count = 1 9 | -------------------------------------------------------------------------------- /proptest-regressions/verbose/values/array_u64.txt: -------------------------------------------------------------------------------- 1 | # Seeds for failure cases proptest has generated in the past. It is 2 | # automatically read and these particular cases re-run before any 3 | # novel cases are generated. 4 | # 5 | # It is recommended to check this file in to source control so that 6 | # everyone who runs the test benefits from these saved cases. 7 | cc d2b2567ca511e0f6c36ed2fb57746b4cde70e8f1e53137ea97c104c47d97e2e9 # shrinks to ref name = "", ref unit = "", quantization = 0.0, offset = 0, dim_count = 1 8 | -------------------------------------------------------------------------------- /proptest-regressions/verbose/values/array_u8.txt: -------------------------------------------------------------------------------- 1 | # Seeds for failure cases proptest has generated in the past. It is 2 | # automatically read and these particular cases re-run before any 3 | # novel cases are generated. 4 | # 5 | # It is recommended to check this file in to source control so that 6 | # everyone who runs the test benefits from these saved cases. 7 | cc aeee9ea69db693aa961c9e45fa6d56a52ee089734a1c8d5e5476c773b2df9959 # shrinks to ref name = "", ref unit = "", quantization = 0.0, offset = 0, dim_count = 0 8 | cc f9b6c358d54bc77f8fcb37b212275101e171db0e28244d8c454af17672bc5539 # shrinks to ref name = "", ref unit = "", quantization = 0.0, offset = 0, dim_count = 0 9 | cc 16f1c81cb4203219e1373226d6d856c7921446bfa9d76331c7c008a31529d896 # shrinks to ref name = "", ref unit = "", quantization = 0.0, offset = 0, dim_count = 2 10 | -------------------------------------------------------------------------------- /proptest-regressions/verbose/values/bool_value.txt: -------------------------------------------------------------------------------- 1 | # Seeds for failure cases proptest has generated in the past. It is 2 | # automatically read and these particular cases re-run before any 3 | # novel cases are generated. 4 | # 5 | # It is recommended to check this file in to source control so that 6 | # everyone who runs the test benefits from these saved cases. 7 | cc 94caa868fb805c7b9c2961babfde84f3037b6b9d8709d1f28dba4afb4ff37e73 # shrinks to value = false, ref name = "" 8 | cc 1d29c02836abb7546dd7aee0fa31f7062a4b70161ab679d52425bc3c44dbee19 # shrinks to value = false, ref name = "𒑰\u{1a7f}\u{1cd0}ൔ" 9 | -------------------------------------------------------------------------------- /proptest-regressions/verbose/values/f128_value.txt: -------------------------------------------------------------------------------- 1 | # Seeds for failure cases proptest has generated in the past. It is 2 | # automatically read and these particular cases re-run before any 3 | # novel cases are generated. 4 | # 5 | # It is recommended to check this file in to source control so that 6 | # everyone who runs the test benefits from these saved cases. 7 | cc dad3764f9345832928049c8a9580f463ba6a7837a1014ddc08e7bfa01608579f # shrinks to value = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], ref name = "", ref unit = "" 8 | -------------------------------------------------------------------------------- /proptest-regressions/verbose/values/f16_value.txt: -------------------------------------------------------------------------------- 1 | # Seeds for failure cases proptest has generated in the past. It is 2 | # automatically read and these particular cases re-run before any 3 | # novel cases are generated. 4 | # 5 | # It is recommended to check this file in to source control so that 6 | # everyone who runs the test benefits from these saved cases. 7 | cc 0114723704b30dac0c39c554e32305c118df92c8d0bdc35d0a94be9572b0a8f0 # shrinks to value = [0, 0], ref name = "", ref unit = "" 8 | -------------------------------------------------------------------------------- /proptest-regressions/verbose/values/f32_value.txt: -------------------------------------------------------------------------------- 1 | # Seeds for failure cases proptest has generated in the past. It is 2 | # automatically read and these particular cases re-run before any 3 | # novel cases are generated. 4 | # 5 | # It is recommended to check this file in to source control so that 6 | # everyone who runs the test benefits from these saved cases. 7 | cc 0732857b3fe8b4d674841454e3215062b2cb991a16ebaf0e578baadb57b76254 # shrinks to value = 0.0, ref name = "\u{7f}", ref unit = "\u{19ac}" 8 | cc 96a725b58d05c6997d865213de5e2bfbb0767df24d42caa8486e8f7d8edd34e6 # shrinks to value = 0.0, ref name = "", ref unit = "" 9 | -------------------------------------------------------------------------------- /proptest-regressions/verbose/values/f64_value.txt: -------------------------------------------------------------------------------- 1 | # Seeds for failure cases proptest has generated in the past. It is 2 | # automatically read and these particular cases re-run before any 3 | # novel cases are generated. 4 | # 5 | # It is recommended to check this file in to source control so that 6 | # everyone who runs the test benefits from these saved cases. 7 | cc 599e1e8d7ffd1869b794e7ca7e241713bbbc30bd89266f7ddbe4d280736943f6 # shrinks to value = -0.0, ref name = "", ref unit = "" 8 | -------------------------------------------------------------------------------- /proptest-regressions/verbose/values/i16_value.txt: -------------------------------------------------------------------------------- 1 | # Seeds for failure cases proptest has generated in the past. It is 2 | # automatically read and these particular cases re-run before any 3 | # novel cases are generated. 4 | # 5 | # It is recommended to check this file in to source control so that 6 | # everyone who runs the test benefits from these saved cases. 7 | cc 177957c7942268728200530363d83ae41f5d5722b415958c0b3fa4dc50f43cd8 # shrinks to value = 0, ref name = "", ref unit = "", quantization = 0.0, offset = 0 8 | -------------------------------------------------------------------------------- /proptest-regressions/verbose/values/i32_value.txt: -------------------------------------------------------------------------------- 1 | # Seeds for failure cases proptest has generated in the past. It is 2 | # automatically read and these particular cases re-run before any 3 | # novel cases are generated. 4 | # 5 | # It is recommended to check this file in to source control so that 6 | # everyone who runs the test benefits from these saved cases. 7 | cc a420aa8441091ddd240461cd79833188a4729757dac53cde391178593116c98e # shrinks to value = 0, ref name = "", ref unit = "", quantization = 0.0, offset = 0 8 | -------------------------------------------------------------------------------- /proptest-regressions/verbose/values/i8_value.txt: -------------------------------------------------------------------------------- 1 | # Seeds for failure cases proptest has generated in the past. It is 2 | # automatically read and these particular cases re-run before any 3 | # novel cases are generated. 4 | # 5 | # It is recommended to check this file in to source control so that 6 | # everyone who runs the test benefits from these saved cases. 7 | cc a4aa3808eb5cb9a224cfbe6099304d38a0aa8d5de986f7ca42ee61be800e017a # shrinks to value = 0, ref name = "", ref unit = "", quantization = 0.0, offset = 0 8 | -------------------------------------------------------------------------------- /proptest-regressions/verbose/values/raw_f16.txt: -------------------------------------------------------------------------------- 1 | # Seeds for failure cases proptest has generated in the past. It is 2 | # automatically read and these particular cases re-run before any 3 | # novel cases are generated. 4 | # 5 | # It is recommended to check this file in to source control so that 6 | # everyone who runs the test benefits from these saved cases. 7 | cc 8448109ac8a7a743ac03b712857e5bc840ea337a08fb9faefa270104fc4d3e1f # shrinks to value = 31745 8 | -------------------------------------------------------------------------------- /proptest-regressions/verbose/values/raw_value.txt: -------------------------------------------------------------------------------- 1 | # Seeds for failure cases proptest has generated in the past. It is 2 | # automatically read and these particular cases re-run before any 3 | # novel cases are generated. 4 | # 5 | # It is recommended to check this file in to source control so that 6 | # everyone who runs the test benefits from these saved cases. 7 | cc 97765adffcca75713b116bbe4eb04f109aa9587c7c3ab6b9b94e540e58e93abf # shrinks to ref data = "", ref name = "" 8 | -------------------------------------------------------------------------------- /proptest-regressions/verbose/values/string_value.txt: -------------------------------------------------------------------------------- 1 | # Seeds for failure cases proptest has generated in the past. It is 2 | # automatically read and these particular cases re-run before any 3 | # novel cases are generated. 4 | # 5 | # It is recommended to check this file in to source control so that 6 | # everyone who runs the test benefits from these saved cases. 7 | cc 1cea8cd1d85e6192be3f19e2c62a490169fdb5d9adcce84feeb4e5c75d022011 # shrinks to ref value = "", ref name = "", ref unit = "" 8 | -------------------------------------------------------------------------------- /proptest-regressions/verbose/values/struct_value.txt: -------------------------------------------------------------------------------- 1 | # Seeds for failure cases proptest has generated in the past. It is 2 | # automatically read and these particular cases re-run before any 3 | # novel cases are generated. 4 | # 5 | # It is recommended to check this file in to source control so that 6 | # everyone who runs the test benefits from these saved cases. 7 | cc 7e7c24792b552edf45e1b80491b05b112d2e2b67bc7ac1b0cd75d6994a9e57da # shrinks to ref data_str = "", ref name = "" 8 | cc 78175f1b6c733b7fdca370a7b50ecfdc6cc16437924694071f49ed651b167dbd # shrinks to ref data_str = "", ref name = "" 9 | -------------------------------------------------------------------------------- /proptest-regressions/verbose/values/u8_value.txt: -------------------------------------------------------------------------------- 1 | # Seeds for failure cases proptest has generated in the past. It is 2 | # automatically read and these particular cases re-run before any 3 | # novel cases are generated. 4 | # 5 | # It is recommended to check this file in to source control so that 6 | # everyone who runs the test benefits from these saved cases. 7 | cc d987c8dce1200444108df4854ce306f3a48e60c7f86eeaf604960c4d54857cde # shrinks to value = 0, ref name = "", ref unit = "", quantization = 0.0, offset = 0 8 | -------------------------------------------------------------------------------- /scripts/README.md: -------------------------------------------------------------------------------- 1 | # Scripts 2 | 3 | This folder contains some helpfull scripts that can be helpfull during development. 4 | 5 | ## Pre-Requirements 6 | 7 | To run the scripts in this folder you have to install a few tools on your system. You can use the following script to install the required tools (just replace `brew` with the packet manager of your choice). 8 | 9 | ```sh 10 | # nightly toolchain 11 | rustup toolchain install nightly 12 | 13 | # llvm toolchain for nightly 14 | rustup component add --toolchain nightly llvm-tools-preview 15 | 16 | # cargo-binutils and rustfilt for nightly 17 | cargo +nightly install cargo-binutils rustfilt 18 | 19 | # jq from your package manager of choice (just replace brew with apt or a similar manager) 20 | brew install jq 21 | ``` 22 | 23 | ## coverage.bash 24 | 25 | `coverage.bash` calculates the region & line based code coverage of the tests. Just execute it and it will write the reports to `target/coverage`. 26 | 27 | Note: When executing this script `cargo clean` be executed and previous coverage data will be deleted. 28 | -------------------------------------------------------------------------------- /scripts/coverage.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # PRE-REQUIREMENTS: 4 | # 5 | # To execute this script you have to have a few additional things installed 6 | # on your system. You can run the following commands to do so: 7 | # 8 | # ```sh 9 | # # nightly toolchain 10 | # rustup toolchain install nightly 11 | # 12 | # # llvm toolchain for nightly 13 | # rustup component add --toolchain nightly llvm-tools-preview 14 | # 15 | # # cargo-binutils and rustfilt for nightly 16 | # cargo +nightly install cargo-binutils rustfilt 17 | # 18 | # # jq from your package manager of choice (just replace brew with apt or a similar manager) 19 | # brew install jq 20 | # ``` 21 | 22 | # switch to the etherparse root directory 23 | pushd "$(dirname "${BASH_SOURCE[0]}")/.." 24 | 25 | # folder for all the coverage files 26 | coverage_dir="target/coverage" 27 | 28 | # make sure no cached data is used (can interfere with the instrumentalisation and testruns) 29 | cargo clean 30 | 31 | # remove previous runs and setup the result folders 32 | rm -rf "${coverage_dir}" 33 | mkdir -p "${coverage_dir}" 34 | mkdir -p "${coverage_dir}/raw" 35 | 36 | # run the instrumented tests 37 | RUSTFLAGS="-C instrument-coverage" \ 38 | LLVM_PROFILE_FILE="${coverage_dir}/raw/coverage-%m.profraw" \ 39 | cargo test --tests --all-features 40 | 41 | # determine the filenames of the run executables 42 | RUSTFLAGS="-C instrument-coverage" \ 43 | LLVM_PROFILE_FILE="${coverage_dir}/raw/coverage-%m.profraw" \ 44 | cargo test --no-run --all-features --message-format=json | jq -r "select(.profile.test == true) | .filenames[]" | grep -v dSYM - > "${coverage_dir}/raw/filenames.txt" 45 | 46 | cargo profdata -- merge -sparse "${coverage_dir}/raw/coverage-"*".profraw" -o "${coverage_dir}/raw/merge.profdata" 47 | 48 | cargo cov -- report \ 49 | --use-color \ 50 | --summary-only \ 51 | --Xdemangler=rustfilt \ 52 | --ignore-filename-regex='/.cargo/registry' \ 53 | --ignore-filename-regex='/.rustup/toolchains' \ 54 | --ignore-filename-regex='/rustc' \ 55 | "--instr-profile=${coverage_dir}/raw/merge.profdata" \ 56 | $(printf -- "-object %s " $(cat "${coverage_dir}/raw/filenames.txt")) \ 57 | > "${coverage_dir}/report_all.txt" 58 | 59 | cargo cov -- report \ 60 | --use-color \ 61 | --summary-only \ 62 | --Xdemangler=rustfilt \ 63 | --ignore-filename-regex='/.cargo/registry' \ 64 | --ignore-filename-regex='/.rustup/toolchains' \ 65 | --ignore-filename-regex='/rustc' \ 66 | --ignore-filename-regex='tests/' \ 67 | "--instr-profile=${coverage_dir}/raw/merge.profdata" \ 68 | $(printf -- "-object %s " $(cat "${coverage_dir}/raw/filenames.txt")) \ 69 | > "${coverage_dir}/report_without_tests.txt" 70 | 71 | cargo cov -- show --format=html \ 72 | --Xdemangler=rustfilt \ 73 | --ignore-filename-regex='/.cargo/registry' \ 74 | --ignore-filename-regex='/.rustup/toolchains' \ 75 | --ignore-filename-regex='/rustc' \ 76 | "--instr-profile=${coverage_dir}/raw/merge.profdata" \ 77 | $(printf -- "-object %s " $(cat "${coverage_dir}/raw/filenames.txt")) \ 78 | "--output-dir=${coverage_dir}/html_all" 79 | 80 | cargo cov -- show --format=html \ 81 | --Xdemangler=rustfilt \ 82 | --ignore-filename-regex='/.cargo/registry' \ 83 | --ignore-filename-regex='/.rustup/toolchains' \ 84 | --ignore-filename-regex='/rustc' \ 85 | --ignore-filename-regex='tests/' \ 86 | "--instr-profile=${coverage_dir}/raw/merge.profdata" \ 87 | $(printf -- "-object %s " $(cat "${coverage_dir}/raw/filenames.txt")) \ 88 | "--output-dir=${coverage_dir}/html_without_tests" 89 | 90 | cargo cov -- export --format=lcov \ 91 | --Xdemangler=rustfilt \ 92 | --ignore-filename-regex='/.cargo/registry' \ 93 | --ignore-filename-regex='/.rustup/toolchains' \ 94 | --ignore-filename-regex='/rustc' \ 95 | "--instr-profile=${coverage_dir}/raw/merge.profdata" \ 96 | $(printf -- "-object %s " $(cat "${coverage_dir}/raw/filenames.txt")) \ 97 | > "${coverage_dir}/export.lcov.txt" 98 | 99 | cargo cov -- export --format=text \ 100 | --Xdemangler=rustfilt \ 101 | --ignore-filename-regex='/.cargo/registry' \ 102 | --ignore-filename-regex='/.rustup/toolchains' \ 103 | --ignore-filename-regex='/rustc' \ 104 | "--instr-profile=${coverage_dir}/raw/merge.profdata" \ 105 | $(printf -- "-object %s " $(cat "${coverage_dir}/raw/filenames.txt")) \ 106 | > "${coverage_dir}/export.json" 107 | 108 | popd -------------------------------------------------------------------------------- /src/control/mod.rs: -------------------------------------------------------------------------------- 1 | 2 | /// "Set Log Level" service id 3 | pub const CMD_ID_SET_LOG_LEVEL: u32 = 0x01; 4 | /// "Set Log Level" name 5 | pub const CMD_NAME_SET_LOG_LEVEL: &str = "SetLogLevel"; 6 | 7 | /// "Set Trace Status" service id 8 | pub const CMD_ID_SET_TRACE_STATUS: u32 = 0x02; 9 | /// "Set Trace Status" name 10 | pub const CMD_NAME_SET_TRACE_STATUS: &str = "SetTraceStatus"; 11 | 12 | /// "Get Log Info" service id 13 | pub const CMD_ID_GET_LOG_INFO: u32 = 0x03; 14 | /// "Get Log Info" name 15 | pub const CMD_NAME_GET_LOG_INFO: &str = "GetLogInfo"; 16 | 17 | /// "Get Default Log Level" service id. 18 | pub const CMD_ID_GET_DEFAULT_LOG_LEVEL: u32 = 0x04; 19 | /// "Get Default Log Level" name. 20 | pub const CMD_NAME_GET_DEFAULT_LOG_LEVEL: &str = "GetDefaultLogLevel"; 21 | 22 | /// "Store Configuration" service id. 23 | pub const CMD_ID_STORE_CONFIGURATION: u32 = 0x05; 24 | /// "Store Configuration" name. 25 | pub const CMD_NAME_STORE_CONFIGURATION: &str = "StoreConfiguration"; 26 | 27 | /// "Reset to Factory Default" service id. 28 | pub const CMD_ID_RESET_TO_FACTORY_DEFAULT: u32 = 0x06; 29 | /// "Reset to Factory Default" name. 30 | pub const CMD_NAME_RESET_TO_FACTORY_DEFAULT: &str = "ResetToFactoryDefault"; 31 | 32 | /// "Set Message Filtering" service id. 33 | pub const CMD_ID_SET_MESSAGE_FILTERING: u32 = 0x0A; 34 | /// "Set Message Filtering" name. 35 | pub const CMD_NAME_SET_MESSAGE_FILTERING: &str = "SetMessageFiltering"; 36 | 37 | /// "Set Default LogLevel" service id. 38 | pub const CMD_ID_SET_DEFAULT_LOG_LEVEL: u32 = 0x11; 39 | /// "Set Default LogLevel" name. 40 | pub const CMD_NAME_SET_DEFAULT_LOG_LEVEL: &str = "SetDefaultLogLevel"; 41 | 42 | /// "Set Default Trace Status" service id. 43 | pub const CMD_ID_SET_DEFAULT_TRACE_STATUS: u32 = 0x12; 44 | /// "Set Default Trace Status" name. 45 | pub const CMD_NAME_SET_DEFAULT_TRACE_STATUS: &str = "SetDefaultTraceStatus"; 46 | 47 | /// "Get ECU Software Version" service id. 48 | pub const CMD_ID_GET_SOFTWARE_VERSION: u32 = 0x13; 49 | /// "Get ECU Software Version" name. 50 | pub const CMD_NAME_GET_SOFTWARE_VERSION: &str = "GetSoftwareVersion"; 51 | 52 | /// "Get Default Trace Status" service id. 53 | pub const CMD_ID_GET_DEFAULT_TRACE_STATUS: u32 = 0x15; 54 | /// "Get Default Trace Status" name. 55 | pub const CMD_NAME_GET_DEFAULT_TRACE_STATUS: &str = "GetDefaultTraceStatus"; 56 | 57 | /// "Get LogChannel Names" service id. 58 | pub const CMD_ID_GET_LOG_CHANNEL_NAMES: u32 = 0x17; 59 | /// "Get LogChannel Names" name. 60 | pub const CMD_NAME_GET_LOG_CHANNEL_NAMES: &str = "GetLogChannelNames"; 61 | 62 | /// "Get Trace Status" service id. 63 | pub const CMD_ID_GET_TRACE_STATUS: u32 = 0x1F; 64 | /// "Get Trace Status" name. 65 | pub const CMD_NAME_GET_TRACE_STATUS: &str = "GetTraceStatus"; 66 | 67 | /// "Set LogChannel Assignment" service id. 68 | pub const CMD_ID_SET_LOG_CHANNEL_ASSIGNMENT: u32 = 0x20; 69 | /// "Set LogChannel Assignment" name. 70 | pub const CMD_NAME_SET_LOG_CHANNEL_ASSIGNMENT: &str = "SetLogChannelAssignment"; 71 | 72 | /// "Set LogChannel Threshold" service id. 73 | pub const CMD_ID_SET_LOG_CHANNEL_THRESHOLD: u32 = 0x21; 74 | /// "Set LogChannel Threshold" name. 75 | pub const CMD_NAME_SET_LOG_CHANNEL_THRESHOLD: &str = "SetLogChannelThreshold"; 76 | 77 | /// "Get LogChannel Threshold" service id. 78 | pub const CMD_ID_GET_LOG_CHANNEL_THRESHOLD: u32 = 0x22; 79 | /// "Get LogChannel Threshold" name. 80 | pub const CMD_NAME_GET_LOG_CHANNEL_THRESHOLD: &str = "GetLogChannelThreshold"; 81 | 82 | /// "BufferOverflowNotification" service id. 83 | pub const CMD_ID_BUFFER_OVERFLOW_NOTIFICATION: u32 = 0x23; 84 | /// "BufferOverflowNotification" name. 85 | pub const CMD_NAME_BUFFER_OVERFLOW_NOTIFICATION: &str = "BufferOverflowNotification"; 86 | 87 | /// "Call SWC Injection" service ids range. 88 | pub const CMD_IDS_CALL_SWC_INJECTIONS: core::ops::RangeInclusive = 0xFFF..=0xFFFFFFFF; 89 | /// "Call SWC Injection" name. 90 | pub const CMD_NAME_CALL_SWC_INJECTIONS: &str = "CallSWCInjection"; 91 | 92 | /// Get the name of the service based on the service id given. 93 | pub fn get_control_command_name(service_id: u32) -> Option<&'static str> { 94 | match service_id { 95 | 0x01 => Some(CMD_NAME_SET_LOG_LEVEL), 96 | 0x02 => Some(CMD_NAME_SET_TRACE_STATUS), 97 | 0x03 => Some(CMD_NAME_GET_LOG_INFO), 98 | 0x04 => Some(CMD_NAME_GET_DEFAULT_LOG_LEVEL), 99 | 0x05 => Some(CMD_NAME_STORE_CONFIGURATION), 100 | 0x06 => Some(CMD_NAME_RESET_TO_FACTORY_DEFAULT), 101 | 0x0A => Some(CMD_NAME_SET_MESSAGE_FILTERING), 102 | 0x11 => Some(CMD_NAME_SET_DEFAULT_LOG_LEVEL), 103 | 0x12 => Some(CMD_NAME_SET_DEFAULT_TRACE_STATUS), 104 | 0x13 => Some(CMD_NAME_GET_SOFTWARE_VERSION), 105 | 0x15 => Some(CMD_NAME_GET_DEFAULT_TRACE_STATUS), 106 | 0x17 => Some(CMD_NAME_GET_LOG_CHANNEL_NAMES), 107 | 0x1F => Some(CMD_NAME_GET_TRACE_STATUS), 108 | 0x20 => Some(CMD_NAME_SET_LOG_CHANNEL_ASSIGNMENT), 109 | 0x21 => Some(CMD_NAME_SET_LOG_CHANNEL_THRESHOLD), 110 | 0x22 => Some(CMD_NAME_GET_LOG_CHANNEL_THRESHOLD), 111 | 0x23 => Some(CMD_NAME_BUFFER_OVERFLOW_NOTIFICATION), 112 | 0xFFF..=0xFFFFFFFF => Some(CMD_NAME_CALL_SWC_INJECTIONS), 113 | _ => None, 114 | } 115 | } 116 | 117 | #[cfg(test)] 118 | mod test { 119 | use super::*; 120 | use proptest::prelude::*; 121 | 122 | proptest!{ 123 | #[test] 124 | fn test_get_control_command_name( 125 | unknown_id in 0x24..0xFFFu32, 126 | sw_injections_id in 0xFFF..=0xFFFFFFFFu32 127 | ) { 128 | let tests = [ 129 | (0x00, None), 130 | (0x01, Some("SetLogLevel")), 131 | (0x02, Some("SetTraceStatus")), 132 | (0x03, Some("GetLogInfo")), 133 | (0x04, Some("GetDefaultLogLevel")), 134 | (0x05, Some("StoreConfiguration")), 135 | (0x06, Some("ResetToFactoryDefault")), 136 | (0x07, None), 137 | (0x08, None), 138 | (0x09, None), 139 | (0x0A, Some("SetMessageFiltering")), 140 | (0x0B, None), 141 | (0x0C, None), 142 | (0x0D, None), 143 | (0x0E, None), 144 | (0x0F, None), 145 | (0x10, None), 146 | (0x11, Some("SetDefaultLogLevel")), 147 | (0x12, Some("SetDefaultTraceStatus")), 148 | (0x13, Some("GetSoftwareVersion")), 149 | (0x14, None), 150 | (0x15, Some("GetDefaultTraceStatus")), 151 | (0x16, None), 152 | (0x17, Some("GetLogChannelNames")), 153 | (0x18, None), 154 | (0x19, None), 155 | (0x1A, None), 156 | (0x1B, None), 157 | (0x1C, None), 158 | (0x1D, None), 159 | (0x1F, Some("GetTraceStatus")), 160 | (0x20, Some("SetLogChannelAssignment")), 161 | (0x21, Some("SetLogChannelThreshold")), 162 | (0x22, Some("GetLogChannelThreshold")), 163 | (0x23, Some("BufferOverflowNotification")), 164 | ]; 165 | for test in tests { 166 | assert_eq!(test.1, get_control_command_name(test.0)) 167 | } 168 | assert_eq!(None, get_control_command_name(unknown_id)); 169 | assert_eq!(Some("CallSWCInjection"), get_control_command_name(sw_injections_id)); 170 | } 171 | } 172 | } -------------------------------------------------------------------------------- /src/dlt_extended_header.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | ///Extended dlt header (optional header in the dlt header) 4 | #[derive(Debug, PartialEq, Eq, Clone, Default)] 5 | pub struct DltExtendedHeader { 6 | pub message_info: DltMessageInfo, 7 | pub number_of_arguments: u8, 8 | pub application_id: [u8; 4], 9 | pub context_id: [u8; 4], 10 | } 11 | 12 | impl DltExtendedHeader { 13 | ///Create a extended header for a non verbose log message with given application id & context id. 14 | pub fn new_non_verbose_log( 15 | log_level: DltLogLevel, 16 | application_id: [u8; 4], 17 | context_id: [u8; 4], 18 | ) -> DltExtendedHeader { 19 | DltExtendedHeader { 20 | message_info: DltMessageInfo(DltMessageType::Log(log_level).to_byte().unwrap()), 21 | number_of_arguments: 0, 22 | application_id, 23 | context_id, 24 | } 25 | } 26 | 27 | ///Create a extended header for a non verbose message with given message type, application id & context id. 28 | pub fn new_non_verbose( 29 | message_type: DltMessageType, 30 | application_id: [u8; 4], 31 | context_id: [u8; 4], 32 | ) -> Result { 33 | Ok(DltExtendedHeader { 34 | message_info: DltMessageInfo(message_type.to_byte()?), 35 | number_of_arguments: 0, 36 | application_id, 37 | context_id, 38 | }) 39 | } 40 | 41 | ///Returns true if the extended header flags the message as a verbose message. 42 | #[inline] 43 | pub fn is_verbose(&self) -> bool { 44 | self.message_info.is_verbose() 45 | } 46 | 47 | ///Sets or unsets the is_verbose bit in the DltExtendedHeader. 48 | #[inline] 49 | pub fn set_is_verbose(&mut self, is_verbose: bool) { 50 | if is_verbose { 51 | self.message_info.0 |= 0b1; 52 | } else { 53 | self.message_info.0 &= 0b1111_1110; 54 | } 55 | } 56 | 57 | ///Returns message type info or `Option::None` for reserved values. 58 | #[inline] 59 | pub fn message_type(&self) -> Option { 60 | self.message_info.into_message_type() 61 | } 62 | 63 | ///Set message type info and based on that the message type. 64 | #[inline] 65 | pub fn set_message_type(&mut self, value: DltMessageType) -> Result<(), error::RangeError> { 66 | let encoded = value.to_byte()?; 67 | 68 | //unset old message type & set the new one 69 | self.message_info.0 &= 0b0000_0001; 70 | self.message_info.0 |= encoded; 71 | 72 | //all good 73 | Ok(()) 74 | } 75 | } 76 | 77 | /// Tests for `DltExtendedHeader` methods 78 | #[cfg(test)] 79 | mod dlt_extended_header_tests { 80 | 81 | use super::*; 82 | use crate::proptest_generators::*; 83 | use proptest::prelude::*; 84 | 85 | #[test] 86 | fn clone_eq() { 87 | let header: DltExtendedHeader = Default::default(); 88 | assert_eq!(header, header.clone()); 89 | } 90 | 91 | #[test] 92 | fn debug() { 93 | let header: DltExtendedHeader = Default::default(); 94 | assert_eq!( 95 | format!( 96 | "DltExtendedHeader {{ message_info: {:?}, number_of_arguments: {:?}, application_id: {:?}, context_id: {:?} }}", 97 | header.message_info, 98 | header.number_of_arguments, 99 | header.application_id, 100 | header.context_id 101 | ), 102 | format!("{:?}", header) 103 | ); 104 | } 105 | 106 | #[test] 107 | fn default() { 108 | let header: DltExtendedHeader = Default::default(); 109 | assert_eq!(header.message_info.0, 0); 110 | assert_eq!(header.number_of_arguments, 0); 111 | assert_eq!(header.application_id, [0, 0, 0, 0]); 112 | assert_eq!(header.context_id, [0, 0, 0, 0]); 113 | } 114 | 115 | proptest! { 116 | #[test] 117 | fn new_non_verbose_log( 118 | log_level in log_level_any(), 119 | application_id in any::<[u8;4]>(), 120 | context_id in any::<[u8;4]>()) 121 | { 122 | use DltMessageType::Log; 123 | let header = DltExtendedHeader::new_non_verbose_log(log_level.clone(), application_id, context_id); 124 | assert_eq!(Log(log_level).to_byte().unwrap(), header.message_info.0); 125 | assert_eq!(0, header.number_of_arguments); 126 | assert_eq!(application_id, header.application_id); 127 | assert_eq!(context_id, header.context_id); 128 | } 129 | } 130 | 131 | proptest! { 132 | #[test] 133 | fn new_non_verbose( 134 | message_type in message_type_any(), 135 | application_id in any::<[u8;4]>(), 136 | context_id in any::<[u8;4]>(), 137 | invalid_user_defined in 0x10..0xffu8 138 | ) { 139 | // valid data 140 | { 141 | let header = DltExtendedHeader::new_non_verbose( 142 | message_type.clone(), 143 | application_id, 144 | context_id 145 | ).unwrap(); 146 | assert_eq!(message_type.to_byte().unwrap(), header.message_info.0); 147 | assert_eq!(0, header.number_of_arguments); 148 | assert_eq!(application_id, header.application_id); 149 | assert_eq!(context_id, header.context_id); 150 | } 151 | 152 | // invalid data 153 | { 154 | use DltMessageType::NetworkTrace; 155 | use DltNetworkType::UserDefined; 156 | use error::RangeError::NetworkTypekUserDefinedOutsideOfRange; 157 | 158 | let result = DltExtendedHeader::new_non_verbose( 159 | NetworkTrace(UserDefined(invalid_user_defined)), 160 | application_id, 161 | context_id 162 | ).unwrap_err(); 163 | assert_eq!(NetworkTypekUserDefinedOutsideOfRange(invalid_user_defined), result); 164 | } 165 | } 166 | } 167 | 168 | #[test] 169 | fn set_is_verbose() { 170 | let mut header: DltExtendedHeader = Default::default(); 171 | let original = header.clone(); 172 | header.set_is_verbose(true); 173 | assert_eq!(true, header.is_verbose()); 174 | header.set_is_verbose(false); 175 | assert_eq!(false, header.is_verbose()); 176 | assert_eq!(original, header); 177 | } 178 | 179 | proptest! { 180 | #[test] 181 | fn set_message_type( 182 | verbose in any::(), 183 | message_type0 in message_type_any(), 184 | message_type1 in message_type_any()) 185 | { 186 | let mut header: DltExtendedHeader = Default::default(); 187 | 188 | //set verbose (stored in same field, to ensure no side effects) 189 | header.set_is_verbose(verbose); 190 | assert_eq!(header.is_verbose(), verbose); 191 | 192 | //set to first message type 193 | header.set_message_type(message_type0.clone()).unwrap(); 194 | assert_eq!(header.is_verbose(), verbose); 195 | assert_eq!(header.message_type(), Some(message_type0)); 196 | 197 | //set to second message type (to make sure the old type is correctly cleaned) 198 | header.set_message_type(message_type1.clone()).unwrap(); 199 | assert_eq!(header.is_verbose(), verbose); 200 | assert_eq!(header.message_type(), Some(message_type1)); 201 | } 202 | } 203 | 204 | #[test] 205 | fn message_type() { 206 | use { 207 | DltControlMessageType::*, DltLogLevel::*, DltMessageType::*, DltNetworkType::*, 208 | DltTraceType::*, 209 | }; 210 | 211 | //check that setting & resetting does correctly reset the values 212 | { 213 | let mut header = DltExtendedHeader::new_non_verbose_log( 214 | Fatal, 215 | Default::default(), 216 | Default::default(), 217 | ); 218 | 219 | header.set_message_type(NetworkTrace(SomeIp)).unwrap(); 220 | assert_eq!(false, header.is_verbose()); 221 | assert_eq!(Some(NetworkTrace(SomeIp)), header.message_type()); 222 | 223 | //set to a different value with non overlapping bits (to make sure the values are reset) 224 | header.set_message_type(Trace(FunctionIn)).unwrap(); 225 | assert_eq!(false, header.is_verbose()); 226 | assert_eq!(Some(Trace(FunctionIn)), header.message_type()); 227 | } 228 | 229 | //check None return type when a unknown value is presented 230 | //message type 231 | for message_type_id in 4..=0b111 { 232 | let mut header = DltExtendedHeader::new_non_verbose_log( 233 | Fatal, 234 | Default::default(), 235 | Default::default(), 236 | ); 237 | header.message_info = DltMessageInfo(message_type_id << 1); 238 | assert_eq!(None, header.message_type()); 239 | } 240 | 241 | //msin bad values 242 | let bad_values = [ 243 | //bad log level 0 & everything above 6 244 | (Log(Fatal), (0u8..1).chain(7u8..=0xf)), 245 | //bad trace source (0 & everything above 5) 246 | (Trace(FunctionIn), (0u8..1).chain(6u8..=0xf)), 247 | //bad control message type (0 & everything above 2) 248 | (Control(Request), (0u8..1).chain(3u8..=0xf)), 249 | ]; 250 | 251 | for t in bad_values.iter() { 252 | for value in t.1.clone() { 253 | let mut header = DltExtendedHeader::new_non_verbose( 254 | t.0.clone(), 255 | Default::default(), 256 | Default::default(), 257 | ) 258 | .unwrap(); 259 | header.message_info.0 &= 0b0000_1111; 260 | header.message_info.0 |= value << 4; 261 | assert_eq!(None, header.message_type()); 262 | } 263 | } 264 | 265 | //check set out of range error 266 | { 267 | use error::RangeError::*; 268 | use DltLogLevel::Fatal; 269 | for i in 0x10..=0xff { 270 | let mut header = DltExtendedHeader::new_non_verbose_log( 271 | Fatal, 272 | Default::default(), 273 | Default::default(), 274 | ); 275 | assert_eq!( 276 | Err(NetworkTypekUserDefinedOutsideOfRange(i)), 277 | header.set_message_type(NetworkTrace(UserDefined(i))) 278 | ); 279 | } 280 | } 281 | } 282 | } // mod dlt_extended_header_tests 283 | -------------------------------------------------------------------------------- /src/dlt_message_info.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | DltMessageType, EXT_MSIN_MSTP_TYPE_CONTROL, EXT_MSIN_MSTP_TYPE_LOG, 3 | EXT_MSIN_MSTP_TYPE_NW_TRACE, EXT_MSIN_MSTP_TYPE_TRACE, 4 | }; 5 | 6 | /// Message info identifying the type of message (e.g. log, trace, network trace & control). 7 | #[derive(Copy, Clone, Default, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] 8 | pub struct DltMessageInfo(pub u8); 9 | 10 | impl DltMessageInfo { 11 | /// Returns if the message is a verbose dlt message. 12 | #[inline] 13 | pub fn is_verbose(&self) -> bool { 14 | 0 != self.0 & 0b0000_0001 15 | } 16 | 17 | /// Returns if the message is a log message. 18 | #[inline] 19 | pub fn is_log(&self) -> bool { 20 | EXT_MSIN_MSTP_TYPE_LOG == self.0 & 0b0000_1110 21 | } 22 | 23 | /// Returns if the message is a trace message. 24 | #[inline] 25 | pub fn is_trace(&self) -> bool { 26 | EXT_MSIN_MSTP_TYPE_TRACE == self.0 & 0b0000_1110 27 | } 28 | 29 | /// Returns if the message is a trace message. 30 | #[inline] 31 | pub fn is_network(&self) -> bool { 32 | EXT_MSIN_MSTP_TYPE_NW_TRACE == self.0 & 0b0000_1110 33 | } 34 | 35 | /// Returns if the message is a control message. 36 | #[inline] 37 | pub fn is_control(&self) -> bool { 38 | EXT_MSIN_MSTP_TYPE_CONTROL == self.0 & 0b0000_1110 39 | } 40 | 41 | /// Returns the message type. 42 | #[inline] 43 | pub fn into_message_type(&self) -> Option { 44 | DltMessageType::from_byte(self.0) 45 | } 46 | } 47 | 48 | #[cfg(test)] 49 | mod test { 50 | use super::*; 51 | 52 | #[test] 53 | fn is_checks() { 54 | for v in 0..=u8::MAX { 55 | let info = DltMessageInfo(v); 56 | if 0 != v & 0b1 { 57 | assert!(info.is_verbose()); 58 | } else { 59 | assert_eq!(false, info.is_verbose()); 60 | } 61 | let mstp = (v & 0b0000_1110) >> 1; 62 | if mstp == 0 { 63 | assert!(info.is_log()); 64 | } else { 65 | assert_eq!(false, info.is_log()); 66 | } 67 | if mstp == 1 { 68 | assert!(info.is_trace()); 69 | } else { 70 | assert_eq!(false, info.is_trace()); 71 | } 72 | if mstp == 2 { 73 | assert!(info.is_network()); 74 | } else { 75 | assert_eq!(false, info.is_network()); 76 | } 77 | if mstp == 3 { 78 | assert!(info.is_control()); 79 | } else { 80 | assert_eq!(false, info.is_control()); 81 | } 82 | } 83 | } 84 | 85 | #[test] 86 | fn into_message_type() { 87 | for v in 0..=u8::MAX { 88 | let info = DltMessageInfo(v); 89 | assert_eq!(info.into_message_type(), DltMessageType::from_byte(v)); 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/dlt_slice_iterator.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | /// Allows iterating over the someip message in a udp or tcp payload. 4 | #[derive(Clone, Debug, Eq, PartialEq)] 5 | pub struct SliceIterator<'a> { 6 | slice: &'a [u8], 7 | } 8 | 9 | impl<'a> SliceIterator<'a> { 10 | #[inline] 11 | pub fn new(slice: &'a [u8]) -> SliceIterator<'a> { 12 | SliceIterator { slice } 13 | } 14 | 15 | /// Returns the slice of data still left in the iterator. 16 | pub fn slice(&self) -> &'a [u8] { 17 | self.slice 18 | } 19 | } 20 | 21 | impl<'a> Iterator for SliceIterator<'a> { 22 | type Item = Result, error::PacketSliceError>; 23 | 24 | #[inline] 25 | fn next(&mut self) -> Option, error::PacketSliceError>> { 26 | if !self.slice.is_empty() { 27 | //parse 28 | let result = DltPacketSlice::from_slice(self.slice); 29 | 30 | //move the slice depending on the result 31 | match &result { 32 | Err(_) => { 33 | //error => move the slice to an len = 0 position so that the iterator ends 34 | let len = self.slice.len(); 35 | self.slice = &self.slice[len..]; 36 | } 37 | Ok(ref value) => { 38 | //by the length just taken by the slice 39 | self.slice = &self.slice[value.slice().len()..]; 40 | } 41 | } 42 | 43 | //return parse result 44 | Some(result) 45 | } else { 46 | None 47 | } 48 | } 49 | } 50 | 51 | /// Tests for `SliceIterator` 52 | #[cfg(test)] 53 | mod slice_interator_tests { 54 | 55 | use super::*; 56 | use crate::proptest_generators::*; 57 | use proptest::prelude::*; 58 | 59 | #[test] 60 | fn clone_eq() { 61 | let it = SliceIterator { slice: &[] }; 62 | assert_eq!(it, it.clone()); 63 | } 64 | 65 | #[test] 66 | fn debug() { 67 | let it = SliceIterator { slice: &[] }; 68 | assert_eq!( 69 | format!("SliceIterator {{ slice: {:?} }}", it.slice), 70 | format!("{:?}", it) 71 | ); 72 | } 73 | 74 | #[test] 75 | fn slice() { 76 | let buffer: [u8; 4] = [1, 2, 3, 4]; 77 | let it = SliceIterator { slice: &buffer }; 78 | assert_eq!(it.slice(), &buffer); 79 | } 80 | 81 | proptest! { 82 | #[test] 83 | fn iterator(ref packets in prop::collection::vec(dlt_header_with_payload_any(), 1..5)) { 84 | use error::PacketSliceError::*; 85 | 86 | //serialize the packets 87 | let mut buffer = Vec::with_capacity( 88 | (*packets).iter().fold(0, |acc, x| acc + usize::from(x.0.header_len()) + x.1.len()) 89 | ); 90 | 91 | let mut offsets: Vec<(usize, usize)> = Vec::with_capacity(packets.len()); 92 | 93 | for packet in packets { 94 | 95 | //save the start for later processing 96 | let start = buffer.len(); 97 | 98 | //header & payload 99 | buffer.extend_from_slice(&packet.0.to_bytes()); 100 | buffer.extend_from_slice(&packet.1); 101 | 102 | //safe the offset for later 103 | offsets.push((start, buffer.len())); 104 | } 105 | 106 | //determine the expected output 107 | let mut expected: Vec> = Vec::with_capacity(packets.len()); 108 | for offset in &offsets { 109 | //create the expected slice 110 | let slice = &buffer[offset.0..offset.1]; 111 | let e = DltPacketSlice::from_slice(slice).unwrap(); 112 | assert_eq!(e.slice(), slice); 113 | expected.push(e); 114 | } 115 | 116 | //iterate over packets 117 | assert_eq!(expected, SliceIterator::new(&buffer).map(|x| x.unwrap()).collect::>>()); 118 | 119 | //check for error return when the slice is too small 120 | //first entry 121 | { 122 | let o = offsets.first().unwrap(); 123 | let mut it = SliceIterator::new(&buffer[..(o.1 - 1)]); 124 | 125 | assert_matches!(it.next(), Some(Err(UnexpectedEndOfSlice(_)))); 126 | //check that the iterator does not continue 127 | assert_matches!(it.next(), None); 128 | } 129 | //last entry 130 | { 131 | let o = offsets.last().unwrap(); 132 | let it = SliceIterator::new(&buffer[..(o.1 - 1)]); 133 | let mut it = it.skip(offsets.len()-1); 134 | 135 | assert_matches!(it.next(), Some(Err(UnexpectedEndOfSlice(_)))); 136 | //check that the iterator does not continue 137 | assert_matches!(it.next(), None); 138 | } 139 | } 140 | } 141 | } // mod slice_iterator_tests 142 | -------------------------------------------------------------------------------- /src/dlt_typed_payload.rs: -------------------------------------------------------------------------------- 1 | use crate::{*, verbose::VerboseIter}; 2 | 3 | /// Typed payload of a DLT log message based on the message info in the DLT 4 | /// extended header. 5 | #[derive(Debug, Clone, Eq, PartialEq, Hash)] 6 | pub enum DltTypedPayload<'a> { 7 | /// A non verbose message of unknown type. 8 | /// 9 | /// This type is used if the DLT message did not contain an extended 10 | /// header and the type of message can only be determined via the 11 | /// message id. In this the type of th message can only be determined 12 | /// based on the message id and an additional information source describing 13 | /// how to decode the message payloads and what type of message it is 14 | /// (e.g. a Fibex file). 15 | UnknownNv(NvPayload<'a>), 16 | 17 | /// Non verbose log message (does not contain a description of it's contents). 18 | /// 19 | /// Non verbose log messages cannot be decoded without additional 20 | /// additional informations how the message payloads can be decoded 21 | /// (e.g. a Fibex file). 22 | LogNv(LogNvPayload<'a>), 23 | 24 | /// Verbose log message (contain a description of it's contents). 25 | /// 26 | /// Verbose log messages can be decoded without additional 27 | /// informations. 28 | LogV(LogVPayload<'a>), 29 | 30 | /// Non verbose trace message. 31 | TraceNv(TraceNvPayload<'a>), 32 | 33 | /// Verbose trace message. 34 | TraceV(TraceVPayload<'a>), 35 | 36 | /// Non verbose network message. 37 | NetworkNv(NetworkNvPayload<'a>), 38 | 39 | /// Verbose network message. 40 | NetworkV(NetworkVPayload<'a>), 41 | 42 | /// Non verbose control message. 43 | ControlNv(ControlNvPayload<'a>), 44 | 45 | /// Verbose control message. 46 | ControlV(ControlVPayload<'a>), 47 | } 48 | 49 | /// Non verbose log message (does not contain a description of it's contents). 50 | /// 51 | /// Non verbose log messages cannot be decoded without additional 52 | /// additional informations how the message payloads can be decoded 53 | /// (e.g. a Fibex file). 54 | #[derive(Debug, Clone, Eq, PartialEq, Hash)] 55 | pub struct LogNvPayload<'a> { 56 | pub log_level: DltLogLevel, 57 | pub msg_id: u32, 58 | pub payload: &'a [u8], 59 | } 60 | 61 | /// Verbose log message (contain a description of it's contents). 62 | /// 63 | /// Verbose log messages can be decoded without additional 64 | /// informations. 65 | #[derive(Debug, Clone, Eq, PartialEq, Hash)] 66 | pub struct LogVPayload<'a> { 67 | pub log_level: DltLogLevel, 68 | pub iter: VerboseIter<'a>, 69 | } 70 | 71 | /// Non verbose trace message. 72 | #[derive(Debug, Clone, Eq, PartialEq, Hash)] 73 | pub struct TraceNvPayload<'a> { 74 | pub trace_type: DltTraceType, 75 | pub msg_id: u32, 76 | pub payload: &'a [u8], 77 | } 78 | 79 | /// Verbose trace message. 80 | #[derive(Debug, Clone, Eq, PartialEq, Hash)] 81 | pub struct TraceVPayload<'a> { 82 | pub trace_type: DltTraceType, 83 | pub iter: VerboseIter<'a>, 84 | } 85 | 86 | /// Non verbose network message. 87 | #[derive(Debug, Clone, Eq, PartialEq, Hash)] 88 | pub struct NetworkNvPayload<'a> { 89 | pub net_type: DltNetworkType, 90 | pub msg_id: u32, 91 | pub payload: &'a [u8], 92 | } 93 | 94 | /// Verbose network message. 95 | #[derive(Debug, Clone, Eq, PartialEq, Hash)] 96 | pub struct NetworkVPayload<'a> { 97 | pub net_type: DltNetworkType, 98 | pub iter: VerboseIter<'a>, 99 | } 100 | 101 | /// Non verbose control message. 102 | #[derive(Debug, Clone, Eq, PartialEq, Hash)] 103 | pub struct ControlNvPayload<'a> { 104 | pub msg_type: DltControlMessageType, 105 | pub service_id: u32, 106 | pub payload: &'a [u8], 107 | } 108 | 109 | /// Verbose control message. 110 | #[derive(Debug, Clone, Eq, PartialEq, Hash)] 111 | pub struct ControlVPayload<'a> { 112 | pub msg_type: DltControlMessageType, 113 | pub iter: VerboseIter<'a>, 114 | } 115 | -------------------------------------------------------------------------------- /src/error/dlt_message_length_too_small_error.rs: -------------------------------------------------------------------------------- 1 | /// Error if the length field in a DLT headeris smaller then the header the calculated 2 | /// header size based on the flags (+ minimum payload size of 4 bytes/octetets) 3 | #[derive(Clone, Debug, PartialEq, Eq)] 4 | pub struct DltMessageLengthTooSmallError { 5 | pub required_length: usize, 6 | pub actual_length: usize, 7 | } 8 | 9 | impl core::fmt::Display for DltMessageLengthTooSmallError { 10 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 11 | write!( 12 | f, 13 | "DLT Header Error: The message length of {} present in the dlt header is smaller then minimum required size of {} bytes.", 14 | self.actual_length, 15 | self.required_length 16 | ) 17 | } 18 | } 19 | 20 | #[cfg(feature = "std")] 21 | impl std::error::Error for DltMessageLengthTooSmallError { 22 | fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { 23 | None 24 | } 25 | } 26 | 27 | #[cfg(test)] 28 | mod dlt_message_length_too_small_error_test { 29 | use super::*; 30 | use alloc::format; 31 | 32 | #[test] 33 | fn clone_eq() { 34 | let v = DltMessageLengthTooSmallError { 35 | required_length: 1, 36 | actual_length: 2, 37 | }; 38 | assert_eq!(v, v.clone()); 39 | } 40 | 41 | #[test] 42 | fn debug() { 43 | let v = DltMessageLengthTooSmallError { 44 | required_length: 1, 45 | actual_length: 2, 46 | }; 47 | assert_eq!( 48 | format!( 49 | "DltMessageLengthTooSmallError {{ required_length: {}, actual_length: {} }}", 50 | v.required_length, v.actual_length, 51 | ), 52 | format!("{:?}", v) 53 | ); 54 | } 55 | 56 | #[test] 57 | fn display() { 58 | let v = DltMessageLengthTooSmallError { 59 | required_length: 1, 60 | actual_length: 2, 61 | }; 62 | assert_eq!( 63 | format!( 64 | "DLT Header Error: The message length of {} present in the dlt header is smaller then minimum required size of {} bytes.", 65 | v.actual_length, 66 | v.required_length, 67 | ), 68 | format!("{}", v) 69 | ); 70 | } 71 | 72 | #[cfg(feature = "std")] 73 | #[test] 74 | fn source() { 75 | use std::error::Error; 76 | assert!(DltMessageLengthTooSmallError { 77 | required_length: 1, 78 | actual_length: 2, 79 | } 80 | .source() 81 | .is_none()); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/error/layer.rs: -------------------------------------------------------------------------------- 1 | /// Error in which an error occured. 2 | #[derive(Clone, Debug, PartialEq, Eq)] 3 | pub enum Layer { 4 | /// Error occured while parsing or writing the DLT header. 5 | DltHeader, 6 | /// Error occured while parsing or writing a verbose type info. 7 | VerboseTypeInfo, 8 | /// Error occured while parsing or writing a verbose value. 9 | VerboseValue, 10 | } 11 | 12 | #[cfg(test)] 13 | mod tests { 14 | use super::*; 15 | use alloc::format; 16 | 17 | #[test] 18 | fn clone_eq() { 19 | use Layer::*; 20 | assert_eq!(VerboseTypeInfo, VerboseTypeInfo.clone()); 21 | } 22 | 23 | #[test] 24 | fn debug() { 25 | use Layer::*; 26 | assert_eq!("VerboseTypeInfo", format!("{:?}", VerboseTypeInfo)); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/error/mod.rs: -------------------------------------------------------------------------------- 1 | mod dlt_message_length_too_small_error; 2 | pub use dlt_message_length_too_small_error::*; 3 | 4 | mod layer; 5 | pub use layer::*; 6 | 7 | mod packet_slice_error; 8 | pub use packet_slice_error::*; 9 | 10 | mod range_error; 11 | pub use range_error::*; 12 | 13 | mod read_error; 14 | pub use read_error::*; 15 | 16 | mod storage_header_start_pattern_error; 17 | pub use storage_header_start_pattern_error::*; 18 | 19 | mod typed_payload_error; 20 | pub use typed_payload_error::*; 21 | 22 | mod unexpected_end_of_slice_error; 23 | pub use unexpected_end_of_slice_error::*; 24 | 25 | mod unsupported_dlt_version_error; 26 | pub use unsupported_dlt_version_error::*; 27 | 28 | mod verbose_decode_error; 29 | pub use verbose_decode_error::*; 30 | -------------------------------------------------------------------------------- /src/error/packet_slice_error.rs: -------------------------------------------------------------------------------- 1 | pub use super::*; 2 | 3 | /// Errors that can occur when slicing a DLT packet. 4 | #[derive(Clone, Debug, PartialEq, Eq)] 5 | pub enum PacketSliceError { 6 | /// An unsupporetd version number has been encountered 7 | /// while decoding the header. 8 | UnsupportedDltVersion(UnsupportedDltVersionError), 9 | 10 | /// Error if the dlt length is smaller then the header the calculated 11 | /// header size based on the flags (+ minimum payload size of 4 bytes/octetets) 12 | MessageLengthTooSmall(DltMessageLengthTooSmallError), 13 | 14 | /// Error if a slice did not contain enough data to decode a value. 15 | UnexpectedEndOfSlice(UnexpectedEndOfSliceError), 16 | } 17 | 18 | impl core::fmt::Display for PacketSliceError { 19 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 20 | use PacketSliceError::*; 21 | match self { 22 | UnsupportedDltVersion(v) => v.fmt(f), 23 | MessageLengthTooSmall(v) => v.fmt(f), 24 | UnexpectedEndOfSlice(v) => v.fmt(f), 25 | } 26 | } 27 | } 28 | 29 | #[cfg(feature = "std")] 30 | impl std::error::Error for PacketSliceError { 31 | fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { 32 | use PacketSliceError::*; 33 | match self { 34 | UnsupportedDltVersion(v) => Some(v), 35 | MessageLengthTooSmall(v) => Some(v), 36 | UnexpectedEndOfSlice(v) => Some(v), 37 | } 38 | } 39 | } 40 | 41 | #[cfg(test)] 42 | mod tests { 43 | use super::*; 44 | use alloc::format; 45 | 46 | #[test] 47 | fn clone_eq() { 48 | use PacketSliceError::*; 49 | let v = UnsupportedDltVersion(UnsupportedDltVersionError { 50 | unsupported_version: 123, 51 | }); 52 | assert_eq!(v, v.clone()); 53 | } 54 | 55 | #[test] 56 | fn debug() { 57 | use PacketSliceError::*; 58 | let inner = UnsupportedDltVersionError { 59 | unsupported_version: 123, 60 | }; 61 | assert_eq!( 62 | format!("UnsupportedDltVersion({:?})", inner), 63 | format!("{:?}", UnsupportedDltVersion(inner.clone())), 64 | ); 65 | } 66 | 67 | #[test] 68 | fn display() { 69 | use PacketSliceError::*; 70 | { 71 | let inner = UnsupportedDltVersionError { 72 | unsupported_version: 123, 73 | }; 74 | assert_eq!( 75 | format!("{}", inner), 76 | format!("{}", UnsupportedDltVersion(inner.clone())), 77 | ); 78 | } 79 | { 80 | let inner = DltMessageLengthTooSmallError { 81 | actual_length: 1, 82 | required_length: 2, 83 | }; 84 | assert_eq!( 85 | format!("{}", inner), 86 | format!("{}", MessageLengthTooSmall(inner.clone())), 87 | ); 88 | } 89 | { 90 | let inner = UnexpectedEndOfSliceError { 91 | actual_size: 1, 92 | layer: Layer::DltHeader, 93 | minimum_size: 3, 94 | }; 95 | assert_eq!( 96 | format!("{}", inner), 97 | format!("{}", UnexpectedEndOfSlice(inner.clone())), 98 | ); 99 | } 100 | } 101 | 102 | #[cfg(feature = "std")] 103 | #[test] 104 | fn source() { 105 | use std::error::Error; 106 | use PacketSliceError::*; 107 | assert!(UnsupportedDltVersion(UnsupportedDltVersionError { 108 | unsupported_version: 123, 109 | }) 110 | .source() 111 | .is_some()); 112 | assert!(MessageLengthTooSmall(DltMessageLengthTooSmallError { 113 | actual_length: 1, 114 | required_length: 2, 115 | }) 116 | .source() 117 | .is_some()); 118 | assert!(UnexpectedEndOfSlice(UnexpectedEndOfSliceError { 119 | actual_size: 1, 120 | layer: Layer::DltHeader, 121 | minimum_size: 3, 122 | }) 123 | .source() 124 | .is_some()); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/error/range_error.rs: -------------------------------------------------------------------------------- 1 | /// Error that can occur when an out of range value is passed to a function. 2 | #[derive(Clone, Debug, PartialEq, Eq)] 3 | pub enum RangeError { 4 | /// Error if the user defined value is outside the range of 7-15 5 | NetworkTypekUserDefinedOutsideOfRange(u8), 6 | } 7 | 8 | #[cfg(feature = "std")] 9 | impl std::error::Error for RangeError { 10 | fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { 11 | None 12 | } 13 | } 14 | 15 | impl core::fmt::Display for RangeError { 16 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 17 | use RangeError::*; 18 | 19 | match self { 20 | NetworkTypekUserDefinedOutsideOfRange(value) => { 21 | write!(f, "RangeError: Message type info field user defined value of {} outside of the allowed range of 7-15.", value) 22 | } 23 | } 24 | } 25 | } 26 | 27 | #[cfg(test)] 28 | mod tests { 29 | use super::*; 30 | use alloc::format; 31 | use proptest::prelude::*; 32 | 33 | #[test] 34 | fn clone_eq() { 35 | use RangeError::*; 36 | let v = NetworkTypekUserDefinedOutsideOfRange(123); 37 | assert_eq!(v, v.clone()); 38 | } 39 | 40 | #[test] 41 | fn debug() { 42 | use RangeError::*; 43 | let v = NetworkTypekUserDefinedOutsideOfRange(123); 44 | assert_eq!( 45 | "NetworkTypekUserDefinedOutsideOfRange(123)", 46 | format!("{:?}", v) 47 | ); 48 | } 49 | 50 | proptest! { 51 | #[test] 52 | fn display(value in any::()) { 53 | use RangeError::*; 54 | 55 | // NetworkTypekUserDefinedOutsideOfRange 56 | assert_eq!( 57 | &format!("RangeError: Message type info field user defined value of {} outside of the allowed range of 7-15.", value), 58 | &format!("{}", NetworkTypekUserDefinedOutsideOfRange(value)) 59 | ); 60 | } 61 | } 62 | 63 | #[test] 64 | #[cfg(feature = "std")] 65 | fn source() { 66 | use std::error::Error; 67 | use RangeError::*; 68 | 69 | assert!(NetworkTypekUserDefinedOutsideOfRange(123) 70 | .source() 71 | .is_none()); 72 | } 73 | } // mod tests 74 | -------------------------------------------------------------------------------- /src/error/read_error.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | ///Errors that can occure on reading a dlt header. 4 | #[cfg(feature = "std")] 5 | #[derive(Debug)] 6 | pub enum ReadError { 7 | /// Error if the slice is smaller then dlt length field or minimal size. 8 | UnexpectedEndOfSlice(UnexpectedEndOfSliceError), 9 | 10 | /// An unsupporetd version number has been encountered 11 | /// while decoding the header. 12 | UnsupportedDltVersion(UnsupportedDltVersionError), 13 | 14 | /// Error if the dlt length is smaller then the header the calculated header size based on the flags (+ minimum payload size of 4 bytes/octetets) 15 | DltMessageLengthTooSmall(DltMessageLengthTooSmallError), 16 | 17 | /// Error if a storage header does not start with the correct pattern. 18 | StorageHeaderStartPattern(StorageHeaderStartPatternError), 19 | 20 | /// Standard io error. 21 | IoError(std::io::Error), 22 | } 23 | 24 | #[cfg(feature = "std")] 25 | impl std::error::Error for ReadError { 26 | fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { 27 | use ReadError::*; 28 | match self { 29 | UnexpectedEndOfSlice(ref err) => Some(err), 30 | UnsupportedDltVersion(ref err) => Some(err), 31 | DltMessageLengthTooSmall(ref err) => Some(err), 32 | StorageHeaderStartPattern(ref err) => Some(err), 33 | IoError(ref err) => Some(err), 34 | } 35 | } 36 | } 37 | 38 | #[cfg(feature = "std")] 39 | impl core::fmt::Display for ReadError { 40 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 41 | use ReadError::*; 42 | 43 | match self { 44 | UnexpectedEndOfSlice(err) => { 45 | write!(f, "ReadError: Unexpected end of slice. The given slice only contained {} bytes, which is less then minimum required {} bytes.", err.actual_size, err.minimum_size) 46 | } 47 | UnsupportedDltVersion(err) => err.fmt(f), 48 | DltMessageLengthTooSmall(err) => err.fmt(f), 49 | StorageHeaderStartPattern(err) => err.fmt(f), 50 | IoError(err) => err.fmt(f), 51 | } 52 | } 53 | } 54 | 55 | #[cfg(feature = "std")] 56 | impl From for ReadError { 57 | fn from(err: StorageHeaderStartPatternError) -> ReadError { 58 | ReadError::StorageHeaderStartPattern(err) 59 | } 60 | } 61 | 62 | #[cfg(feature = "std")] 63 | impl From for ReadError { 64 | fn from(err: PacketSliceError) -> ReadError { 65 | use PacketSliceError as I; 66 | match err { 67 | I::UnsupportedDltVersion(err) => ReadError::UnsupportedDltVersion(err), 68 | I::MessageLengthTooSmall(err) => ReadError::DltMessageLengthTooSmall(err), 69 | I::UnexpectedEndOfSlice(err) => ReadError::UnexpectedEndOfSlice(err), 70 | } 71 | } 72 | } 73 | 74 | #[cfg(feature = "std")] 75 | impl From for ReadError { 76 | fn from(err: std::io::Error) -> ReadError { 77 | ReadError::IoError(err) 78 | } 79 | } 80 | 81 | /// Tests for `ReadError` methods 82 | #[cfg(all(feature = "std", test))] 83 | mod tests { 84 | use super::*; 85 | use alloc::format; 86 | use proptest::prelude::*; 87 | 88 | #[test] 89 | fn debug() { 90 | use ReadError::*; 91 | 92 | { 93 | let c = UnexpectedEndOfSliceError { 94 | minimum_size: 1, 95 | actual_size: 2, 96 | layer: Layer::DltHeader, 97 | }; 98 | assert_eq!( 99 | format!("UnexpectedEndOfSlice({:?})", c), 100 | format!("{:?}", UnexpectedEndOfSlice(c)) 101 | ); 102 | } 103 | { 104 | let c = UnsupportedDltVersionError { 105 | unsupported_version: 123, 106 | }; 107 | assert_eq!( 108 | format!("UnsupportedDltVersion({:?})", c), 109 | format!("{:?}", UnsupportedDltVersion(c)) 110 | ); 111 | } 112 | { 113 | let c = DltMessageLengthTooSmallError { 114 | required_length: 3, 115 | actual_length: 4, 116 | }; 117 | assert_eq!( 118 | format!("DltMessageLengthTooSmall({:?})", c), 119 | format!("{:?}", DltMessageLengthTooSmall(c)) 120 | ); 121 | } 122 | { 123 | let c = std::io::Error::new(std::io::ErrorKind::Other, "oh no!"); 124 | assert_eq!(format!("IoError({:?})", c), format!("{:?}", IoError(c))); 125 | } 126 | } 127 | 128 | proptest! { 129 | #[test] 130 | fn display( 131 | usize0 in any::(), 132 | usize1 in any::(), 133 | ) { 134 | 135 | use ReadError::*; 136 | 137 | // UnexpectedEndOfSlice 138 | assert_eq!( 139 | &format!("ReadError: Unexpected end of slice. The given slice only contained {} bytes, which is less then minimum required {} bytes.", usize1, usize0), 140 | &format!( 141 | "{}", 142 | UnexpectedEndOfSlice( 143 | UnexpectedEndOfSliceError { 144 | layer: Layer::DltHeader, 145 | minimum_size: usize0, 146 | actual_size: usize1, 147 | } 148 | ) 149 | ) 150 | ); 151 | 152 | // UnsupportedDltVersionError 153 | { 154 | let c = UnsupportedDltVersionError{ unsupported_version: 123 }; 155 | assert_eq!( 156 | &format!("{}", c), 157 | &format!("{}", UnsupportedDltVersion(c)) 158 | ); 159 | } 160 | 161 | // DltMessageLengthTooSmall 162 | { 163 | let c = DltMessageLengthTooSmallError{ 164 | required_length: usize0, 165 | actual_length: usize1 166 | }; 167 | assert_eq!( 168 | &format!("{}", c), 169 | &format!("{}", DltMessageLengthTooSmall(c)) 170 | ); 171 | } 172 | 173 | // StorageHeaderStartPattern 174 | { 175 | let c = StorageHeaderStartPatternError{ 176 | actual_pattern: [1,2,3,4] 177 | }; 178 | assert_eq!( 179 | &format!("{}", c), 180 | &format!("{}", StorageHeaderStartPattern(c)) 181 | ); 182 | } 183 | 184 | //IoError 185 | { 186 | let custom_error = std::io::Error::new(std::io::ErrorKind::Other, "some error"); 187 | assert_eq!( 188 | &format!("{}", custom_error), 189 | &format!("{}", IoError(custom_error)) 190 | ); 191 | } 192 | } 193 | } 194 | 195 | #[test] 196 | fn source() { 197 | use std::error::Error; 198 | use ReadError::*; 199 | 200 | assert!(UnexpectedEndOfSlice(UnexpectedEndOfSliceError { 201 | layer: Layer::DltHeader, 202 | minimum_size: 1, 203 | actual_size: 2 204 | }) 205 | .source() 206 | .is_some()); 207 | assert!(UnsupportedDltVersion(UnsupportedDltVersionError { 208 | unsupported_version: 123 209 | }) 210 | .source() 211 | .is_some()); 212 | assert!(DltMessageLengthTooSmall(DltMessageLengthTooSmallError { 213 | required_length: 3, 214 | actual_length: 4 215 | }) 216 | .source() 217 | .is_some()); 218 | assert!(StorageHeaderStartPattern(StorageHeaderStartPatternError { 219 | actual_pattern: [1, 2, 3, 4] 220 | }) 221 | .source() 222 | .is_some()); 223 | assert!( 224 | IoError(std::io::Error::new(std::io::ErrorKind::Other, "oh no!")) 225 | .source() 226 | .is_some() 227 | ); 228 | } 229 | 230 | #[test] 231 | fn from_io_error() { 232 | let r: ReadError = std::io::Error::new(std::io::ErrorKind::Other, "oh no!").into(); 233 | assert_matches!(r, ReadError::IoError(_)); 234 | } 235 | 236 | #[test] 237 | fn from_storage_header_error() { 238 | let r: ReadError = StorageHeaderStartPatternError { 239 | actual_pattern: [1, 2, 3, 4], 240 | } 241 | .into(); 242 | assert_matches!(r, ReadError::StorageHeaderStartPattern(_)); 243 | } 244 | 245 | #[test] 246 | fn from_packet_slice_error() { 247 | use PacketSliceError as I; 248 | 249 | // UnsupportedDltVersion 250 | { 251 | let r: ReadError = I::UnsupportedDltVersion(UnsupportedDltVersionError { 252 | unsupported_version: 123, 253 | }) 254 | .into(); 255 | assert_matches!(r, ReadError::UnsupportedDltVersion(_)); 256 | } 257 | 258 | // MessageLengthTooSmall 259 | { 260 | let r: ReadError = I::MessageLengthTooSmall(DltMessageLengthTooSmallError { 261 | required_length: 3, 262 | actual_length: 4, 263 | }) 264 | .into(); 265 | assert_matches!(r, ReadError::DltMessageLengthTooSmall(_)); 266 | } 267 | 268 | // UnexpectedEndOfSlice 269 | { 270 | let r: ReadError = I::UnexpectedEndOfSlice(UnexpectedEndOfSliceError { 271 | layer: Layer::DltHeader, 272 | minimum_size: 1, 273 | actual_size: 2, 274 | }) 275 | .into(); 276 | assert_matches!(r, ReadError::UnexpectedEndOfSlice(_)); 277 | } 278 | } 279 | } // mod tests 280 | -------------------------------------------------------------------------------- /src/error/storage_header_start_pattern_error.rs: -------------------------------------------------------------------------------- 1 | /// Error that occurs when another pattern then 2 | /// [`crate::storage::StorageHeader::PATTERN_AT_START`] is encountered 3 | /// at the start when parsing a StorageHeader. 4 | #[derive(Clone, Debug, PartialEq, Eq)] 5 | pub struct StorageHeaderStartPatternError { 6 | /// Encountered pattern at the start. 7 | pub actual_pattern: [u8; 4], 8 | } 9 | 10 | impl core::fmt::Display for StorageHeaderStartPatternError { 11 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 12 | write!( 13 | f, 14 | "Error when parsing DLT storage header. Expected pattern {:?} at start but got {:?}", 15 | crate::storage::StorageHeader::PATTERN_AT_START, 16 | self.actual_pattern 17 | ) 18 | } 19 | } 20 | 21 | #[cfg(feature = "std")] 22 | impl std::error::Error for StorageHeaderStartPatternError { 23 | fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { 24 | None 25 | } 26 | } 27 | 28 | #[cfg(test)] 29 | mod tests { 30 | use super::*; 31 | use alloc::format; 32 | 33 | #[test] 34 | fn clone_eq() { 35 | let v = StorageHeaderStartPatternError { 36 | actual_pattern: [1, 2, 3, 4], 37 | }; 38 | assert_eq!(v, v.clone()); 39 | } 40 | 41 | #[test] 42 | fn debug() { 43 | let v = StorageHeaderStartPatternError { 44 | actual_pattern: [1, 2, 3, 4], 45 | }; 46 | assert_eq!( 47 | format!( 48 | "StorageHeaderStartPatternError {{ actual_pattern: {:?} }}", 49 | v.actual_pattern 50 | ), 51 | format!("{:?}", v) 52 | ); 53 | } 54 | 55 | #[test] 56 | fn display() { 57 | let v = StorageHeaderStartPatternError { 58 | actual_pattern: [1, 2, 3, 4], 59 | }; 60 | assert_eq!( 61 | format!( 62 | "Error when parsing DLT storage header. Expected pattern {:?} at start but got {:?}", 63 | crate::storage::StorageHeader::PATTERN_AT_START, 64 | v.actual_pattern 65 | ), 66 | format!("{}", v) 67 | ); 68 | } 69 | 70 | #[cfg(feature = "std")] 71 | #[test] 72 | fn source() { 73 | use std::error::Error; 74 | assert!(StorageHeaderStartPatternError { 75 | actual_pattern: [1, 2, 3, 4] 76 | } 77 | .source() 78 | .is_none()); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/error/typed_payload_error.rs: -------------------------------------------------------------------------------- 1 | use crate::DltMessageInfo; 2 | 3 | /// Error that can occur when trying to get a [`crate::DltTypedPayload`] from 4 | /// a [`crate::DltPacketSlice`]. 5 | #[derive(Clone, Debug, PartialEq, Eq)] 6 | pub enum TypedPayloadError { 7 | /// Error if the length of the message is smaller than a message id. 8 | LenSmallerThanMessageId { 9 | packet_len: usize, 10 | header_len: usize, 11 | }, 12 | 13 | /// Error if the message info 14 | UnknownMessageInfo(DltMessageInfo), 15 | } 16 | 17 | #[cfg(feature = "std")] 18 | impl std::error::Error for TypedPayloadError { 19 | fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { 20 | None 21 | } 22 | } 23 | 24 | impl core::fmt::Display for TypedPayloadError { 25 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 26 | use TypedPayloadError::*; 27 | match self { 28 | LenSmallerThanMessageId { 29 | packet_len, 30 | header_len, 31 | } => { 32 | write!(f, "DLT non verbose message too small for the message id (4 bytes required, only {packet_len} bytes present and {header_len}) bytes taken by the header.") 33 | } 34 | UnknownMessageInfo(message_info) => { 35 | write!( 36 | f, 37 | "DLT message info contains the value '{}' that is unknown", 38 | message_info.0 39 | ) 40 | } 41 | } 42 | } 43 | } 44 | 45 | #[cfg(test)] 46 | mod tests { 47 | use super::*; 48 | use alloc::format; 49 | use proptest::prelude::*; 50 | 51 | #[test] 52 | fn clone_eq() { 53 | use TypedPayloadError::*; 54 | let v = LenSmallerThanMessageId { 55 | packet_len: 123, 56 | header_len: 234, 57 | }; 58 | assert_eq!(v, v.clone()); 59 | } 60 | 61 | #[test] 62 | fn debug() { 63 | use TypedPayloadError::*; 64 | let v = LenSmallerThanMessageId { 65 | packet_len: 123, 66 | header_len: 234, 67 | }; 68 | assert_eq!( 69 | "LenSmallerThanMessageId { packet_len: 123, header_len: 234 }", 70 | format!("{:?}", v) 71 | ); 72 | } 73 | 74 | proptest! { 75 | #[test] 76 | fn display(value0_usize in any::(), value1_usize in any::(), value_u8 in any::()) { 77 | use TypedPayloadError::*; 78 | 79 | // LenSmallerThanMessageId 80 | assert_eq!( 81 | &format!("DLT non verbose message too small for the message id (4 bytes required, only {} bytes present and {}) bytes taken by the header.", value0_usize, value1_usize), 82 | &format!("{}", LenSmallerThanMessageId { packet_len: value0_usize, header_len: value1_usize }) 83 | ); 84 | 85 | // UnknownMessageInfo 86 | assert_eq!( 87 | &format!("DLT message info contains the value '{}' that is unknown", value_u8), 88 | &format!("{}", UnknownMessageInfo(DltMessageInfo(value_u8))) 89 | ); 90 | } 91 | } 92 | 93 | #[test] 94 | #[cfg(feature = "std")] 95 | fn source() { 96 | use std::error::Error; 97 | use TypedPayloadError::*; 98 | 99 | assert!(LenSmallerThanMessageId { 100 | packet_len: 123, 101 | header_len: 234 102 | } 103 | .source() 104 | .is_none()); 105 | } 106 | } // mod tests 107 | -------------------------------------------------------------------------------- /src/error/unexpected_end_of_slice_error.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | /// Error if a slice did not contain enough data to decode a value. 4 | #[derive(Clone, Debug, PartialEq, Eq)] 5 | pub struct UnexpectedEndOfSliceError { 6 | /// Layer in which the length error was detected. 7 | pub layer: Layer, 8 | 9 | /// Minimum expected slice length. 10 | pub minimum_size: usize, 11 | 12 | /// Actual slice length (which was too small). 13 | pub actual_size: usize, 14 | } 15 | 16 | impl core::fmt::Display for UnexpectedEndOfSliceError { 17 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 18 | write!( 19 | f, 20 | "{:?}: Unexpected end of slice. The given slice only contained {} bytes, which is less then minimum required {} bytes.", 21 | self.layer, 22 | self.actual_size, 23 | self.minimum_size 24 | ) 25 | } 26 | } 27 | 28 | #[cfg(feature = "std")] 29 | impl std::error::Error for UnexpectedEndOfSliceError { 30 | fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { 31 | None 32 | } 33 | } 34 | 35 | #[cfg(test)] 36 | mod tests { 37 | use super::*; 38 | use alloc::format; 39 | 40 | #[test] 41 | fn clone_eq() { 42 | let v = UnexpectedEndOfSliceError { 43 | layer: Layer::DltHeader, 44 | minimum_size: 2, 45 | actual_size: 3, 46 | }; 47 | assert_eq!(v, v.clone()); 48 | } 49 | 50 | #[test] 51 | fn debug() { 52 | let v = UnexpectedEndOfSliceError { 53 | layer: Layer::DltHeader, 54 | minimum_size: 2, 55 | actual_size: 3, 56 | }; 57 | assert_eq!( 58 | format!( 59 | "UnexpectedEndOfSliceError {{ layer: {:?}, minimum_size: {}, actual_size: {} }}", 60 | v.layer, v.minimum_size, v.actual_size 61 | ), 62 | format!("{:?}", v) 63 | ); 64 | } 65 | 66 | #[test] 67 | fn display() { 68 | let v = UnexpectedEndOfSliceError { 69 | layer: Layer::DltHeader, 70 | minimum_size: 2, 71 | actual_size: 3, 72 | }; 73 | assert_eq!( 74 | format!( 75 | "{:?}: Unexpected end of slice. The given slice only contained {} bytes, which is less then minimum required {} bytes.", 76 | v.layer, 77 | v.actual_size, 78 | v.minimum_size, 79 | ), 80 | format!("{}", v) 81 | ); 82 | } 83 | 84 | #[cfg(feature = "std")] 85 | #[test] 86 | fn source() { 87 | use std::error::Error; 88 | assert!(UnexpectedEndOfSliceError { 89 | layer: Layer::DltHeader, 90 | minimum_size: 2, 91 | actual_size: 3, 92 | } 93 | .source() 94 | .is_none()); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/error/unsupported_dlt_version_error.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | /// Error that is triggered when an unsupported DLT version is 4 | /// encountred when parsing. 5 | #[derive(Clone, Debug, PartialEq, Eq)] 6 | pub struct UnsupportedDltVersionError { 7 | /// Unsupported version number that was encountered in the DLT header. 8 | pub unsupported_version: u8, 9 | } 10 | 11 | impl core::fmt::Display for UnsupportedDltVersionError { 12 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 13 | write!( 14 | f, 15 | "Encountered unsupported DLT version '{}' in header. Only versions {:?} are supported.", 16 | self.unsupported_version, 17 | DltHeader::SUPPORTED_DECODABLE_VERSIONS 18 | ) 19 | } 20 | } 21 | 22 | #[cfg(feature = "std")] 23 | impl std::error::Error for UnsupportedDltVersionError { 24 | fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { 25 | None 26 | } 27 | } 28 | 29 | #[cfg(test)] 30 | mod unsupported_dlt_version_error_test { 31 | use super::*; 32 | 33 | #[test] 34 | fn clone_eq() { 35 | let v = UnsupportedDltVersionError { 36 | unsupported_version: 123, 37 | }; 38 | assert_eq!(v, v.clone()); 39 | } 40 | 41 | #[test] 42 | fn debug() { 43 | let v = UnsupportedDltVersionError { 44 | unsupported_version: 123, 45 | }; 46 | assert_eq!( 47 | format!( 48 | "Encountered unsupported DLT version '{}' in header. Only versions {:?} are supported.", 49 | v.unsupported_version, 50 | crate::DltHeader::SUPPORTED_DECODABLE_VERSIONS 51 | ), 52 | format!("{}", v) 53 | ); 54 | } 55 | 56 | #[test] 57 | fn display() { 58 | let v = UnsupportedDltVersionError { 59 | unsupported_version: 123, 60 | }; 61 | assert_eq!( 62 | format!( 63 | "UnsupportedDltVersionError {{ unsupported_version: {} }}", 64 | v.unsupported_version 65 | ), 66 | format!("{:?}", v) 67 | ); 68 | } 69 | 70 | #[cfg(feature = "std")] 71 | #[test] 72 | fn source() { 73 | use std::error::Error; 74 | assert!(UnsupportedDltVersionError { 75 | unsupported_version: 123, 76 | } 77 | .source() 78 | .is_none()); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/error/verbose_decode_error.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use core::str::Utf8Error; 3 | 4 | #[derive(Clone, Debug, PartialEq, Eq)] 5 | pub enum VerboseDecodeError { 6 | /// Error that occurs if a type info is inconsistent. 7 | /// 8 | /// An example is when the type is described as both a 9 | /// bool and float or otherwise contains flags that contradict 10 | /// each other. 11 | /// 12 | /// The encoded type info is given as an argument. 13 | InvalidTypeInfo([u8; 4]), 14 | 15 | /// Error in case an invalid bool value is encountered (not 0 or 1). 16 | InvalidBoolValue(u8), 17 | 18 | /// Error if not enough data was present in the slice to decode 19 | /// a verbose value. 20 | UnexpectedEndOfSlice(UnexpectedEndOfSliceError), 21 | 22 | /// Error if a variable name string is not zero terminated. 23 | VariableNameStringMissingNullTermination, 24 | 25 | /// Error if a variable unit string is not zero terminated. 26 | VariableUnitStringMissingNullTermination, 27 | 28 | /// Error if the total len calculated from the array dimensions overflows. 29 | ArrayDimensionsOverflow, 30 | 31 | StructDataLengthOverflow, 32 | 33 | /// Error when decoding an string (can also occur for variable names or unit names). 34 | Utf8(Utf8Error), 35 | } 36 | 37 | impl core::fmt::Display for VerboseDecodeError { 38 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 39 | use VerboseDecodeError::*; 40 | match self { 41 | InvalidTypeInfo(value) => write!( 42 | f, "DLT Verbose Message Field: Encountered an invalid typeinfo {:?} (contradicting or unknown)", value 43 | ), 44 | InvalidBoolValue(value) => write!( 45 | f, "DLT Verbose Message Field: Encountered invalid bool value '{}' (only 0 or 1 are valid)", value 46 | ), 47 | UnexpectedEndOfSlice(err) => err.fmt(f), 48 | VariableNameStringMissingNullTermination => write!( 49 | f, "DLT Verbose Message Field: Encountered a variable name string missing the terminating zero value" 50 | ), 51 | VariableUnitStringMissingNullTermination => write!( 52 | f, "DLT Verbose Message Field: Encountered a variable unit string missing the terminating zero value" 53 | ), 54 | Utf8(err) => err.fmt(f), 55 | ArrayDimensionsOverflow => write!(f, "DLT Verbose Message Field: Array dimension sizes too big. Calculating the overall array size would cause an integer overflow."), 56 | StructDataLengthOverflow => write!(f, "DLT Verbose Message Field: Struct data length too big. Would cause an integer overflow."), 57 | } 58 | } 59 | } 60 | 61 | #[cfg(feature = "std")] 62 | impl std::error::Error for VerboseDecodeError { 63 | fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { 64 | use VerboseDecodeError::*; 65 | match self { 66 | InvalidTypeInfo(_) => None, 67 | InvalidBoolValue(_) => None, 68 | UnexpectedEndOfSlice(err) => Some(err), 69 | VariableNameStringMissingNullTermination => None, 70 | VariableUnitStringMissingNullTermination => None, 71 | Utf8(err) => Some(err), 72 | ArrayDimensionsOverflow => None, 73 | StructDataLengthOverflow => None, 74 | } 75 | } 76 | } 77 | 78 | impl From for VerboseDecodeError { 79 | fn from(err: Utf8Error) -> VerboseDecodeError { 80 | VerboseDecodeError::Utf8(err) 81 | } 82 | } 83 | 84 | #[cfg(test)] 85 | mod tests { 86 | use super::*; 87 | use alloc::format; 88 | 89 | #[test] 90 | fn clone_eq() { 91 | use VerboseDecodeError::*; 92 | let v = InvalidBoolValue(2); 93 | assert_eq!(v, v.clone()); 94 | } 95 | 96 | #[test] 97 | fn debug() { 98 | use VerboseDecodeError::*; 99 | let v = InvalidBoolValue(2); 100 | assert_eq!(format!("InvalidBoolValue({})", 2), format!("{:?}", v)); 101 | } 102 | 103 | #[test] 104 | fn display() { 105 | use VerboseDecodeError::*; 106 | 107 | assert_eq!( 108 | format!("DLT Verbose Message Field: Encountered an invalid typeinfo {:?} (contradicting or unknown)", [1,2,3,4]), 109 | format!("{}", InvalidTypeInfo([1,2,3,4])) 110 | ); 111 | 112 | assert_eq!( 113 | format!("DLT Verbose Message Field: Encountered invalid bool value '{}' (only 0 or 1 are valid)", 2), 114 | format!("{}", InvalidBoolValue(2)) 115 | ); 116 | 117 | { 118 | let v = UnexpectedEndOfSliceError { 119 | layer: Layer::DltHeader, 120 | actual_size: 1, 121 | minimum_size: 2, 122 | }; 123 | assert_eq!(format!("{}", v), format!("{}", UnexpectedEndOfSlice(v))); 124 | } 125 | 126 | assert_eq!( 127 | format!("DLT Verbose Message Field: Encountered a variable name string missing the terminating zero value"), 128 | format!("{}", VariableNameStringMissingNullTermination) 129 | ); 130 | 131 | assert_eq!( 132 | format!("DLT Verbose Message Field: Encountered a variable unit string missing the terminating zero value"), 133 | format!("{}", VariableUnitStringMissingNullTermination) 134 | ); 135 | 136 | #[allow(invalid_from_utf8)] 137 | { 138 | let v = std::str::from_utf8(&[0, 159, 146, 150]).unwrap_err(); 139 | assert_eq!(format!("{}", v), format!("{}", Utf8(v))); 140 | } 141 | } 142 | 143 | #[cfg(feature = "std")] 144 | #[test] 145 | #[allow(invalid_from_utf8)] 146 | fn source() { 147 | use std::error::Error; 148 | use VerboseDecodeError::*; 149 | assert!(InvalidTypeInfo([1, 2, 3, 4]).source().is_none()); 150 | assert!(InvalidBoolValue(2).source().is_none()); 151 | assert!(UnexpectedEndOfSlice(UnexpectedEndOfSliceError { 152 | layer: Layer::DltHeader, 153 | actual_size: 1, 154 | minimum_size: 2, 155 | }) 156 | .source() 157 | .is_some()); 158 | assert!(VariableNameStringMissingNullTermination.source().is_none()); 159 | assert!(VariableUnitStringMissingNullTermination.source().is_none()); 160 | assert!(Utf8(std::str::from_utf8(&[0, 159, 146, 150]).unwrap_err()) 161 | .source() 162 | .is_some()); 163 | } 164 | 165 | #[test] 166 | #[allow(invalid_from_utf8)] 167 | fn from_utf8_error() { 168 | let e: VerboseDecodeError = std::str::from_utf8(&[0, 159, 146, 150]).unwrap_err().into(); 169 | assert_matches!(e, VerboseDecodeError::Utf8(_)); 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /src/nv_payload.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | /// A non verbose message of unknown type. 4 | /// 5 | /// This type is used if the DLT message did not contain an extended 6 | /// header and the type of message can only be determined via the 7 | /// message id. In this the type of th message can only be determined 8 | /// based on the message id and an additional information source describing 9 | /// how to decode the message payloads and what type of message it is 10 | /// (e.g. a Fibex file). 11 | #[derive(Debug, Clone, Eq, PartialEq, Hash)] 12 | pub struct NvPayload<'a> { 13 | pub msg_id: u32, 14 | pub payload: &'a [u8], 15 | } 16 | 17 | impl<'a> From> for NvPayload<'a> { 18 | fn from(value: LogNvPayload<'a>) -> Self { 19 | NvPayload { 20 | msg_id: value.msg_id, 21 | payload: value.payload, 22 | } 23 | } 24 | } 25 | 26 | impl<'a> From> for NvPayload<'a> { 27 | fn from(value: TraceNvPayload<'a>) -> Self { 28 | NvPayload { 29 | msg_id: value.msg_id, 30 | payload: value.payload, 31 | } 32 | } 33 | } 34 | 35 | impl<'a> From> for NvPayload<'a> { 36 | fn from(value: NetworkNvPayload<'a>) -> Self { 37 | NvPayload { 38 | msg_id: value.msg_id, 39 | payload: value.payload, 40 | } 41 | } 42 | } 43 | 44 | #[cfg(test)] 45 | mod tests { 46 | use super::*; 47 | 48 | #[test] 49 | fn from() { 50 | let data = [5,6,7,8]; 51 | let msg_id = 1234_5678u32; 52 | let payload = &data; 53 | 54 | // LogNvPayload 55 | assert_eq!( 56 | NvPayload::from(LogNvPayload{ msg_id, payload, log_level: DltLogLevel::Info }), 57 | NvPayload{ msg_id, payload } 58 | ); 59 | 60 | // TraceNvPayload 61 | assert_eq!( 62 | NvPayload::from(TraceNvPayload{ msg_id, payload, trace_type: DltTraceType::State }), 63 | NvPayload{ msg_id, payload } 64 | ); 65 | 66 | // TraceNvPayload 67 | assert_eq!( 68 | NvPayload::from(NetworkNvPayload{msg_id,payload, net_type: DltNetworkType::Flexray }), 69 | NvPayload{ msg_id, payload } 70 | ); 71 | } 72 | } -------------------------------------------------------------------------------- /src/proptest_generators/mod.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use proptest::prelude::*; 3 | use proptest::*; 4 | 5 | /// Maximum size of payload when testing 6 | const TEST_MAX_PAYLOAD_SIZE: usize = 1234; 7 | 8 | prop_compose! { 9 | pub fn extended_dlt_header_any()(message_info in any::(), 10 | number_of_arguments in any::(), 11 | application_id in any::<[u8;4]>(), 12 | context_id in any::<[u8;4]>()) -> DltExtendedHeader 13 | { 14 | DltExtendedHeader { 15 | message_info: DltMessageInfo(message_info), 16 | number_of_arguments: number_of_arguments, 17 | application_id: application_id, 18 | context_id: context_id 19 | } 20 | } 21 | } 22 | 23 | prop_compose! { 24 | pub fn dlt_header_with_payload_any()( 25 | payload_length in 0usize..TEST_MAX_PAYLOAD_SIZE 26 | )( 27 | is_big_endian in any::(), 28 | message_counter in any::(), 29 | ecu_id in any::>(), 30 | session_id in any::>(), 31 | timestamp in any::>(), 32 | extended_header in option::of(extended_dlt_header_any()), 33 | payload in proptest::collection::vec(any::(), payload_length) 34 | ) -> (DltHeader, Vec) 35 | { 36 | ( 37 | { 38 | let mut header = DltHeader { 39 | is_big_endian, 40 | message_counter, 41 | length: payload.len() as u16, 42 | ecu_id, 43 | session_id, 44 | timestamp, 45 | extended_header 46 | }; 47 | let header_size = header.header_len(); 48 | header.length = header_size + (payload.len() as u16); 49 | header 50 | }, 51 | payload 52 | ) 53 | } 54 | } 55 | 56 | prop_compose! { 57 | pub fn dlt_header_any()(is_big_endian in any::(), 58 | message_counter in any::(), 59 | length in any::(), 60 | ecu_id in any::>(), 61 | session_id in any::>(), 62 | timestamp in any::>(), 63 | extended_header in option::of(extended_dlt_header_any())) -> DltHeader 64 | { 65 | DltHeader { 66 | is_big_endian, 67 | message_counter, 68 | length, 69 | ecu_id, 70 | session_id, 71 | timestamp, 72 | extended_header 73 | } 74 | } 75 | } 76 | 77 | prop_compose! { 78 | pub fn storage_header_any()( 79 | timestamp_seconds in any::(), 80 | timestamp_microseconds in any::(), 81 | ecu_id in any::<[u8;4]>() 82 | ) -> storage::StorageHeader { 83 | storage::StorageHeader{ 84 | timestamp_seconds, 85 | timestamp_microseconds, 86 | ecu_id 87 | } 88 | } 89 | } 90 | 91 | pub fn log_level_any() -> impl Strategy { 92 | use DltLogLevel::*; 93 | prop_oneof![ 94 | Just(Fatal), 95 | Just(Error), 96 | Just(Warn), 97 | Just(Info), 98 | Just(Debug), 99 | Just(Verbose), 100 | ] 101 | } 102 | 103 | pub fn trace_type_any() -> impl Strategy { 104 | use DltTraceType::*; 105 | prop_oneof![ 106 | Just(Variable), 107 | Just(FunctionIn), 108 | Just(FunctionOut), 109 | Just(State), 110 | Just(Vfb), 111 | ] 112 | } 113 | 114 | pub fn network_type_any() -> impl Strategy { 115 | use DltNetworkType::*; 116 | prop_oneof![ 117 | Just(Ipc), 118 | Just(Can), 119 | Just(Flexray), 120 | Just(Most), 121 | Just(Ethernet), 122 | Just(SomeIp), 123 | Just(UserDefined(0x7)), 124 | Just(UserDefined(0x8)), 125 | Just(UserDefined(0x9)), 126 | Just(UserDefined(0xA)), 127 | Just(UserDefined(0xB)), 128 | Just(UserDefined(0xC)), 129 | Just(UserDefined(0xD)), 130 | Just(UserDefined(0xE)), 131 | Just(UserDefined(0xF)), 132 | ] 133 | } 134 | 135 | pub fn control_message_type_any() -> impl Strategy { 136 | use DltControlMessageType::*; 137 | prop_oneof![Just(Request), Just(Response),] 138 | } 139 | 140 | pub fn message_type_any() -> impl Strategy { 141 | use DltControlMessageType::*; 142 | use DltLogLevel::*; 143 | use DltMessageType::*; 144 | use DltNetworkType::*; 145 | use DltTraceType::*; 146 | prop_oneof![ 147 | Just(Log(Fatal)), 148 | Just(Log(Error)), 149 | Just(Log(Warn)), 150 | Just(Log(Info)), 151 | Just(Log(Debug)), 152 | Just(Log(Verbose)), 153 | Just(Trace(Variable)), 154 | Just(Trace(FunctionIn)), 155 | Just(Trace(FunctionOut)), 156 | Just(Trace(State)), 157 | Just(Trace(Vfb)), 158 | Just(NetworkTrace(Ipc)), 159 | Just(NetworkTrace(Can)), 160 | Just(NetworkTrace(Flexray)), 161 | Just(NetworkTrace(Most)), 162 | Just(NetworkTrace(Ethernet)), 163 | Just(NetworkTrace(SomeIp)), 164 | Just(NetworkTrace(UserDefined(0x7))), 165 | Just(NetworkTrace(UserDefined(0x8))), 166 | Just(NetworkTrace(UserDefined(0x9))), 167 | Just(NetworkTrace(UserDefined(0xA))), 168 | Just(NetworkTrace(UserDefined(0xB))), 169 | Just(NetworkTrace(UserDefined(0xC))), 170 | Just(NetworkTrace(UserDefined(0xD))), 171 | Just(NetworkTrace(UserDefined(0xE))), 172 | Just(NetworkTrace(UserDefined(0xF))), 173 | Just(Control(Request)), 174 | Just(Control(Response)), 175 | ] 176 | } 177 | -------------------------------------------------------------------------------- /src/storage/dlt_storage_writer.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "std")] 2 | use std::io::{Error, Write}; 3 | 4 | use crate::{storage::StorageHeader, DltPacketSlice}; 5 | 6 | /// Allows the writing of dlt storage files. 7 | /// 8 | /// # Example 9 | /// 10 | /// ```no_run 11 | /// use std::{fs::File, io::BufWriter}; 12 | /// use dlt_parse::storage::{DltStorageWriter, StorageHeader}; 13 | /// 14 | /// let dlt_file = File::create("out.dlt").expect("failed to open output file"); 15 | /// let mut dlt_writer = DltStorageWriter::new(BufWriter::new(dlt_file)); 16 | /// 17 | /// // ... 18 | /// # use dlt_parse::{DltHeader, DltPacketSlice}; 19 | /// # use std::io::Write; 20 | /// # let packet0 = { 21 | /// # let mut packet = Vec::::new(); 22 | /// # let mut header = DltHeader{ 23 | /// # is_big_endian: true, 24 | /// # message_counter: 0, 25 | /// # length: 0, 26 | /// # ecu_id: None, 27 | /// # session_id: None, 28 | /// # timestamp: None, 29 | /// # extended_header: None, 30 | /// # }; 31 | /// # header.length = header.header_len() + 4; 32 | /// # header.write(&mut packet).unwrap(); 33 | /// # packet.write_all(&[1,2,3,4]).unwrap(); 34 | /// # packet 35 | /// # }; 36 | /// # let dlt_slice = DltPacketSlice::from_slice(&packet0).unwrap(); 37 | /// # let timestamp_seconds = 0; 38 | /// # let timestamp_microseconds = 0; 39 | /// # let ecu_id = [0u8;4]; 40 | /// 41 | /// // write a dlt message 42 | /// dlt_writer.write_slice( 43 | /// StorageHeader{ 44 | /// timestamp_seconds, 45 | /// timestamp_microseconds, 46 | /// ecu_id 47 | /// }, 48 | /// dlt_slice 49 | /// ).expect("failed to write dlt packet"); 50 | /// ``` 51 | #[cfg(feature = "std")] 52 | #[derive(Debug)] 53 | pub struct DltStorageWriter { 54 | writer: W, 55 | } 56 | 57 | #[cfg(feature = "std")] 58 | impl DltStorageWriter { 59 | /// Creates a new writer that allows writing dlt packets to a storage file. 60 | pub fn new(writer: W) -> DltStorageWriter { 61 | DltStorageWriter { writer } 62 | } 63 | 64 | /// Writes a sliced packet into a storage file. 65 | pub fn write_slice( 66 | &mut self, 67 | storage_header: StorageHeader, 68 | dlt_slice: DltPacketSlice<'_>, 69 | ) -> Result<(), Error> { 70 | storage_header.write(&mut self.writer)?; 71 | self.writer.write_all(dlt_slice.slice()) 72 | } 73 | } 74 | 75 | #[cfg(feature = "std")] 76 | #[cfg(test)] 77 | mod dlt_storage_writer_tests { 78 | use super::*; 79 | use crate::DltHeader; 80 | use std::format; 81 | use std::vec::Vec; 82 | 83 | #[test] 84 | fn debug() { 85 | let mut buffer = Vec::::new(); 86 | let writer = DltStorageWriter::new(&mut buffer); 87 | assert!(format!("{:?}", writer).len() > 0); 88 | } 89 | 90 | #[test] 91 | fn new() { 92 | let mut buffer = Vec::::new(); 93 | let _writer = DltStorageWriter::new(&mut buffer); 94 | assert_eq!(0, buffer.len()); 95 | } 96 | 97 | #[test] 98 | fn write_slice() { 99 | // ok 100 | { 101 | let mut buffer = Vec::::new(); 102 | let mut writer = DltStorageWriter::new(&mut buffer); 103 | 104 | let packet0 = { 105 | let mut packet = Vec::::new(); 106 | let mut header = DltHeader { 107 | is_big_endian: true, 108 | message_counter: 0, 109 | length: 0, 110 | ecu_id: None, 111 | session_id: None, 112 | timestamp: None, 113 | extended_header: None, 114 | }; 115 | header.length = header.header_len() + 4; 116 | header.write(&mut packet).unwrap(); 117 | packet.write_all(&[1, 2, 3, 4]).unwrap(); 118 | packet 119 | }; 120 | let header0 = StorageHeader { 121 | timestamp_seconds: 1234, 122 | timestamp_microseconds: 2345, 123 | ecu_id: [b'A', b'B', b'C', b'D'], 124 | }; 125 | writer 126 | .write_slice( 127 | header0.clone(), 128 | DltPacketSlice::from_slice(&packet0).unwrap(), 129 | ) 130 | .unwrap(); 131 | 132 | // add a secondary packet 133 | let packet1 = { 134 | let mut packet = Vec::::new(); 135 | let mut header = DltHeader { 136 | is_big_endian: false, 137 | message_counter: 0, 138 | length: 0, 139 | ecu_id: None, 140 | session_id: None, 141 | timestamp: None, 142 | extended_header: None, 143 | }; 144 | header.length = header.header_len() + 4; 145 | header.write(&mut packet).unwrap(); 146 | packet.write_all(&[9, 0, 1, 2]).unwrap(); 147 | packet 148 | }; 149 | let header1 = StorageHeader { 150 | timestamp_seconds: 3456, 151 | timestamp_microseconds: 4567, 152 | ecu_id: [b'B', b'C', b'D', b'E'], 153 | }; 154 | writer 155 | .write_slice( 156 | header1.clone(), 157 | DltPacketSlice::from_slice(&packet1).unwrap(), 158 | ) 159 | .unwrap(); 160 | 161 | // check contents 162 | { 163 | let mut expected = Vec::new(); 164 | expected.extend_from_slice(&header0.to_bytes()); 165 | expected.extend_from_slice(&packet0); 166 | expected.extend_from_slice(&header1.to_bytes()); 167 | expected.extend_from_slice(&packet1); 168 | assert_eq!(expected, buffer); 169 | } 170 | } 171 | 172 | // check write error because of size error 173 | { 174 | let packet = { 175 | let mut packet = Vec::::new(); 176 | let mut header = DltHeader { 177 | is_big_endian: true, 178 | message_counter: 0, 179 | length: 0, 180 | ecu_id: None, 181 | session_id: None, 182 | timestamp: None, 183 | extended_header: None, 184 | }; 185 | header.length = header.header_len() + 4; 186 | header.write(&mut packet).unwrap(); 187 | packet.write_all(&[1, 2, 3, 4]).unwrap(); 188 | packet 189 | }; 190 | let header = StorageHeader { 191 | timestamp_seconds: 1234, 192 | timestamp_microseconds: 2345, 193 | ecu_id: [b'A', b'B', b'C', b'D'], 194 | }; 195 | 196 | // writer with not enough memory for the storage header 197 | { 198 | let mut buffer = [0u8; StorageHeader::BYTE_LEN - 1]; 199 | let mut cursor = std::io::Cursor::new(&mut buffer[..]); 200 | let mut writer = DltStorageWriter::new(&mut cursor); 201 | assert!(writer 202 | .write_slice(header.clone(), DltPacketSlice::from_slice(&packet).unwrap()) 203 | .is_err()); 204 | } 205 | // write with not enough memory for the packet 206 | { 207 | let mut buffer = [0u8; StorageHeader::BYTE_LEN + 1]; 208 | let mut cursor = std::io::Cursor::new(&mut buffer[..]); 209 | let mut writer = DltStorageWriter::new(&mut cursor); 210 | assert!(writer 211 | .write_slice(header, DltPacketSlice::from_slice(&packet).unwrap()) 212 | .is_err()); 213 | } 214 | } 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /src/storage/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "std")] 2 | mod dlt_storage_reader; 3 | #[cfg(feature = "std")] 4 | pub use dlt_storage_reader::*; 5 | 6 | #[cfg(feature = "std")] 7 | mod dlt_storage_writer; 8 | #[cfg(feature = "std")] 9 | pub use dlt_storage_writer::*; 10 | 11 | mod storage_header; 12 | pub use storage_header::*; 13 | 14 | mod storage_slice; 15 | pub use storage_slice::*; 16 | -------------------------------------------------------------------------------- /src/storage/storage_header.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | use core::str::Utf8Error; 3 | 4 | /// Header present before a `DltHeader` if a DLT packet is 5 | /// stored in .dlt file or database. 6 | #[derive(Debug, Eq, PartialEq, Clone)] 7 | pub struct StorageHeader { 8 | pub timestamp_seconds: u32, 9 | pub timestamp_microseconds: u32, 10 | pub ecu_id: [u8; 4], 11 | } 12 | 13 | impl StorageHeader { 14 | /// Pattern/Magic Number that must be present at the start of a storage header. 15 | pub const PATTERN_AT_START: [u8; 4] = [0x44, 0x4C, 0x54, 0x01]; 16 | 17 | /// Serialized length of the header in bytes. 18 | pub const BYTE_LEN: usize = 16; 19 | 20 | /// Returns the serialized from of the header. 21 | pub fn to_bytes(&self) -> [u8; 16] { 22 | let ts = self.timestamp_seconds.to_le_bytes(); 23 | let tms = self.timestamp_microseconds.to_le_bytes(); 24 | [ 25 | StorageHeader::PATTERN_AT_START[0], 26 | StorageHeader::PATTERN_AT_START[1], 27 | StorageHeader::PATTERN_AT_START[2], 28 | StorageHeader::PATTERN_AT_START[3], 29 | ts[0], 30 | ts[1], 31 | ts[2], 32 | ts[3], 33 | tms[0], 34 | tms[1], 35 | tms[2], 36 | tms[3], 37 | self.ecu_id[0], 38 | self.ecu_id[1], 39 | self.ecu_id[2], 40 | self.ecu_id[3], 41 | ] 42 | } 43 | 44 | /// Tries to decode a storage header. 45 | pub fn from_bytes( 46 | bytes: [u8; 16], 47 | ) -> Result { 48 | let start_pattern = [bytes[0], bytes[1], bytes[2], bytes[3]]; 49 | if start_pattern != StorageHeader::PATTERN_AT_START { 50 | Err(error::StorageHeaderStartPatternError { 51 | actual_pattern: start_pattern, 52 | }) 53 | } else { 54 | Ok(StorageHeader { 55 | timestamp_seconds: u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]), 56 | timestamp_microseconds: u32::from_le_bytes([ 57 | bytes[8], bytes[9], bytes[10], bytes[11], 58 | ]), 59 | ecu_id: [bytes[12], bytes[13], bytes[14], bytes[15]], 60 | }) 61 | } 62 | } 63 | 64 | ///Deserialize a DltHeader & TpHeader from the given reader. 65 | #[cfg(feature = "std")] 66 | pub fn read(reader: &mut T) -> Result { 67 | let mut bytes: [u8; 16] = [0; 16]; 68 | reader.read_exact(&mut bytes)?; 69 | Ok(StorageHeader::from_bytes(bytes)?) 70 | } 71 | 72 | ///Serializes the header to the given writer. 73 | #[cfg(feature = "std")] 74 | pub fn write(&self, writer: &mut T) -> Result<(), std::io::Error> { 75 | writer.write_all(&self.to_bytes())?; 76 | Ok(()) 77 | } 78 | 79 | /// Returns the ecu id decoded as an UTF8 string or an error if 80 | /// decoding was not possible. 81 | pub fn ecu_id_str(&self) -> Result<&str, Utf8Error> { 82 | core::str::from_utf8(&self.ecu_id) 83 | } 84 | } 85 | 86 | #[cfg(test)] 87 | mod storage_header_tests { 88 | 89 | use super::*; 90 | use crate::proptest_generators::storage_header_any; 91 | use proptest::prelude::*; 92 | use std::format; 93 | 94 | proptest! { 95 | #[test] 96 | fn debug( 97 | header in storage_header_any() 98 | ) { 99 | prop_assert_eq!( 100 | format!( 101 | "StorageHeader {{ timestamp_seconds: {}, timestamp_microseconds: {}, ecu_id: {:?} }}", 102 | header.timestamp_seconds, 103 | header.timestamp_microseconds, 104 | header.ecu_id 105 | ), 106 | format!("{:?}", header) 107 | ); 108 | } 109 | } 110 | 111 | proptest! { 112 | #[test] 113 | fn to_bytes( 114 | header in storage_header_any() 115 | ) { 116 | let secs_be = header.timestamp_seconds.to_le_bytes(); 117 | let us_be = header.timestamp_microseconds.to_le_bytes(); 118 | 119 | prop_assert_eq!( 120 | header.to_bytes(), 121 | [ 122 | 0x44, 0x4C, 0x54, 0x01, 123 | secs_be[0], secs_be[1], secs_be[2], secs_be[3], 124 | us_be[0], us_be[1], us_be[2], us_be[3], 125 | header.ecu_id[0], header.ecu_id[1], header.ecu_id[2], header.ecu_id[3], 126 | ] 127 | ); 128 | } 129 | } 130 | 131 | proptest! { 132 | #[test] 133 | fn from_bytes( 134 | header in storage_header_any(), 135 | bad_pattern in any::<[u8;4]>().prop_filter( 136 | "pattern must not match the expected pattern", 137 | |v| *v != StorageHeader::PATTERN_AT_START 138 | ) 139 | ) { 140 | // ok case 141 | prop_assert_eq!( 142 | Ok(header.clone()), 143 | StorageHeader::from_bytes(header.to_bytes()) 144 | ); 145 | 146 | // start partern error 147 | { 148 | let mut bytes = header.to_bytes(); 149 | bytes[0] = bad_pattern[0]; 150 | bytes[1] = bad_pattern[1]; 151 | bytes[2] = bad_pattern[2]; 152 | bytes[3] = bad_pattern[3]; 153 | prop_assert_eq!( 154 | Err(error::StorageHeaderStartPatternError{ 155 | actual_pattern: bad_pattern.clone(), 156 | }), 157 | StorageHeader::from_bytes(bytes) 158 | ); 159 | } 160 | } 161 | } 162 | 163 | proptest! { 164 | #[cfg(feature = "std")] 165 | #[test] 166 | fn read( 167 | header in storage_header_any(), 168 | bad_len in 0..StorageHeader::BYTE_LEN, 169 | bad_pattern in any::<[u8;4]>().prop_filter( 170 | "pattern must not match the expected pattern", 171 | |v| *v != StorageHeader::PATTERN_AT_START 172 | ) 173 | ) { 174 | // ok read 175 | { 176 | let bytes = header.to_bytes(); 177 | let mut cursor = std::io::Cursor::new(&bytes[..]); 178 | prop_assert_eq!( 179 | header.clone(), 180 | StorageHeader::read(&mut cursor).unwrap() 181 | ); 182 | } 183 | 184 | // unexpected eof 185 | { 186 | let bytes = header.to_bytes(); 187 | let mut cursor = std::io::Cursor::new(&bytes[..bad_len]); 188 | prop_assert!(StorageHeader::read(&mut cursor).is_err()); 189 | } 190 | 191 | // start pattern error 192 | { 193 | let mut bytes = header.to_bytes(); 194 | bytes[0] = bad_pattern[0]; 195 | bytes[1] = bad_pattern[1]; 196 | bytes[2] = bad_pattern[2]; 197 | bytes[3] = bad_pattern[3]; 198 | let mut cursor = std::io::Cursor::new(&bytes[..]); 199 | prop_assert!(StorageHeader::read(&mut cursor).is_err()); 200 | } 201 | } 202 | } 203 | 204 | proptest! { 205 | #[cfg(feature = "std")] 206 | #[test] 207 | fn write( 208 | header in storage_header_any() 209 | ) { 210 | // ok write 211 | { 212 | let mut buffer = [0u8; StorageHeader::BYTE_LEN]; 213 | let mut cursor = std::io::Cursor::new(&mut buffer[..]); 214 | header.write(&mut cursor).unwrap(); 215 | prop_assert_eq!(&buffer, &header.to_bytes()); 216 | } 217 | 218 | // trigger and error as there is not enough memory to write the complete header 219 | { 220 | let mut buffer = [0u8; StorageHeader::BYTE_LEN - 1]; 221 | let mut cursor = std::io::Cursor::new(&mut buffer[..]); 222 | prop_assert!(header.write(&mut cursor).is_err()); 223 | } 224 | } 225 | } 226 | 227 | proptest! { 228 | #[test] 229 | fn ecu_id_str( 230 | header in storage_header_any() 231 | ) { 232 | prop_assert_eq!( 233 | header.ecu_id_str(), 234 | core::str::from_utf8(&header.ecu_id) 235 | ); 236 | } 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /src/storage/storage_slice.rs: -------------------------------------------------------------------------------- 1 | use super::StorageHeader; 2 | use crate::DltPacketSlice; 3 | 4 | #[derive(Clone, Debug, Eq, PartialEq)] 5 | pub struct StorageSlice<'a> { 6 | pub storage_header: StorageHeader, 7 | pub packet: DltPacketSlice<'a>, 8 | } 9 | -------------------------------------------------------------------------------- /src/verbose/mod.rs: -------------------------------------------------------------------------------- 1 | mod field_slicer; 2 | use field_slicer::*; 3 | 4 | mod values; 5 | pub use values::*; 6 | 7 | mod verbose_iter; 8 | pub use verbose_iter::*; 9 | 10 | mod pre_checked_verbose_iter; 11 | pub use pre_checked_verbose_iter::*; 12 | 13 | mod verbose_value; 14 | pub use verbose_value::*; 15 | 16 | use super::*; 17 | use core::str; 18 | 19 | #[derive(Debug, PartialEq, Clone)] 20 | #[cfg_attr(feature = "serde", derive(serde::Serialize))] 21 | pub struct Scaling { 22 | quantization: f32, 23 | offset: T, 24 | } 25 | 26 | #[derive(Debug, PartialEq, Clone)] 27 | #[cfg_attr(feature = "serde", derive(serde::Serialize))] 28 | pub struct VariableInfoUnit<'a> { 29 | name: &'a str, 30 | unit: &'a str, 31 | } 32 | 33 | #[derive(Debug, PartialEq, Clone)] 34 | #[cfg_attr(feature = "serde", derive(serde::Serialize))] 35 | pub struct ArrayDimensions<'a> { 36 | /// If true the dimesions u16 are encoded in big endian. 37 | is_big_endian: bool, 38 | /// Pointer to the raw dimensions data. 39 | dimensions: &'a [u8], 40 | } 41 | 42 | impl<'a> ArrayDimensions<'a> { 43 | pub fn iter(&'a self) -> ArrayDimensionIterator<'a> { 44 | ArrayDimensionIterator { 45 | is_big_endian: self.is_big_endian, 46 | rest: self.dimensions, 47 | } 48 | } 49 | } 50 | 51 | impl<'a> IntoIterator for &'a ArrayDimensions<'a> { 52 | type Item = u16; 53 | type IntoIter = ArrayDimensionIterator<'a>; 54 | 55 | fn into_iter(self) -> Self::IntoIter { 56 | self.iter() 57 | } 58 | } 59 | 60 | #[derive(Debug)] 61 | pub struct ArrayDimensionIterator<'a> { 62 | is_big_endian: bool, 63 | rest: &'a [u8], 64 | } 65 | 66 | impl<'a> Iterator for ArrayDimensionIterator<'a> { 67 | type Item = u16; 68 | 69 | fn next(&mut self) -> Option { 70 | if self.rest.len() < 2 { 71 | None 72 | } else { 73 | let result = if self.is_big_endian { 74 | u16::from_be_bytes([self.rest[0], self.rest[1]]) 75 | } else { 76 | u16::from_le_bytes([self.rest[0], self.rest[1]]) 77 | }; 78 | self.rest = &self.rest[2..]; 79 | Some(result) 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/verbose/pre_checked_verbose_iter.rs: -------------------------------------------------------------------------------- 1 | use super::{VerboseIter, VerboseValue}; 2 | use crate::error::VerboseDecodeError; 3 | 4 | /// Iterator over verbose values (payload was verified at start and contains no errors). 5 | #[derive(Debug, Clone, Eq, PartialEq)] 6 | pub struct PrecheckedVerboseIter<'a> { 7 | iter: VerboseIter<'a>, 8 | } 9 | 10 | impl<'a> PrecheckedVerboseIter<'a> { 11 | /// Creates an iterator to iterate over verbose values and check 12 | /// there are no errors present. 13 | pub fn try_new( 14 | is_big_endian: bool, 15 | number_of_arguments: u16, 16 | payload: &'a [u8], 17 | ) -> Result, VerboseDecodeError> { 18 | let iter = VerboseIter::new(is_big_endian, number_of_arguments, payload); 19 | // do a test run through the data to ensure all is good 20 | for v in iter.clone() { 21 | v?; 22 | } 23 | Ok(PrecheckedVerboseIter { iter }) 24 | } 25 | } 26 | 27 | impl<'a> TryFrom> for PrecheckedVerboseIter<'a> { 28 | type Error = VerboseDecodeError; 29 | 30 | fn try_from(value: VerboseIter<'a>) -> Result { 31 | for v in value.clone() { 32 | v?; 33 | } 34 | Ok(PrecheckedVerboseIter { iter: value }) 35 | } 36 | } 37 | 38 | impl<'a> core::iter::Iterator for PrecheckedVerboseIter<'a> { 39 | type Item = VerboseValue<'a>; 40 | 41 | fn next(&mut self) -> Option { 42 | self.iter.next().map(|v| v.unwrap()) 43 | } 44 | } 45 | 46 | #[cfg(feature = "serde")] 47 | impl<'a> serde::ser::Serialize for PrecheckedVerboseIter<'a> { 48 | fn serialize(&self, serializer: S) -> Result 49 | where 50 | S: serde::Serializer, 51 | { 52 | use serde::ser::SerializeSeq; 53 | let mut seq = serializer.serialize_seq(Some(self.iter.number_of_arguments().into()))?; 54 | for element in self.clone() { 55 | seq.serialize_element(&element)?; 56 | } 57 | seq.end() 58 | } 59 | } 60 | 61 | #[cfg(test)] 62 | mod test { 63 | use super::VerboseIter; 64 | use crate::verbose::{PrecheckedVerboseIter, U16Value, U32Value, VerboseValue}; 65 | use arrayvec::ArrayVec; 66 | 67 | #[test] 68 | fn new_and_next() { 69 | // zero args 70 | { 71 | let data = [1, 2, 3, 4]; 72 | let actual = PrecheckedVerboseIter::try_new(true, 0, &data).unwrap(); 73 | assert_eq!(actual.iter, VerboseIter::new(true, 0, &data)); 74 | } 75 | // single value 76 | { 77 | let mut data = ArrayVec::::new(); 78 | let value = U16Value { 79 | variable_info: None, 80 | scaling: None, 81 | value: 1234, 82 | }; 83 | value.add_to_msg(&mut data, true).unwrap(); 84 | 85 | let mut iter = PrecheckedVerboseIter::try_new(true, 1, &data).unwrap(); 86 | assert_eq!(Some(VerboseValue::U16(value)), iter.next()); 87 | assert_eq!(None, iter.next()); 88 | assert_eq!(None, iter.next()); 89 | } 90 | // error: number of arguments bigger then present data 91 | { 92 | let mut data = ArrayVec::::new(); 93 | let first_value = U16Value { 94 | variable_info: None, 95 | scaling: None, 96 | value: 1234, 97 | }; 98 | first_value.add_to_msg(&mut data, false).unwrap(); 99 | let second_value = U32Value { 100 | variable_info: None, 101 | scaling: None, 102 | value: 2345, 103 | }; 104 | second_value.add_to_msg(&mut data, false).unwrap(); 105 | 106 | assert!(PrecheckedVerboseIter::try_new(false, 3, &data).is_err()); 107 | } 108 | } 109 | 110 | #[cfg(feature = "serde")] 111 | #[test] 112 | fn serialize() { 113 | use VerboseValue::{U16, U32}; 114 | 115 | let mut data = ArrayVec::::new(); 116 | let first_value = U16Value { 117 | variable_info: None, 118 | scaling: None, 119 | value: 1234, 120 | }; 121 | first_value.add_to_msg(&mut data, false).unwrap(); 122 | let second_value = U32Value { 123 | variable_info: None, 124 | scaling: None, 125 | value: 2345, 126 | }; 127 | second_value.add_to_msg(&mut data, false).unwrap(); 128 | 129 | let iter = PrecheckedVerboseIter::try_new(false, 2, &data).unwrap(); 130 | 131 | assert_eq!( 132 | serde_json::to_string(&iter).unwrap(), 133 | serde_json::to_string(&[U16(first_value), U32(second_value)]).unwrap() 134 | ); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/verbose/values/array_iteratable.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "serde")] 2 | use super::{ 3 | ArrayBoolIterator, ArrayF128Iterator, ArrayF16Iterator, ArrayF32Iterator, ArrayF64Iterator, 4 | ArrayI128Iterator, ArrayI16Iterator, ArrayI32Iterator, ArrayI64Iterator, ArrayI8Iterator, 5 | ArrayU128Iterator, ArrayU16Iterator, ArrayU32Iterator, ArrayU64Iterator, ArrayU8Iterator, 6 | }; 7 | use super::{RawF128, RawF16}; 8 | #[cfg(feature = "serde")] 9 | use crate::verbose::std::mem::size_of; 10 | #[cfg(feature = "serde")] 11 | use serde::ser::{Serialize, SerializeSeq, Serializer}; 12 | 13 | #[cfg(feature = "serde")] 14 | #[derive(Clone, Debug)] 15 | pub(crate) struct ArrayItDimension<'a, T: ArrayIteratable + Sized> { 16 | pub(crate) is_big_endian: bool, 17 | pub(crate) dimensions: &'a [u8], 18 | pub(crate) data: &'a [u8], 19 | pub(crate) phantom: core::marker::PhantomData, 20 | } 21 | 22 | #[cfg(feature = "serde")] 23 | impl<'a, T: ArrayIteratable + Sized> Serialize for ArrayItDimension<'a, T> { 24 | fn serialize(&self, serializer: S) -> Result 25 | where 26 | S: Serializer, 27 | { 28 | match self.dimensions.len() { 29 | 0 | 1 => serializer.serialize_seq(Some(0))?.end(), 30 | 2 => T::serialize_elements(self.is_big_endian, self.data, serializer), 31 | _ => { 32 | // calculate memory step size 33 | let mut stepsize: usize = 1; 34 | for i in (2..self.dimensions.len()).step_by(2) { 35 | let bytes = [self.dimensions[i], self.dimensions[i + 1]]; 36 | stepsize *= usize::from(if self.is_big_endian { 37 | u16::from_be_bytes(bytes) 38 | } else { 39 | u16::from_le_bytes(bytes) 40 | }); 41 | } 42 | 43 | // determine own dim size & the subdimensions 44 | let sub_dimensions = &self.dimensions[2..]; 45 | let dim_count: usize = { 46 | let bytes = [self.dimensions[0], self.dimensions[1]]; 47 | if self.is_big_endian { 48 | u16::from_be_bytes(bytes) 49 | } else { 50 | u16::from_le_bytes(bytes) 51 | } 52 | } 53 | .into(); 54 | // iterate over blocks 55 | let mut seq = serializer.serialize_seq(Some(dim_count))?; 56 | for i in 0..dim_count { 57 | // serialize subdimensions 58 | let block_start = i * stepsize * size_of::(); 59 | let block_end = (i + 1) * stepsize * size_of::(); 60 | 61 | let subit = ArrayItDimension::<'a, T> { 62 | is_big_endian: self.is_big_endian, 63 | dimensions: sub_dimensions, 64 | data: &self.data[block_start..block_end], 65 | phantom: Default::default(), 66 | }; 67 | seq.serialize_element(&subit)?; 68 | } 69 | seq.end() 70 | } 71 | } 72 | } 73 | } 74 | 75 | #[cfg(feature = "serde")] 76 | pub(crate) trait ArrayIteratable { 77 | const ELEMENT_SIZE: usize; 78 | 79 | fn serialize_elements( 80 | is_big_endian: bool, 81 | data: &[u8], 82 | serializer: S, 83 | ) -> Result; 84 | } 85 | 86 | #[cfg(feature = "serde")] 87 | impl ArrayIteratable for bool { 88 | const ELEMENT_SIZE: usize = 1; 89 | 90 | fn serialize_elements( 91 | _is_big_endian: bool, 92 | data: &[u8], 93 | serializer: S, 94 | ) -> Result { 95 | ArrayBoolIterator { rest: data }.serialize(serializer) 96 | } 97 | } 98 | 99 | #[cfg(feature = "serde")] 100 | impl ArrayIteratable for u8 { 101 | const ELEMENT_SIZE: usize = 1; 102 | 103 | fn serialize_elements( 104 | _is_big_endian: bool, 105 | data: &[u8], 106 | serializer: S, 107 | ) -> Result { 108 | ArrayU8Iterator { rest: data }.serialize(serializer) 109 | } 110 | } 111 | 112 | #[cfg(feature = "serde")] 113 | impl ArrayIteratable for u16 { 114 | const ELEMENT_SIZE: usize = 2; 115 | 116 | fn serialize_elements( 117 | _is_big_endian: bool, 118 | data: &[u8], 119 | serializer: S, 120 | ) -> Result { 121 | ArrayU16Iterator { 122 | rest: data, 123 | is_big_endian: _is_big_endian, 124 | } 125 | .serialize(serializer) 126 | } 127 | } 128 | 129 | #[cfg(feature = "serde")] 130 | impl ArrayIteratable for u32 { 131 | const ELEMENT_SIZE: usize = 4; 132 | 133 | fn serialize_elements( 134 | _is_big_endian: bool, 135 | data: &[u8], 136 | serializer: S, 137 | ) -> Result { 138 | ArrayU32Iterator { 139 | rest: data, 140 | is_big_endian: _is_big_endian, 141 | } 142 | .serialize(serializer) 143 | } 144 | } 145 | 146 | #[cfg(feature = "serde")] 147 | impl ArrayIteratable for u64 { 148 | const ELEMENT_SIZE: usize = 8; 149 | 150 | fn serialize_elements( 151 | _is_big_endian: bool, 152 | data: &[u8], 153 | serializer: S, 154 | ) -> Result { 155 | ArrayU64Iterator { 156 | rest: data, 157 | is_big_endian: _is_big_endian, 158 | } 159 | .serialize(serializer) 160 | } 161 | } 162 | 163 | #[cfg(feature = "serde")] 164 | impl ArrayIteratable for u128 { 165 | const ELEMENT_SIZE: usize = 16; 166 | 167 | fn serialize_elements( 168 | _is_big_endian: bool, 169 | data: &[u8], 170 | serializer: S, 171 | ) -> Result { 172 | ArrayU128Iterator { 173 | rest: data, 174 | is_big_endian: _is_big_endian, 175 | } 176 | .serialize(serializer) 177 | } 178 | } 179 | 180 | #[cfg(feature = "serde")] 181 | impl ArrayIteratable for i8 { 182 | const ELEMENT_SIZE: usize = 1; 183 | 184 | fn serialize_elements( 185 | _is_big_endian: bool, 186 | data: &[u8], 187 | serializer: S, 188 | ) -> Result { 189 | ArrayI8Iterator { rest: data }.serialize(serializer) 190 | } 191 | } 192 | 193 | #[cfg(feature = "serde")] 194 | impl ArrayIteratable for i16 { 195 | const ELEMENT_SIZE: usize = 2; 196 | 197 | fn serialize_elements( 198 | _is_big_endian: bool, 199 | data: &[u8], 200 | serializer: S, 201 | ) -> Result { 202 | ArrayI16Iterator { 203 | rest: data, 204 | is_big_endian: _is_big_endian, 205 | } 206 | .serialize(serializer) 207 | } 208 | } 209 | 210 | #[cfg(feature = "serde")] 211 | impl ArrayIteratable for i32 { 212 | const ELEMENT_SIZE: usize = 4; 213 | 214 | fn serialize_elements( 215 | _is_big_endian: bool, 216 | data: &[u8], 217 | serializer: S, 218 | ) -> Result { 219 | ArrayI32Iterator { 220 | rest: data, 221 | is_big_endian: _is_big_endian, 222 | } 223 | .serialize(serializer) 224 | } 225 | } 226 | 227 | #[cfg(feature = "serde")] 228 | impl ArrayIteratable for i64 { 229 | const ELEMENT_SIZE: usize = 8; 230 | 231 | fn serialize_elements( 232 | _is_big_endian: bool, 233 | data: &[u8], 234 | serializer: S, 235 | ) -> Result { 236 | ArrayI64Iterator { 237 | rest: data, 238 | is_big_endian: _is_big_endian, 239 | } 240 | .serialize(serializer) 241 | } 242 | } 243 | 244 | #[cfg(feature = "serde")] 245 | impl ArrayIteratable for i128 { 246 | const ELEMENT_SIZE: usize = 16; 247 | 248 | fn serialize_elements( 249 | _is_big_endian: bool, 250 | data: &[u8], 251 | serializer: S, 252 | ) -> Result { 253 | ArrayI128Iterator { 254 | rest: data, 255 | is_big_endian: _is_big_endian, 256 | } 257 | .serialize(serializer) 258 | } 259 | } 260 | 261 | #[cfg(feature = "serde")] 262 | impl ArrayIteratable for RawF16 { 263 | const ELEMENT_SIZE: usize = 2; 264 | 265 | fn serialize_elements( 266 | _is_big_endian: bool, 267 | data: &[u8], 268 | serializer: S, 269 | ) -> Result { 270 | ArrayF16Iterator { 271 | rest: data, 272 | is_big_endian: _is_big_endian, 273 | } 274 | .serialize(serializer) 275 | } 276 | } 277 | 278 | #[cfg(feature = "serde")] 279 | impl ArrayIteratable for f32 { 280 | const ELEMENT_SIZE: usize = 4; 281 | 282 | fn serialize_elements( 283 | _is_big_endian: bool, 284 | data: &[u8], 285 | serializer: S, 286 | ) -> Result { 287 | ArrayF32Iterator { 288 | rest: data, 289 | is_big_endian: _is_big_endian, 290 | } 291 | .serialize(serializer) 292 | } 293 | } 294 | 295 | #[cfg(feature = "serde")] 296 | impl ArrayIteratable for f64 { 297 | const ELEMENT_SIZE: usize = 8; 298 | 299 | fn serialize_elements( 300 | _is_big_endian: bool, 301 | data: &[u8], 302 | serializer: S, 303 | ) -> Result { 304 | ArrayF64Iterator { 305 | rest: data, 306 | is_big_endian: _is_big_endian, 307 | } 308 | .serialize(serializer) 309 | } 310 | } 311 | 312 | #[cfg(feature = "serde")] 313 | impl ArrayIteratable for RawF128 { 314 | const ELEMENT_SIZE: usize = 16; 315 | 316 | fn serialize_elements( 317 | _is_big_endian: bool, 318 | data: &[u8], 319 | serializer: S, 320 | ) -> Result { 321 | ArrayF128Iterator { 322 | rest: data, 323 | is_big_endian: _is_big_endian, 324 | } 325 | .serialize(serializer) 326 | } 327 | } 328 | -------------------------------------------------------------------------------- /src/verbose/values/bool_value.rs: -------------------------------------------------------------------------------- 1 | use arrayvec::{ArrayVec, CapacityError}; 2 | 3 | #[derive(Debug, Eq, PartialEq, Clone)] 4 | #[cfg_attr(feature = "serde", derive(serde::Serialize))] 5 | pub struct BoolValue<'a> { 6 | pub name: Option<&'a str>, 7 | pub value: bool, 8 | } 9 | 10 | impl<'a> BoolValue<'a> { 11 | /// Adds the verbose value to the given dlt mesage buffer. 12 | pub fn add_to_msg( 13 | &self, 14 | buf: &mut ArrayVec, 15 | is_big_endian: bool, 16 | ) -> Result<(), CapacityError> { 17 | if self.name.is_some() { 18 | let name = unsafe { self.name.unwrap_unchecked() }; 19 | let type_info: [u8; 4] = [0b0001_0001, 0b0000_1000, 0b0000_0000, 0b0000_0000]; 20 | buf.try_extend_from_slice(&type_info)?; 21 | 22 | let name_len = if is_big_endian { 23 | (name.len() as u16 + 1).to_be_bytes() 24 | } else { 25 | (name.len() as u16 + 1).to_le_bytes() 26 | }; 27 | buf.try_extend_from_slice(&[name_len[0], name_len[1]])?; 28 | buf.try_extend_from_slice(name.as_bytes())?; 29 | if buf.remaining_capacity() > 1 { 30 | // Safe as capacity is checked earlier 31 | unsafe { buf.push_unchecked(0) }; 32 | unsafe { buf.push_unchecked(u8::from(self.value)) } 33 | Ok(()) 34 | } else { 35 | Err(CapacityError::new(())) 36 | } 37 | } else { 38 | let type_info: [u8; 4] = [0b0001_0001, 0b0000_0000, 0b0000_0000, 0b0000_0000]; 39 | buf.try_extend_from_slice(&type_info)?; 40 | 41 | if buf.remaining_capacity() > 0 { 42 | // Safe as capacity is checked earlier 43 | unsafe { buf.push_unchecked(u8::from(self.value)) } 44 | Ok(()) 45 | } else { 46 | Err(CapacityError::new(())) 47 | } 48 | } 49 | } 50 | } 51 | 52 | #[cfg(test)] 53 | mod test { 54 | use super::*; 55 | use crate::verbose::VerboseValue; 56 | use crate::verbose::VerboseValue::Bool; 57 | use alloc::vec::Vec; 58 | use proptest::prelude::*; 59 | use std::format; 60 | 61 | proptest! { 62 | #[test] 63 | fn write_read(value in any::(), ref name in "\\pc{1,20}") { 64 | const MAX_SYMBOL_LENGTH: usize = 20; 65 | const BYTES_NEEDED: usize = 8; 66 | // The buffer needs to be sized the max len of the name * 4 + 8 bits. (8 Byte: 4 Byte TypeInfo + 2 Bytes Length of Name + 1 Byte Null Terminator of Name + 1 Byte Data) 67 | // As Proptest only generates chars by characters (which can be up to 4 bytes), the buffer needs to be 4 * len of name 68 | const BUFFER_SIZE: usize = MAX_SYMBOL_LENGTH * 4 + BYTES_NEEDED; 69 | 70 | // test big endian with name 71 | { 72 | let mut msg_buff: ArrayVec = ArrayVec::new(); 73 | 74 | let bool_val = BoolValue { name: Some(name), value }; 75 | let slice_len = name.len() + BYTES_NEEDED; 76 | let mut content_buff = Vec::with_capacity(slice_len); 77 | let is_big_endian = true; 78 | let len_be = (name.len() as u16 + 1).to_be_bytes(); 79 | prop_assert_eq!(bool_val.add_to_msg(&mut msg_buff, is_big_endian), Ok(())); 80 | 81 | 82 | 83 | content_buff.extend_from_slice(&[0b0001_0001, 0b0000_1000, 0b0000_0000, 0b0000_0000, len_be[0], len_be[1]]); 84 | // for b in name.as_bytes() { 85 | // content_buff.push(*b); 86 | // } 87 | content_buff.extend_from_slice(name.as_bytes()); 88 | content_buff.push(0); 89 | content_buff.push(u8::from(value)); 90 | 91 | prop_assert_eq!(&msg_buff[..slice_len], &content_buff[..]); 92 | 93 | // Now wrap back 94 | let parsed_back = VerboseValue::from_slice(&msg_buff, is_big_endian); 95 | prop_assert_eq!(parsed_back, Ok((Bool(bool_val),&[] as &[u8]))); 96 | 97 | } 98 | 99 | // Test little endian with name 100 | { 101 | let mut msg_buff: ArrayVec = ArrayVec::new(); 102 | let bool_val = BoolValue { name: Some(name), value }; 103 | let slice_len = name.len() + BYTES_NEEDED; 104 | let mut content_buff = Vec::with_capacity(slice_len); 105 | let is_big_endian = false; 106 | let len_le = (name.len() as u16 + 1).to_le_bytes(); 107 | 108 | prop_assert_eq!(bool_val.add_to_msg(&mut msg_buff, is_big_endian), Ok(())); 109 | 110 | content_buff.extend_from_slice(&[0b0001_0001, 0b0000_1000, 0b0000_0000, 0b0000_0000, len_le[0], len_le[1]]); 111 | for b in name.as_bytes() { 112 | content_buff.push(*b); 113 | } 114 | content_buff.push(0); 115 | content_buff.push(u8::from(value)); 116 | 117 | prop_assert_eq!(&msg_buff[..slice_len], &content_buff[..]); 118 | 119 | // Now wrap back 120 | let parsed_back = VerboseValue::from_slice(&msg_buff, is_big_endian); 121 | prop_assert_eq!(parsed_back, Ok((Bool(bool_val),&[] as &[u8]))); 122 | 123 | } 124 | 125 | 126 | // Test big endian without name 127 | { 128 | let mut msg_buff: ArrayVec = ArrayVec::new(); 129 | 130 | let bool_val = BoolValue { name: None, value }; 131 | let slice_len = 5; 132 | prop_assert_eq!(bool_val.add_to_msg(&mut msg_buff, true), Ok(())); 133 | 134 | let expected = &[0b0001_0001, 0b0000_0000, 0b0000_0000, 0b0000_0000, u8::from(value)]; 135 | prop_assert_eq!(&msg_buff[..slice_len], expected); 136 | 137 | // Now wrap back 138 | let parsed_back_be = VerboseValue::from_slice(&msg_buff, true); 139 | prop_assert_eq!(parsed_back_be, Ok((Bool(bool_val),&[] as &[u8]))); 140 | 141 | } 142 | 143 | // Test little endian without name 144 | { 145 | let mut msg_buff: ArrayVec = ArrayVec::new(); 146 | 147 | let bool_val = BoolValue { name: None, value }; 148 | let slice_len = 5; 149 | prop_assert_eq!(bool_val.add_to_msg(&mut msg_buff, false), Ok(())); 150 | 151 | let expected = &[0b0001_0001, 0b0000_0000, 0b0000_0000, 0b0000_0000, u8::from(value)]; 152 | prop_assert_eq!(&msg_buff[..slice_len], expected); 153 | 154 | // Now wrap back 155 | let parsed_back_le = VerboseValue::from_slice(&msg_buff, false); 156 | prop_assert_eq!(parsed_back_le, Ok((Bool(bool_val),&[] as &[u8]))); 157 | 158 | } 159 | 160 | // Capacity error big endian with name 161 | { 162 | let bool_val = BoolValue { name: Some(name), value }; 163 | let is_big_endian = true; 164 | 165 | let mut msg_buff: ArrayVec = ArrayVec::new(); 166 | prop_assert_eq!(bool_val.add_to_msg(&mut msg_buff, is_big_endian), Err(CapacityError::new(()))); 167 | 168 | let mut msg_buff: ArrayVec = ArrayVec::new(); 169 | prop_assert_eq!(bool_val.add_to_msg(&mut msg_buff, is_big_endian), Err(CapacityError::new(()))); 170 | 171 | } 172 | 173 | // Capacity error little endian with name 174 | { 175 | let bool_val = BoolValue { name: Some(name), value }; 176 | let is_big_endian = false; 177 | 178 | let mut msg_buff: ArrayVec = ArrayVec::new(); 179 | prop_assert_eq!(bool_val.add_to_msg(&mut msg_buff, is_big_endian), Err(CapacityError::new(()))); 180 | 181 | let mut msg_buff: ArrayVec = ArrayVec::new(); 182 | prop_assert_eq!(bool_val.add_to_msg(&mut msg_buff, is_big_endian), Err(CapacityError::new(()))); 183 | 184 | } 185 | 186 | // Capacity error big endian without name 187 | { 188 | let mut msg_buff: ArrayVec = ArrayVec::new(); 189 | 190 | let bool_val = BoolValue { name: None, value }; 191 | let is_big_endian = true; 192 | prop_assert_eq!(bool_val.add_to_msg(&mut msg_buff, is_big_endian), Err(CapacityError::new(()))); 193 | 194 | let mut msg_buff: ArrayVec = ArrayVec::new(); 195 | prop_assert_eq!(bool_val.add_to_msg(&mut msg_buff, is_big_endian), Err(CapacityError::new(()))); 196 | 197 | } 198 | // Capacity error big endian without name 199 | { 200 | let mut msg_buff: ArrayVec = ArrayVec::new(); 201 | 202 | let bool_val = BoolValue { name: None, value }; 203 | let is_big_endian = false; 204 | prop_assert_eq!(bool_val.add_to_msg(&mut msg_buff, is_big_endian), Err(CapacityError::new(()))); 205 | 206 | let mut msg_buff: ArrayVec = ArrayVec::new(); 207 | prop_assert_eq!(bool_val.add_to_msg(&mut msg_buff, is_big_endian), Err(CapacityError::new(()))); 208 | 209 | } 210 | 211 | 212 | } 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /src/verbose/values/mod.rs: -------------------------------------------------------------------------------- 1 | mod bool_value; 2 | pub use bool_value::*; 3 | 4 | mod i8_value; 5 | pub use i8_value::*; 6 | 7 | mod i16_value; 8 | pub use i16_value::*; 9 | 10 | mod i32_value; 11 | pub use i32_value::*; 12 | 13 | mod i64_value; 14 | pub use i64_value::*; 15 | 16 | mod i128_value; 17 | pub use i128_value::*; 18 | 19 | mod u8_value; 20 | pub use u8_value::*; 21 | 22 | mod u16_value; 23 | pub use u16_value::*; 24 | 25 | mod u32_value; 26 | pub use u32_value::*; 27 | 28 | mod u64_value; 29 | pub use u64_value::*; 30 | 31 | mod u128_value; 32 | pub use u128_value::*; 33 | 34 | mod f16_value; 35 | pub use f16_value::*; 36 | 37 | mod f32_value; 38 | pub use f32_value::*; 39 | 40 | mod f64_value; 41 | pub use f64_value::*; 42 | 43 | mod f128_value; 44 | pub use f128_value::*; 45 | 46 | mod string_value; 47 | pub use string_value::*; 48 | 49 | mod raw_f16; 50 | pub use raw_f16::*; 51 | 52 | mod raw_f128; 53 | pub use raw_f128::*; 54 | 55 | mod raw_value; 56 | pub use raw_value::*; 57 | 58 | mod trace_info_value; 59 | pub use trace_info_value::*; 60 | 61 | mod struct_value; 62 | pub use struct_value::*; 63 | 64 | mod array_bool; 65 | pub use array_bool::*; 66 | 67 | mod array_u8; 68 | pub use array_u8::*; 69 | 70 | mod array_u16; 71 | pub use array_u16::*; 72 | 73 | mod array_u32; 74 | pub use array_u32::*; 75 | 76 | mod array_u64; 77 | pub use array_u64::*; 78 | 79 | mod array_u128; 80 | pub use array_u128::*; 81 | 82 | mod array_i8; 83 | pub use array_i8::*; 84 | 85 | mod array_i16; 86 | pub use array_i16::*; 87 | 88 | mod array_i32; 89 | pub use array_i32::*; 90 | 91 | mod array_i64; 92 | pub use array_i64::*; 93 | 94 | mod array_i128; 95 | pub use array_i128::*; 96 | 97 | #[cfg(feature = "serde")] 98 | mod array_iteratable; 99 | #[cfg(feature = "serde")] 100 | pub(crate) use array_iteratable::*; 101 | 102 | mod array_f16; 103 | pub use array_f16::*; 104 | 105 | mod array_f32; 106 | pub use array_f32::*; 107 | 108 | mod array_f64; 109 | pub use array_f64::*; 110 | 111 | mod array_f128; 112 | pub use array_f128::*; 113 | -------------------------------------------------------------------------------- /src/verbose/values/raw_f128.rs: -------------------------------------------------------------------------------- 1 | /// A 128 bit floating point number stored in "raw". 2 | /// 3 | /// This is needed as Rust does not support (and most systems) 4 | /// don't support 128 bit floating point values. 5 | #[derive(Copy, Clone, Debug, PartialEq)] 6 | pub struct RawF128(u128); 7 | 8 | impl RawF128 { 9 | /// Create a floating point value from its representation as a 10 | /// byte array in big endian. 11 | #[inline] 12 | pub const fn from_be_bytes(bytes: [u8; 16]) -> RawF128 { 13 | RawF128(u128::from_be_bytes(bytes)) 14 | } 15 | 16 | /// Create a floating point value from its representation as a 17 | /// byte array in little endian. 18 | #[inline] 19 | pub const fn from_le_bytes(bytes: [u8; 16]) -> RawF128 { 20 | RawF128(u128::from_le_bytes(bytes)) 21 | } 22 | 23 | /// Create a floating point value from its representation as a byte 24 | /// array in native endian. 25 | #[inline] 26 | pub const fn from_ne_bytes(bytes: [u8; 16]) -> RawF128 { 27 | RawF128(u128::from_ne_bytes(bytes)) 28 | } 29 | 30 | /// Return the memory representation of this floating point number 31 | /// as a byte array in big-endian (network) byte order. 32 | #[inline] 33 | pub const fn to_be_bytes(self) -> [u8; 16] { 34 | self.0.to_be_bytes() 35 | } 36 | 37 | /// Return the memory representation of this floating point number 38 | /// as a byte array in little-endian byte order 39 | #[inline] 40 | pub const fn to_le_bytes(self) -> [u8; 16] { 41 | self.0.to_le_bytes() 42 | } 43 | 44 | /// Return the memory representation of this floating point number as 45 | /// a byte array in native byte order. 46 | #[inline] 47 | pub const fn to_ne_bytes(self) -> [u8; 16] { 48 | self.0.to_ne_bytes() 49 | } 50 | 51 | /// Raw transmutation from `u128`. 52 | #[inline] 53 | pub const fn from_bits(bits: u128) -> RawF128 { 54 | RawF128(bits) 55 | } 56 | 57 | /// Raw transmutation to `u1128`. 58 | #[inline] 59 | pub const fn to_bits(self) -> u128 { 60 | self.0 61 | } 62 | } 63 | 64 | #[cfg(feature = "serde")] 65 | impl serde::Serialize for RawF128 { 66 | fn serialize(&self, serializer: S) -> Result 67 | where 68 | S: serde::Serializer, 69 | { 70 | serializer.serialize_u128(self.0) 71 | } 72 | } 73 | 74 | #[cfg(test)] 75 | mod tests { 76 | use super::*; 77 | use proptest::prelude::*; 78 | 79 | proptest! { 80 | #[test] 81 | fn debug_clone_eq(value in any::()) { 82 | use alloc::format; 83 | 84 | assert_eq!( 85 | format!("{:?}", RawF128(value)), 86 | format!("{:?}", RawF128(value)) 87 | ); 88 | assert_eq!(RawF128(value), RawF128(value).clone()); 89 | } 90 | } 91 | 92 | proptest! { 93 | #[test] 94 | fn from_be_bytes(value in any::()) { 95 | assert_eq!( 96 | value, 97 | RawF128::from_be_bytes(value.to_be_bytes()).0 98 | ); 99 | } 100 | } 101 | 102 | proptest! { 103 | #[test] 104 | fn from_le_bytes(value in any::()) { 105 | assert_eq!( 106 | value, 107 | RawF128::from_le_bytes(value.to_le_bytes()).0 108 | ); 109 | } 110 | } 111 | 112 | proptest! { 113 | #[test] 114 | fn from_ne_bytes(value in any::()) { 115 | assert_eq!( 116 | value, 117 | RawF128::from_ne_bytes(value.to_ne_bytes()).0 118 | ); 119 | } 120 | } 121 | 122 | proptest! { 123 | #[test] 124 | fn to_be_bytes(value in any::()) { 125 | assert_eq!( 126 | value.to_be_bytes(), 127 | RawF128(value).to_be_bytes() 128 | ); 129 | } 130 | } 131 | 132 | proptest! { 133 | #[test] 134 | fn to_le_bytes(value in any::()) { 135 | assert_eq!( 136 | value.to_le_bytes(), 137 | RawF128(value).to_le_bytes() 138 | ); 139 | } 140 | } 141 | 142 | proptest! { 143 | #[test] 144 | fn to_ne_bytes(value in any::()) { 145 | assert_eq!( 146 | value.to_ne_bytes(), 147 | RawF128(value).to_ne_bytes() 148 | ); 149 | } 150 | } 151 | 152 | proptest! { 153 | #[test] 154 | fn from_bits(value in any::()) { 155 | assert_eq!( 156 | value, 157 | RawF128::from_bits(value).0 158 | ); 159 | } 160 | } 161 | 162 | proptest! { 163 | #[test] 164 | fn to_bits(value in any::()) { 165 | assert_eq!( 166 | value, 167 | RawF128(value).to_bits() 168 | ); 169 | } 170 | } 171 | 172 | #[cfg(feature = "serde")] 173 | proptest! { 174 | #[test] 175 | fn serialize(value in any::()) { 176 | let v = RawF128(value); 177 | assert_eq!( 178 | serde_json::to_string(&v.to_bits()).unwrap(), 179 | serde_json::to_string(&v).unwrap() 180 | ); 181 | } 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /src/verbose/values/raw_f16.rs: -------------------------------------------------------------------------------- 1 | /// A 16 bit floating point number stored in "raw". 2 | /// 3 | /// This is needed as Rust does not support (and most systems) 4 | /// don't support 16 bit floating point values. 5 | #[derive(Copy, Clone, Debug, PartialEq)] 6 | pub struct RawF16(u16); 7 | 8 | impl RawF16 { 9 | const SIGN_MASK: u16 = 0b1000_0000_0000_0000; 10 | const EXPO_MASK: u16 = 0b0111_1100_0000_0000; 11 | const FRAC_MASK: u16 = 0b0000_0011_1111_1111; 12 | 13 | pub const ZERO: RawF16 = RawF16::from_bits(0); 14 | pub const ONE: RawF16 = RawF16::from_bits(0b0011_1100_0000_0000); 15 | pub const NAN: RawF16 = RawF16::from_bits(0b0111_1100_0000_0001); 16 | pub const INFINITY: RawF16 = RawF16::from_bits(0b0111_1100_0000_0000); 17 | pub const NEGATIVE_INFINITY: RawF16 = RawF16::from_bits(0b1111_1100_0000_0000); 18 | 19 | /// Converts the f16 to a f32. 20 | #[inline] 21 | pub fn to_f32(self) -> f32 { 22 | let raw_u16 = self.0; 23 | // extract elements & re-shift to f32 24 | // 25 | // f16 26 | // * 10 bits fraction 27 | // * 5 bits exponent 28 | // * 1 bit sign bit 29 | // f32 30 | // * 23 bits fraction 31 | // * 8 bits exponent 32 | // * 1 bit sign bit 33 | let sign = ((raw_u16 & RawF16::SIGN_MASK) as u32) << (31 - 15); 34 | let masked_expo = raw_u16 & RawF16::EXPO_MASK; 35 | let expo = if RawF16::EXPO_MASK == masked_expo { 36 | // max has to be handled specially (as it represents infinity or NaN) 37 | 0b0111_1111_1000_0000_0000_0000_0000_0000 38 | } else if masked_expo == 0 { 39 | 0 40 | } else { 41 | // to get the to the exponent substract 0b01111 42 | let decoded_expo = i32::from((raw_u16 & RawF16::EXPO_MASK) >> 10) - 0b01111; 43 | // and to get to the 32 bit encoding 0x7f needs to be added 44 | ((decoded_expo + 0x7F) as u32) << 23 45 | }; 46 | // shift by 13 as the bits start from the highest to the lowest 47 | let frac = ((raw_u16 & RawF16::FRAC_MASK) as u32) << 13; 48 | 49 | // recompose to u32 50 | f32::from_bits(sign | expo | frac) 51 | } 52 | 53 | /// Create a floating point value from its representation as a 54 | /// byte array in big endian. 55 | #[inline] 56 | pub const fn from_be_bytes(bytes: [u8; 2]) -> RawF16 { 57 | RawF16(u16::from_be_bytes(bytes)) 58 | } 59 | 60 | /// Create a floating point value from its representation as a 61 | /// byte array in little endian. 62 | #[inline] 63 | pub const fn from_le_bytes(bytes: [u8; 2]) -> RawF16 { 64 | RawF16(u16::from_le_bytes(bytes)) 65 | } 66 | 67 | /// Create a floating point value from its representation as a byte 68 | /// array in native endian. 69 | #[inline] 70 | pub const fn from_ne_bytes(bytes: [u8; 2]) -> RawF16 { 71 | RawF16(u16::from_ne_bytes(bytes)) 72 | } 73 | 74 | /// Return the memory representation of this floating point number 75 | /// as a byte array in big-endian (network) byte order. 76 | #[inline] 77 | pub const fn to_be_bytes(self) -> [u8; 2] { 78 | self.0.to_be_bytes() 79 | } 80 | 81 | /// Return the memory representation of this floating point number 82 | /// as a byte array in little-endian byte order 83 | #[inline] 84 | pub const fn to_le_bytes(self) -> [u8; 2] { 85 | self.0.to_le_bytes() 86 | } 87 | 88 | /// Return the memory representation of this floating point number as 89 | /// a byte array in native byte order. 90 | #[inline] 91 | pub const fn to_ne_bytes(self) -> [u8; 2] { 92 | self.0.to_ne_bytes() 93 | } 94 | 95 | /// Raw transmutation from `u16`. 96 | #[inline] 97 | pub const fn from_bits(bits: u16) -> RawF16 { 98 | RawF16(bits) 99 | } 100 | 101 | /// Raw transmutation to `u16`. 102 | #[inline] 103 | pub const fn to_bits(self) -> u16 { 104 | self.0 105 | } 106 | } 107 | 108 | impl From for f32 { 109 | #[inline] 110 | fn from(value: RawF16) -> Self { 111 | value.to_f32() 112 | } 113 | } 114 | 115 | #[cfg(feature = "serde")] 116 | impl serde::Serialize for RawF16 { 117 | fn serialize(&self, serializer: S) -> Result 118 | where 119 | S: serde::Serializer, 120 | { 121 | serializer.serialize_f32(self.to_f32()) 122 | } 123 | } 124 | 125 | #[cfg(test)] 126 | mod tests { 127 | use super::*; 128 | use proptest::prelude::*; 129 | 130 | proptest! { 131 | #[test] 132 | fn debug_clone_eq(value in any::()) { 133 | use alloc::format; 134 | 135 | assert_eq!( 136 | format!("{:?}", RawF16(value)), 137 | format!("{:?}", RawF16(value)) 138 | ); 139 | assert_eq!(RawF16(value), RawF16(value).clone()); 140 | } 141 | } 142 | 143 | #[test] 144 | fn constant() { 145 | assert_eq!(0.0, RawF16::ZERO.to_f32()); 146 | assert_eq!(1.0, RawF16::ONE.to_f32()); 147 | assert!(RawF16::NAN.to_f32().is_nan()); 148 | assert!(RawF16::INFINITY.to_f32().is_infinite()); 149 | assert!(RawF16::NEGATIVE_INFINITY.to_f32().is_infinite()); 150 | } 151 | 152 | #[test] 153 | fn to_f32() { 154 | // zero 155 | assert_eq!(0.0, RawF16(0).to_f32()); 156 | 157 | // one 158 | assert_eq!(1.0, RawF16::from_bits(0b0_01111_0000000000).to_f32()); 159 | 160 | // infinite 161 | assert!(RawF16::from_bits(0b0111_1100_0000_0000) 162 | .to_f32() 163 | .is_infinite()); 164 | 165 | // nan 166 | assert!(RawF16::from_bits(0b0111_1100_0000_0001).to_f32().is_nan()); 167 | 168 | // largest normal number 169 | assert_eq!(65504.0, RawF16::from_bits(0b0111_1011_1111_1111).to_f32()); 170 | assert_eq!(-65504.0, RawF16::from_bits(0b1111_1011_1111_1111).to_f32()); 171 | } 172 | 173 | proptest! { 174 | #[test] 175 | fn from_be_bytes(value in any::()) { 176 | assert_eq!( 177 | value, 178 | RawF16::from_be_bytes(value.to_be_bytes()).0 179 | ); 180 | } 181 | } 182 | 183 | proptest! { 184 | #[test] 185 | fn from_le_bytes(value in any::()) { 186 | assert_eq!( 187 | value, 188 | RawF16::from_le_bytes(value.to_le_bytes()).0 189 | ); 190 | } 191 | } 192 | 193 | proptest! { 194 | #[test] 195 | fn from_ne_bytes(value in any::()) { 196 | assert_eq!( 197 | value, 198 | RawF16::from_ne_bytes(value.to_ne_bytes()).0 199 | ); 200 | } 201 | } 202 | 203 | proptest! { 204 | #[test] 205 | fn to_be_bytes(value in any::()) { 206 | assert_eq!( 207 | value.to_be_bytes(), 208 | RawF16(value).to_be_bytes() 209 | ); 210 | } 211 | } 212 | 213 | proptest! { 214 | #[test] 215 | fn to_le_bytes(value in any::()) { 216 | assert_eq!( 217 | value.to_le_bytes(), 218 | RawF16(value).to_le_bytes() 219 | ); 220 | } 221 | } 222 | 223 | proptest! { 224 | #[test] 225 | fn to_ne_bytes(value in any::()) { 226 | assert_eq!( 227 | value.to_ne_bytes(), 228 | RawF16(value).to_ne_bytes() 229 | ); 230 | } 231 | } 232 | 233 | proptest! { 234 | #[test] 235 | fn from_bits(value in any::()) { 236 | assert_eq!( 237 | value, 238 | RawF16::from_bits(value).0 239 | ); 240 | } 241 | } 242 | 243 | proptest! { 244 | #[test] 245 | fn to_bits(value in any::()) { 246 | assert_eq!( 247 | value, 248 | RawF16(value).to_bits() 249 | ); 250 | } 251 | } 252 | 253 | proptest! { 254 | #[test] 255 | fn from_f16_to_f32(value in any::()) { 256 | let v = RawF16(value); 257 | let actual: f32 = v.into(); 258 | if actual.is_nan() { 259 | assert!(v.to_f32().is_nan()); 260 | } else { 261 | assert_eq!(actual, v.to_f32()); 262 | } 263 | } 264 | } 265 | 266 | #[cfg(feature = "serde")] 267 | proptest! { 268 | #[test] 269 | fn serialize(value in any::()) { 270 | let v = RawF16(value); 271 | assert_eq!( 272 | serde_json::to_string(&v.to_f32()).unwrap(), 273 | serde_json::to_string(&v).unwrap() 274 | ); 275 | } 276 | } 277 | } 278 | -------------------------------------------------------------------------------- /src/verbose/values/raw_value.rs: -------------------------------------------------------------------------------- 1 | use arrayvec::{ArrayVec, CapacityError}; 2 | 3 | #[derive(Debug, Eq, PartialEq, Clone)] 4 | #[cfg_attr(feature = "serde", derive(serde::Serialize))] 5 | pub struct RawValue<'a> { 6 | pub name: Option<&'a str>, 7 | pub data: &'a [u8], 8 | } 9 | 10 | impl<'a> RawValue<'a> { 11 | /// Adds the verbose value to the given dlt mesage buffer. 12 | pub fn add_to_msg( 13 | &self, 14 | buf: &mut ArrayVec, 15 | is_big_endian: bool, 16 | ) -> Result<(), CapacityError> { 17 | if let Some(name) = self.name { 18 | let type_info = [0b0000_0000, 0b0000_1100, 0b0000_0000, 0b0000_0000]; 19 | let (data_len, name_len) = if is_big_endian { 20 | ( 21 | (self.data.len() as u16).to_be_bytes(), 22 | (name.len() as u16 + 1).to_be_bytes(), 23 | ) 24 | } else { 25 | ( 26 | (self.data.len() as u16).to_le_bytes(), 27 | (name.len() as u16 + 1).to_le_bytes(), 28 | ) 29 | }; 30 | buf.try_extend_from_slice(&type_info)?; 31 | buf.try_extend_from_slice(&[data_len[0], data_len[1], name_len[0], name_len[1]])?; 32 | buf.try_extend_from_slice(name.as_bytes())?; 33 | if buf.remaining_capacity() > 0 { 34 | // Safe as capacity is checked earlier 35 | unsafe { buf.push_unchecked(0) }; 36 | } else { 37 | return Err(CapacityError::new(())); 38 | } 39 | } else { 40 | let type_info = [0b0000_0000, 0b0000_0100, 0b0000_0000, 0b0000_0000]; 41 | let data_len = if is_big_endian { 42 | (self.data.len() as u16).to_be_bytes() 43 | } else { 44 | (self.data.len() as u16).to_le_bytes() 45 | }; 46 | buf.try_extend_from_slice(&type_info)?; 47 | buf.try_extend_from_slice(&[data_len[0], data_len[1]])?; 48 | } 49 | 50 | buf.try_extend_from_slice(self.data)?; 51 | 52 | Ok(()) 53 | } 54 | } 55 | 56 | #[cfg(test)] 57 | mod test { 58 | use super::*; 59 | use crate::verbose::VerboseValue; 60 | use crate::verbose::VerboseValue::Raw; 61 | use alloc::vec::Vec; 62 | use proptest::prelude::*; 63 | use std::format; 64 | 65 | proptest! { 66 | #[test] 67 | fn write_read(ref data in "\\pc{0,80}", ref name in "\\pc{0,20}") { 68 | const MAX_SYMBOL_LENGTH_NAME: usize = 20; 69 | const MAX_SYMBOL_LENGTH_VALUE: usize = 80; 70 | const BYTES_NEEDED: usize = 6; 71 | const BYTES_NEEDED_WITH_NAME: usize = 3 + BYTES_NEEDED; 72 | 73 | const BUFFER_SIZE: usize = MAX_SYMBOL_LENGTH_NAME * 4 + MAX_SYMBOL_LENGTH_VALUE * 4 + BYTES_NEEDED_WITH_NAME; 74 | 75 | let data = data.as_bytes(); 76 | // test big endian with name 77 | { 78 | 79 | let mut msg_buff: ArrayVec = ArrayVec::new(); 80 | let slice_len = name.len() + data.len() + BYTES_NEEDED_WITH_NAME; 81 | let is_big_endian = true; 82 | 83 | let raw_value = RawValue {name: Some(name), data}; 84 | let mut content_buff = Vec::with_capacity(slice_len); 85 | 86 | let len_name_be = (name.len() as u16 + 1).to_be_bytes(); 87 | let len_value_be = (data.len() as u16).to_be_bytes(); 88 | 89 | prop_assert_eq!(raw_value.add_to_msg(&mut msg_buff, is_big_endian), Ok(())); 90 | 91 | content_buff.extend_from_slice(&[0b0000_0000, 0b0000_1100, 0b0000_0000, 0b0000_0000, len_value_be[0], len_value_be[1], len_name_be[0], len_name_be[1]]); 92 | content_buff.extend_from_slice(name.as_bytes()); 93 | content_buff.push(0); 94 | content_buff.extend_from_slice(data); 95 | prop_assert_eq!(&msg_buff[..slice_len], &content_buff[..]); 96 | 97 | // Now wrap back 98 | let parsed_back = VerboseValue::from_slice(&msg_buff, is_big_endian); 99 | prop_assert_eq!(parsed_back, Ok((Raw(raw_value),&[] as &[u8]))); 100 | } 101 | 102 | // test little endian with name 103 | { 104 | 105 | let mut msg_buff: ArrayVec = ArrayVec::new(); 106 | let slice_len = name.len() + data.len() + BYTES_NEEDED_WITH_NAME; 107 | let is_big_endian = false; 108 | 109 | let raw_value = RawValue {name: Some(name), data}; 110 | let mut content_buff = Vec::with_capacity(slice_len); 111 | 112 | let len_name_le = (name.len() as u16 + 1).to_le_bytes(); 113 | let len_value_le = (data.len() as u16).to_le_bytes(); 114 | 115 | prop_assert_eq!(raw_value.add_to_msg(&mut msg_buff, is_big_endian), Ok(())); 116 | 117 | content_buff.extend_from_slice(&[0b0000_0000, 0b0000_1100, 0b0000_0000, 0b0000_0000, len_value_le[0], len_value_le[1], len_name_le[0], len_name_le[1]]); 118 | content_buff.extend_from_slice(name.as_bytes()); 119 | content_buff.push(0); 120 | content_buff.extend_from_slice(data); 121 | prop_assert_eq!(&msg_buff[..slice_len], &content_buff[..]); 122 | 123 | // Now wrap back 124 | let parsed_back = VerboseValue::from_slice(&msg_buff, is_big_endian); 125 | prop_assert_eq!(parsed_back, Ok((Raw(raw_value),&[] as &[u8]))); 126 | } 127 | 128 | // test big endian without name 129 | { 130 | 131 | let mut msg_buff: ArrayVec = ArrayVec::new(); 132 | let slice_len = data.len() + BYTES_NEEDED; 133 | let is_big_endian = true; 134 | 135 | let raw_value = RawValue {name: None, data}; 136 | let mut content_buff = Vec::with_capacity(slice_len); 137 | 138 | let len_value_be = (data.len() as u16).to_be_bytes(); 139 | 140 | prop_assert_eq!(raw_value.add_to_msg(&mut msg_buff, is_big_endian), Ok(())); 141 | 142 | content_buff.extend_from_slice(&[0b0000_0000, 0b0000_0100, 0b0000_0000, 0b0000_0000, len_value_be[0], len_value_be[1]]); 143 | content_buff.extend_from_slice(data); 144 | prop_assert_eq!(&msg_buff[..slice_len], &content_buff[..]); 145 | 146 | // Now wrap back 147 | let parsed_back = VerboseValue::from_slice(&msg_buff, is_big_endian); 148 | prop_assert_eq!(parsed_back, Ok((Raw(raw_value),&[] as &[u8]))); 149 | } 150 | 151 | // test little endian without name 152 | { 153 | 154 | let mut msg_buff: ArrayVec = ArrayVec::new(); 155 | let slice_len = data.len() + BYTES_NEEDED; 156 | let is_big_endian = false; 157 | 158 | let raw_value = RawValue {name: None, data}; 159 | let mut content_buff = Vec::with_capacity(slice_len); 160 | 161 | let len_value_le = (data.len() as u16).to_le_bytes(); 162 | 163 | prop_assert_eq!(raw_value.add_to_msg(&mut msg_buff, is_big_endian), Ok(())); 164 | 165 | content_buff.extend_from_slice(&[0b0000_0000, 0b0000_0100, 0b0000_0000, 0b0000_0000, len_value_le[0], len_value_le[1]]); 166 | content_buff.extend_from_slice(data); 167 | prop_assert_eq!(&msg_buff[..slice_len], &content_buff[..]); 168 | 169 | // Now wrap back 170 | let parsed_back = VerboseValue::from_slice(&msg_buff, is_big_endian); 171 | prop_assert_eq!(parsed_back, Ok((Raw(raw_value),&[] as &[u8]))); 172 | } 173 | 174 | // Capacity error big endian with name 175 | { 176 | const SLICE_LEN: usize = BYTES_NEEDED_WITH_NAME-1; 177 | 178 | let raw_value = RawValue {name: Some(name), data}; 179 | let is_big_endian = true; 180 | 181 | let mut msg_buff: ArrayVec = ArrayVec::new(); 182 | prop_assert_eq!(raw_value.add_to_msg(&mut msg_buff, is_big_endian), Err(CapacityError::new(()))); 183 | 184 | let mut msg_buff: ArrayVec = ArrayVec::new(); 185 | prop_assert_eq!(raw_value.add_to_msg(&mut msg_buff, is_big_endian), Err(CapacityError::new(()))); 186 | 187 | } 188 | 189 | // Capacity error little endian with name 190 | { 191 | const SLICE_LEN: usize = BYTES_NEEDED_WITH_NAME-1; 192 | 193 | let raw_value = RawValue {name: Some(name), data}; 194 | let is_big_endian = false; 195 | 196 | let mut msg_buff: ArrayVec = ArrayVec::new(); 197 | prop_assert_eq!(raw_value.add_to_msg(&mut msg_buff, is_big_endian), Err(CapacityError::new(()))); 198 | 199 | let mut msg_buff: ArrayVec = ArrayVec::new(); 200 | prop_assert_eq!(raw_value.add_to_msg(&mut msg_buff, is_big_endian), Err(CapacityError::new(()))); 201 | 202 | } 203 | 204 | // Capacity error big endian without name 205 | { 206 | const SLICE_LEN: usize = BYTES_NEEDED - 1; 207 | 208 | let raw_value = RawValue {name: None, data}; 209 | let is_big_endian = true; 210 | 211 | let mut msg_buff: ArrayVec = ArrayVec::new(); 212 | prop_assert_eq!(raw_value.add_to_msg(&mut msg_buff, is_big_endian), Err(CapacityError::new(()))); 213 | 214 | let mut msg_buff: ArrayVec = ArrayVec::new(); 215 | prop_assert_eq!(raw_value.add_to_msg(&mut msg_buff, is_big_endian), Err(CapacityError::new(()))); 216 | 217 | } 218 | 219 | // Capacity error little endian without name 220 | { 221 | const SLICE_LEN: usize = BYTES_NEEDED - 1; 222 | 223 | let raw_value = RawValue {name: None, data}; 224 | let is_big_endian = true; 225 | 226 | let mut msg_buff: ArrayVec = ArrayVec::new(); 227 | prop_assert_eq!(raw_value.add_to_msg(&mut msg_buff, is_big_endian), Err(CapacityError::new(()))); 228 | 229 | let mut msg_buff: ArrayVec = ArrayVec::new(); 230 | prop_assert_eq!(raw_value.add_to_msg(&mut msg_buff, is_big_endian), Err(CapacityError::new(()))); 231 | 232 | } 233 | 234 | 235 | } 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /src/verbose/values/trace_info_value.rs: -------------------------------------------------------------------------------- 1 | use arrayvec::{ArrayVec, CapacityError}; 2 | 3 | #[derive(Debug, Eq, PartialEq, Clone)] 4 | #[cfg_attr(feature = "serde", derive(serde::Serialize))] 5 | pub struct TraceInfoValue<'a> { 6 | pub value: &'a str, 7 | } 8 | 9 | impl<'a> TraceInfoValue<'a> { 10 | /// Adds the verbose value to the given dlt mesage buffer. 11 | pub fn add_to_msg( 12 | &self, 13 | buf: &mut ArrayVec, 14 | is_big_endian: bool, 15 | ) -> Result<(), CapacityError> { 16 | let type_info = [0b0000_0000, 0b0010_0000, 0b0000_0000, 0b0000_0000]; 17 | let value_len = if is_big_endian { 18 | (self.value.len() as u16 + 1).to_be_bytes() 19 | } else { 20 | (self.value.len() as u16 + 1).to_le_bytes() 21 | }; 22 | buf.try_extend_from_slice(&type_info)?; 23 | buf.try_extend_from_slice(&[value_len[0], value_len[1]])?; 24 | 25 | buf.try_extend_from_slice(self.value.as_bytes())?; 26 | if buf.remaining_capacity() > 0 { 27 | // Safe as capacity is checked earlier 28 | unsafe { buf.push_unchecked(0) }; 29 | } else { 30 | return Err(CapacityError::new(())); 31 | } 32 | 33 | Ok(()) 34 | } 35 | } 36 | 37 | #[cfg(test)] 38 | mod test { 39 | use super::*; 40 | use crate::verbose::VerboseValue; 41 | use crate::verbose::VerboseValue::TraceInfo; 42 | use alloc::vec::Vec; 43 | use proptest::prelude::*; 44 | use std::format; 45 | 46 | proptest! { 47 | #[test] 48 | fn write_read(ref value in "\\pc{0,80}") { 49 | const MAX_SYMBOL_LENGTH_VALUE: usize = 80; 50 | const BYTES_NEEDED: usize = 7; 51 | 52 | const BUFFER_SIZE: usize = MAX_SYMBOL_LENGTH_VALUE * 4 + BYTES_NEEDED; 53 | 54 | 55 | // test big endian 56 | { 57 | 58 | let mut msg_buff: ArrayVec = ArrayVec::new(); 59 | let slice_len = value.len() + BYTES_NEEDED; 60 | let is_big_endian = true; 61 | 62 | let trace_value = TraceInfoValue {value}; 63 | let mut content_buff = Vec::with_capacity(slice_len); 64 | 65 | let len_value_be = (value.len() as u16 + 1).to_be_bytes(); 66 | 67 | prop_assert_eq!(trace_value.add_to_msg(&mut msg_buff, is_big_endian), Ok(())); 68 | 69 | content_buff.extend_from_slice(&[0b0000_0000, 0b0010_0000, 0b0000_0000, 0b0000_0000, len_value_be[0], len_value_be[1]]); 70 | content_buff.extend_from_slice(&value.as_bytes()); 71 | content_buff.push(0); 72 | prop_assert_eq!(&msg_buff[..slice_len], &content_buff[..]); 73 | 74 | // Now wrap back 75 | let parsed_back = VerboseValue::from_slice(&msg_buff, is_big_endian); 76 | prop_assert_eq!(parsed_back, Ok((TraceInfo (trace_value),&[] as &[u8]))); 77 | } 78 | 79 | // test little endian 80 | { 81 | 82 | let mut msg_buff: ArrayVec = ArrayVec::new(); 83 | let slice_len = value.len() + BYTES_NEEDED; 84 | let is_big_endian = false; 85 | 86 | let trace_value = TraceInfoValue {value}; 87 | let mut content_buff = Vec::with_capacity(slice_len); 88 | 89 | let len_value_le = (value.len() as u16 + 1).to_le_bytes(); 90 | 91 | prop_assert_eq!(trace_value.add_to_msg(&mut msg_buff, is_big_endian), Ok(())); 92 | 93 | content_buff.extend_from_slice(&[0b0000_0000, 0b0010_0000, 0b0000_0000, 0b0000_0000, len_value_le[0], len_value_le[1]]); 94 | content_buff.extend_from_slice(&value.as_bytes()); 95 | content_buff.push(0); 96 | prop_assert_eq!(&msg_buff[..slice_len], &content_buff[..]); 97 | 98 | // Now wrap back 99 | let parsed_back = VerboseValue::from_slice(&msg_buff, is_big_endian); 100 | prop_assert_eq!(parsed_back, Ok((TraceInfo(trace_value),&[] as &[u8]))); 101 | } 102 | 103 | 104 | // Capacity error big endian 105 | { 106 | const SLICE_LEN: usize = BYTES_NEEDED - 1; 107 | 108 | let trace_value = TraceInfoValue {value}; 109 | let is_big_endian = true; 110 | 111 | let mut msg_buff: ArrayVec = ArrayVec::new(); 112 | prop_assert_eq!(trace_value.add_to_msg(&mut msg_buff, is_big_endian), Err(CapacityError::new(()))); 113 | 114 | let mut msg_buff: ArrayVec = ArrayVec::new(); 115 | prop_assert_eq!(trace_value.add_to_msg(&mut msg_buff, is_big_endian), Err(CapacityError::new(()))); 116 | 117 | } 118 | 119 | // Capacity error little endian 120 | { 121 | const SLICE_LEN: usize = BYTES_NEEDED - 1; 122 | 123 | let trace_value = TraceInfoValue {value}; 124 | let is_big_endian = true; 125 | 126 | let mut msg_buff: ArrayVec = ArrayVec::new(); 127 | prop_assert_eq!(trace_value.add_to_msg(&mut msg_buff, is_big_endian), Err(CapacityError::new(()))); 128 | 129 | let mut msg_buff: ArrayVec = ArrayVec::new(); 130 | prop_assert_eq!(trace_value.add_to_msg(&mut msg_buff, is_big_endian), Err(CapacityError::new(()))); 131 | 132 | } 133 | 134 | 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/verbose/verbose_iter.rs: -------------------------------------------------------------------------------- 1 | use super::VerboseValue; 2 | use crate::error::VerboseDecodeError; 3 | 4 | /// Iterator over verbose values. 5 | #[derive(Debug, Clone, Eq, PartialEq, Hash)] 6 | pub struct VerboseIter<'a> { 7 | is_big_endian: bool, 8 | number_of_arguments: u16, 9 | rest: &'a [u8], 10 | } 11 | 12 | impl<'a> VerboseIter<'a> { 13 | /// Creates new iterator to iterate over the verbose values of a dlt messages. 14 | #[inline] 15 | pub fn new( 16 | is_big_endian: bool, 17 | number_of_arguments: u16, 18 | payload: &'a [u8], 19 | ) -> VerboseIter<'a> { 20 | VerboseIter { 21 | is_big_endian, 22 | number_of_arguments, 23 | rest: payload, 24 | } 25 | } 26 | 27 | /// Returns if the values encoded in the big endian format. 28 | #[inline] 29 | pub fn is_big_endian(&self) -> bool { 30 | self.is_big_endian 31 | } 32 | 33 | /// Number of arguments left in the iterator. 34 | #[inline] 35 | pub fn number_of_arguments(&self) -> u16 { 36 | self.number_of_arguments 37 | } 38 | 39 | /// Raw data. 40 | #[inline] 41 | pub fn raw(&self) -> &'a [u8] { 42 | self.rest 43 | } 44 | } 45 | 46 | impl<'a> core::iter::Iterator for VerboseIter<'a> { 47 | type Item = Result, VerboseDecodeError>; 48 | 49 | fn next(&mut self) -> Option { 50 | if self.number_of_arguments == 0 { 51 | None 52 | } else { 53 | match VerboseValue::from_slice(self.rest, self.is_big_endian) { 54 | Ok((value, rest)) => { 55 | self.rest = rest; 56 | self.number_of_arguments -= 1; 57 | Some(Ok(value)) 58 | } 59 | Err(err) => { 60 | // move to end in case of error so we end the iteration 61 | self.rest = &self.rest[self.rest.len()..]; 62 | self.number_of_arguments = 0; 63 | Some(Err(err)) 64 | } 65 | } 66 | } 67 | } 68 | } 69 | 70 | #[cfg(test)] 71 | mod test { 72 | use super::VerboseIter; 73 | use crate::verbose::{U16Value, U32Value, VerboseValue}; 74 | use arrayvec::ArrayVec; 75 | 76 | #[test] 77 | fn new() { 78 | let data = [1, 2, 3, 4]; 79 | let actual = VerboseIter::new(true, 123, &data); 80 | assert!(actual.is_big_endian); 81 | assert_eq!(actual.number_of_arguments, 123); 82 | assert_eq!(actual.rest, &data); 83 | } 84 | 85 | #[test] 86 | fn next() { 87 | // empty 88 | { 89 | let data = [1, 2, 3, 4]; 90 | let mut iter = VerboseIter::new(false, 0, &data); 91 | assert_eq!(None, iter.next()); 92 | assert_eq!(None, iter.next()); 93 | } 94 | // single value ok (big endian) 95 | { 96 | let mut data = ArrayVec::::new(); 97 | let value = U16Value { 98 | variable_info: None, 99 | scaling: None, 100 | value: 1234, 101 | }; 102 | value.add_to_msg(&mut data, true).unwrap(); 103 | 104 | let mut iter = VerboseIter::new(true, 1, &data); 105 | assert_eq!(Some(Ok(VerboseValue::U16(value))), iter.next()); 106 | assert_eq!(None, iter.next()); 107 | assert_eq!(None, iter.next()); 108 | } 109 | // two values ok (little endian) 110 | { 111 | let mut data = ArrayVec::::new(); 112 | let first_value = U16Value { 113 | variable_info: None, 114 | scaling: None, 115 | value: 1234, 116 | }; 117 | first_value.add_to_msg(&mut data, false).unwrap(); 118 | let second_value = U32Value { 119 | variable_info: None, 120 | scaling: None, 121 | value: 2345, 122 | }; 123 | second_value.add_to_msg(&mut data, false).unwrap(); 124 | 125 | let mut iter = VerboseIter::new(false, 2, &data); 126 | assert_eq!(Some(Ok(VerboseValue::U16(first_value))), iter.next()); 127 | assert_eq!(Some(Ok(VerboseValue::U32(second_value))), iter.next()); 128 | assert_eq!(None, iter.next()); 129 | assert_eq!(None, iter.next()); 130 | } 131 | // more values present then number of arguments 132 | { 133 | let mut data = ArrayVec::::new(); 134 | let first_value = U16Value { 135 | variable_info: None, 136 | scaling: None, 137 | value: 1234, 138 | }; 139 | first_value.add_to_msg(&mut data, false).unwrap(); 140 | let second_value = U32Value { 141 | variable_info: None, 142 | scaling: None, 143 | value: 2345, 144 | }; 145 | second_value.add_to_msg(&mut data, false).unwrap(); 146 | 147 | let mut iter = VerboseIter::new(false, 1, &data); 148 | assert_eq!(Some(Ok(VerboseValue::U16(first_value))), iter.next()); 149 | assert_eq!(None, iter.next()); 150 | assert_eq!(None, iter.next()); 151 | } 152 | // number of arguments bigger then present data 153 | { 154 | let mut data = ArrayVec::::new(); 155 | let first_value = U16Value { 156 | variable_info: None, 157 | scaling: None, 158 | value: 1234, 159 | }; 160 | first_value.add_to_msg(&mut data, false).unwrap(); 161 | let second_value = U32Value { 162 | variable_info: None, 163 | scaling: None, 164 | value: 2345, 165 | }; 166 | second_value.add_to_msg(&mut data, false).unwrap(); 167 | 168 | let mut iter = VerboseIter::new(false, 3, &data); 169 | assert_eq!(Some(Ok(VerboseValue::U16(first_value))), iter.next()); 170 | assert_eq!(Some(Ok(VerboseValue::U32(second_value))), iter.next()); 171 | assert!(iter.next().unwrap().is_err()); 172 | assert_eq!(None, iter.next()); 173 | } 174 | } 175 | } 176 | --------------------------------------------------------------------------------