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] }
  }
}