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
// 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::collections::hash_map::DefaultHasher;
use std::convert::TryFrom;
use std::hash::{Hash, Hasher};
use byteorder::{BigEndian};
use rand::Rng;
use log::error;
use serde::{Serialize, Deserialize, de::DeserializeOwned};

use crate::serialization::cdr_serializer::to_bytes;
//use crate::serialization::error::Result;
use crate::serialization::error::Error;

/// A sample data type may be `Keyed` : 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 a sample by an application-defined function.
///
/// 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 actual 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 get_key(&self) -> Self::K;

  // provided method (TODO: what for?)
  fn get_hash(&self) -> u64
  where
    Self::K: Key,
  {
    let mut hasher = DefaultHasher::new();
    self.get_key().hash(&mut hasher);
    hasher.finish()
  }
}

// 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() -> KeyHash {
    KeyHash([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<KeyHash, Error> {
    let a =
      <[u8;16]>::try_from( bytes )
        .map_err( |_e|  Error::Eof )?;
    Ok(KeyHash(a))
  }

}



/// Key trait for Keyed Topics
///
/// 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) .

pub trait Key:
  Eq + PartialEq + PartialOrd + Ord + Hash + Clone + Serialize + DeserializeOwned
{
  
  // no methods required

  /// This function tries to determine if the maximum size of the sequential CDR encapsulation of 
  /// all the key fields is less than or equal to 128 bits.
  /// In case this function gets it wrong, it can be overridden.
  fn may_exceed_128_bits() -> bool {
    false //TODO: this is just a placeholder

    // Implementation plan:
    // We should be able to derive this value (true/false) at compile time. A derive macro looks
    // lke the best tool to do it. Problem is types that are defined in pre-existing libraries.
    // Need to think about this a bit further.
  }

  // provided method:
  fn into_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.
    */

    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( 
      // TODO: Here we just detect at run-time that the cdr_bytes is too long
      // fit into 16-byte hash field as-is. This should be done statically, and the
      // writer and reader (acreoss implementations of RTPS) must agree on this
      // based on key type alone, not any particular contents.
      if Self::may_exceed_128_bits() || cdr_bytes.len() > 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 into_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 get_key(&self) -> Self::K {
    (*self).get_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 {}
impl Key for u8 {}
impl Key for u16 {}
impl Key for u32 {}
impl Key for u64 {}
impl Key for u128 {}
impl Key for usize {}

impl Key for String {}

#[derive(Debug, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
/// 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 get_random_key() -> BuiltInTopicKey {
    let mut rng = rand::thread_rng();
    BuiltInTopicKey {
      value: [rng.gen(), rng.gen(), rng.gen()],
    }
  }

  pub fn default() -> BuiltInTopicKey {
    BuiltInTopicKey { value: [0, 0, 0] }
  }
}