1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
// This module defines traits to specifiy a key as defined in DDS specification.
// See e.g. Figure 2.3 in "2.2.1.2.2 Overall Conceptual Model"
use std::{convert::TryFrom, hash::Hash};
use byteorder::BigEndian;
use rand::Rng;
use log::error;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
pub use cdr_encoding_size::*;
use crate::serialization::{cdr_serializer::to_bytes, error::Error};
/// Data sample must implement [`Keyed`] to be used in a WITH_KEY topic.
///
/// It allows a Key to be extracted from the
/// sample. In its simplest form, the key may be just a part of the sample data,
/// but it can be anything computable from an immutable sample by an
/// application-defined function. It is recommended that this function be
/// lightwieght to compute.
///
/// The key is used to distinguish between different Instances of the data in a
/// DDS Topic.
///
/// A `Keyed` type has an associated type `K`, which is the corresponding key
/// type. `K` must implement [`Key`]. Otherwise, `K` can be chosen to suit the
/// application. It is advisable that `K` is something that can be cloned with
/// reasonable effort.
///
/// [`Key`]: trait.Key.html
pub trait Keyed {
//type K: Key; // This does not work yet is stable Rust, 2020-08-11
// Instead, where D:Keyed we do anything with D::K, we must specify bound:
// where <D as Keyed>::K : Key,
type K;
fn key(&self) -> Self::K;
}
// See RTPS spec Section 8.7.10 Key Hash
// and Section 9.6.3.8 KeyHash
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Copy)]
pub struct KeyHash([u8; 16]);
impl KeyHash {
pub fn zero() -> Self {
Self([0; 16])
}
pub fn to_vec(self) -> Vec<u8> {
Vec::from(self.0)
}
pub fn into_cdr_bytes(self) -> Result<Vec<u8>, Error> {
Ok(self.to_vec())
}
pub fn from_cdr_bytes(bytes: Vec<u8>) -> Result<Self, Error> {
let a = <[u8; 16]>::try_from(bytes).map_err(|_e| Error::Eof)?;
Ok(Self(a))
}
}
/// Trait for instance lookup key in a WITH_KEY topic.
///
/// The corresponding data sample type must implement [`Keyed`].
/// If the topic is NO_KEY, both of these can be ignored.
///
/// It is a combination of traits from the standard library
/// * [PartialEq](https://doc.rust-lang.org/std/cmp/trait.PartialEq.html)
/// * [Eq](https://doc.rust-lang.org/std/cmp/trait.Eq.html)
/// * [PartialOrd](https://doc.rust-lang.org/std/cmp/trait.PartialOrd.html)
/// * [Ord](https://doc.rust-lang.org/std/cmp/trait.Ord.html)
/// * [Hash](https://doc.rust-lang.org/std/hash/trait.Hash.html)
/// * [Clone](https://doc.rust-lang.org/std/clone/trait.Clone.html)
///
/// and Serde traits
/// * [Serialize](https://docs.serde.rs/serde/trait.Serialize.html) and
/// * [DeserializeOwned](https://docs.serde.rs/serde/de/trait.DeserializeOwned.html)
///
/// and a RustDDS-specific trait
/// * [CdrEncodingSize] , for which we provide a [derive
/// macro](derive@cdr_encoding_size::CdrEncodingSize).
///
/// No other methods are required, so for many types it should be possible to
/// `#[derive]` all the prerequisite traits and implement as `impl Key for Foo
/// {}`. Consider also deriving [`Copy`] for your key, if the usual
/// preconditions are satisfied.
///
/// Note: When implementing Key, DeserializeOwned cannot and need not be
/// derived, as it is a type alias. Derive (or implement) the [`Deserialize`]
/// trait instead.
/// # Example
/// ```
/// use rustdds::*;
/// use serde::{Serialize, Deserialize};
///
/// #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord,
/// Serialize, Deserialize, CdrEncodingSize)]
/// pub struct MyKey {
/// number: u32,
/// name: String,
/// }
///
/// impl Key for MyKey {}
/// ```
pub trait Key:
Eq + PartialEq + PartialOrd + Ord + Hash + Clone + Serialize + DeserializeOwned + CdrEncodingSize
{
// no methods required
// provided method:
fn hash_key(&self) -> KeyHash {
// See RTPS Spec v2.3 Section 9.6.3.8 KeyHash
/* The KeyHash_t is computed from the Data as follows using one of two algorithms depending on whether
the Data type is such that the maximum size of the sequential CDR encapsulation of
all the key fields is less than or equal to 128 bits (the size of the KeyHash_t).
• If the maximum size of the sequential CDR representation of all the key fields is less
than or equal to 128 bits, then the KeyHash_t shall be computed as the CDR Big-Endian
representation of all the Key fields in sequence. Any unfilled bits in the KeyHash_t
shall be set to zero.
• Otherwise the KeyHash_t shall be computed as a 128-bit MD5 Digest (IETF RFC 1321)
applied to the CDR Big- Endian representation of all the Key fields in sequence.
Note that the choice of the algorithm to use depends on the data-type,
not on any particular data value.
*/
// The specification calls for "sequential CDR representation of all the key
// fields" and "CDR Big- Endian representation of all the Key fields in
// sequence". We take this to mean the CDR encoding of the Key.
// (Does it include CDR-specified alignment padding too?)
//
let mut cdr_bytes = to_bytes::<Self, BigEndian>(self).unwrap_or_else(|e| {
error!("Hashing key {:?} failed!", e);
// This would cause a lot of hash collisions, but wht else we could do
// if the key cannot be serialized? Are there any realistic conditions
// this could even ocur?
vec![0; 16]
});
KeyHash(
if Self::cdr_encoding_max_size() > CdrEncodingMaxSize::Bytes(16) {
// use MD5 hash to get the hash. The MD5 hash is always exactly
// 16 bytes, so just deref it to [u8;16]
*md5::compute(&cdr_bytes)
} else {
cdr_bytes.resize(16, 0x00); // pad with zeros to get 16 bytes
<[u8; 16]>::try_from(cdr_bytes).unwrap() // this succeeds, because of
// the resize above
},
)
}
}
impl Key for () {
fn hash_key(&self) -> KeyHash {
KeyHash::zero()
}
}
/// Key for a reference type `&D` is the same as for the value type `D`.
/// This is required internally for the implementation of NoKey topics.
impl<D: Keyed> Keyed for &D {
type K = D::K;
fn key(&self) -> Self::K {
(*self).key()
}
}
// TODO: might want to implement this for each primitive?
impl Key for bool {}
impl Key for char {}
impl Key for i8 {}
impl Key for i16 {}
impl Key for i32 {}
impl Key for i64 {}
impl Key for i128 {}
//impl Key for isize {} // should not be used in serializable data, as size is
// platform-dependent
impl Key for u8 {}
impl Key for u16 {}
impl Key for u32 {}
impl Key for u64 {}
impl Key for u128 {}
//impl Key for usize {} // should not be used in serializable data, as size is
// platform-dependent
impl Key for String {}
#[derive(Debug, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize, CdrEncodingSize)]
/// Key type to identicy data instances in builtin topics
pub struct BuiltInTopicKey {
/// IDL PSM (2.3.3, pg 138) uses array of 3x long to implement this
value: [i32; 3],
}
impl BuiltInTopicKey {
pub fn random_key() -> Self {
let mut rng = rand::thread_rng();
Self {
value: [rng.gen(), rng.gen(), rng.gen()],
}
}
pub fn default() -> Self {
Self { value: [0, 0, 0] }
}
}