1use std::{borrow::Cow, io::Read};
18
19use anyhow::{self, ensure, format_err};
20use byteorder::{ByteOrder, LittleEndian, ReadBytesExt};
21use chrono::{DateTime, NaiveDateTime, Utc};
22use rust_decimal::Decimal;
23use uuid::Uuid;
24
25use exonum_crypto::{Hash, PublicKey, HASH_SIZE};
26
27use super::ObjectHash;
28
29pub trait BinaryValue: Sized {
66 fn to_bytes(&self) -> Vec<u8>;
68
69 fn into_bytes(self) -> Vec<u8> {
73 self.to_bytes()
74 }
75
76 fn from_bytes(bytes: Cow<'_, [u8]>) -> anyhow::Result<Self>;
78}
79
80impl_object_hash_for_binary_value! { (), bool, Vec<u8>, String, PublicKey, DateTime<Utc>, Uuid, Decimal }
81
82macro_rules! impl_binary_value_scalar {
83 ($type:tt, $read:ident) => {
84 #[allow(clippy::use_self)]
85 impl BinaryValue for $type {
86 fn to_bytes(&self) -> Vec<u8> {
87 vec![*self as u8]
88 }
89
90 fn from_bytes(bytes: Cow<'_, [u8]>) -> anyhow::Result<Self> {
91 use byteorder::ReadBytesExt;
92 bytes.as_ref().$read().map_err(From::from)
93 }
94 }
95
96 impl_object_hash_for_binary_value! { $type }
97 };
98 ($type:tt, $write:ident, $read:ident, $len:expr) => {
99 #[allow(clippy::use_self)]
100 impl BinaryValue for $type {
101 fn to_bytes(&self) -> Vec<u8> {
102 let mut v = vec![0; $len];
103 LittleEndian::$write(&mut v, *self);
104 v
105 }
106
107 fn from_bytes(bytes: Cow<'_, [u8]>) -> anyhow::Result<Self> {
108 use byteorder::ReadBytesExt;
109 bytes.as_ref().$read::<LittleEndian>().map_err(From::from)
110 }
111 }
112
113 impl_object_hash_for_binary_value! { $type }
114 };
115}
116
117impl_binary_value_scalar! { u8, read_u8 }
119impl_binary_value_scalar! { u16, write_u16, read_u16, 2 }
120impl_binary_value_scalar! { u32, write_u32, read_u32, 4 }
121impl_binary_value_scalar! { u64, write_u64, read_u64, 8 }
122impl_binary_value_scalar! { u128, write_u128, read_u128, 16 }
123impl_binary_value_scalar! { i8, read_i8 }
125impl_binary_value_scalar! { i16, write_i16, read_i16, 2 }
126impl_binary_value_scalar! { i32, write_i32, read_i32, 4 }
127impl_binary_value_scalar! { i64, write_i64, read_i64, 8 }
128impl_binary_value_scalar! { i128, write_i128, read_i128, 16 }
129
130impl BinaryValue for () {
132 fn to_bytes(&self) -> Vec<u8> {
133 Vec::default()
134 }
135
136 fn from_bytes(_bytes: Cow<'_, [u8]>) -> anyhow::Result<Self> {
137 Ok(())
138 }
139}
140
141#[allow(clippy::use_self)] impl BinaryValue for bool {
143 fn to_bytes(&self) -> Vec<u8> {
144 vec![*self as u8]
145 }
146
147 fn from_bytes(bytes: Cow<'_, [u8]>) -> anyhow::Result<Self> {
148 let value = bytes.as_ref();
149 assert_eq!(value.len(), 1);
150
151 match value[0] {
152 0 => Ok(false),
153 1 => Ok(true),
154 value => Err(format_err!("Invalid value for bool: {}", value)),
155 }
156 }
157}
158
159impl BinaryValue for Vec<u8> {
160 fn to_bytes(&self) -> Vec<u8> {
161 self.clone()
162 }
163
164 fn from_bytes(bytes: Cow<'_, [u8]>) -> anyhow::Result<Self> {
165 Ok(bytes.into_owned())
166 }
167}
168
169impl BinaryValue for String {
170 fn to_bytes(&self) -> Vec<u8> {
171 self.as_bytes().to_owned()
172 }
173
174 fn from_bytes(bytes: Cow<'_, [u8]>) -> anyhow::Result<Self> {
175 Self::from_utf8(bytes.into_owned()).map_err(From::from)
176 }
177}
178
179impl BinaryValue for Hash {
180 fn to_bytes(&self) -> Vec<u8> {
181 self.as_ref().to_vec()
182 }
183
184 fn from_bytes(bytes: Cow<'_, [u8]>) -> anyhow::Result<Self> {
185 Self::from_slice(bytes.as_ref()).ok_or_else(|| {
186 format_err!("Unable to decode Hash from bytes: buffer size does not match")
187 })
188 }
189}
190
191impl BinaryValue for PublicKey {
192 fn to_bytes(&self) -> Vec<u8> {
193 self.as_ref().to_vec()
194 }
195
196 fn from_bytes(bytes: Cow<'_, [u8]>) -> anyhow::Result<Self> {
197 Self::from_slice(bytes.as_ref()).ok_or_else(|| {
198 format_err!("Unable to decode PublicKey from bytes: buffer size does not match")
199 })
200 }
201}
202
203impl BinaryValue for DateTime<Utc> {
206 fn to_bytes(&self) -> Vec<u8> {
207 let secs = self.timestamp();
208 let nanos = self.timestamp_subsec_nanos();
209
210 let mut buffer = vec![0; 12];
211 LittleEndian::write_i64(&mut buffer[0..8], secs);
212 LittleEndian::write_u32(&mut buffer[8..12], nanos);
213 buffer
214 }
215
216 fn from_bytes(bytes: Cow<'_, [u8]>) -> anyhow::Result<Self> {
217 let mut value = bytes.as_ref();
218 let secs = value.read_i64::<LittleEndian>()?;
219 let nanos = value.read_u32::<LittleEndian>()?;
220 Ok(Self::from_utc(
221 NaiveDateTime::from_timestamp(secs, nanos),
222 Utc,
223 ))
224 }
225}
226
227impl BinaryValue for Uuid {
228 fn to_bytes(&self) -> Vec<u8> {
229 self.as_bytes().to_vec()
230 }
231
232 fn from_bytes(bytes: Cow<'_, [u8]>) -> anyhow::Result<Self> {
233 Self::from_slice(bytes.as_ref()).map_err(From::from)
234 }
235}
236
237impl BinaryValue for Decimal {
238 fn to_bytes(&self) -> Vec<u8> {
239 self.serialize().to_vec()
240 }
241
242 fn from_bytes(bytes: Cow<'_, [u8]>) -> anyhow::Result<Self> {
243 let mut value = bytes.as_ref();
244 let mut buf: [u8; 16] = [0; 16];
245 value.read_exact(&mut buf)?;
246 Ok(Self::deserialize(buf))
247 }
248}
249
250impl BinaryValue for [u8; HASH_SIZE] {
251 fn to_bytes(&self) -> Vec<u8> {
252 self.to_vec()
253 }
254
255 fn from_bytes(bytes: Cow<'_, [u8]>) -> anyhow::Result<Self> {
256 let bytes = bytes.as_ref();
257 ensure!(
258 bytes.len() == HASH_SIZE,
259 "Unable to decode array from bytes: buffer size does not match"
260 );
261 let mut value = [0_u8; HASH_SIZE];
262 value.copy_from_slice(bytes);
263 Ok(value)
264 }
265}
266
267#[cfg(test)]
268mod tests {
269 use std::fmt::Debug;
270 use std::str::FromStr;
271
272 use chrono::Duration;
273
274 use super::*;
275
276 fn assert_round_trip_eq<T: BinaryValue + PartialEq + Debug>(values: &[T]) {
277 for value in values {
278 let bytes = value.to_bytes();
279 assert_eq!(
280 *value,
281 <T as BinaryValue>::from_bytes(bytes.into()).unwrap()
282 );
283 }
284 }
285
286 macro_rules! impl_test_binary_form_scalar_unsigned {
287 ($name:ident, $type:tt) => {
288 #[test]
289 fn $name() {
290 let values = [$type::min_value(), 1, $type::max_value()];
291 assert_round_trip_eq(&values);
292 }
293 };
294 }
295
296 macro_rules! impl_test_binary_form_scalar_signed {
297 ($name:ident, $type:tt) => {
298 #[test]
299 fn $name() {
300 let values = [$type::min_value(), -1, 0, 1, $type::max_value()];
301 assert_round_trip_eq(&values);
302 }
303 };
304 }
305
306 impl_test_binary_form_scalar_unsigned! { test_binary_form_round_trip_u8, u8 }
308 impl_test_binary_form_scalar_unsigned! { test_binary_form_round_trip_u32, u32 }
309 impl_test_binary_form_scalar_unsigned! { test_binary_form_round_trip_u16, u16 }
310 impl_test_binary_form_scalar_unsigned! { test_binary_form_round_trip_u64, u64 }
311 impl_test_binary_form_scalar_unsigned! { test_binary_form_round_trip_u128, u128 }
312
313 impl_test_binary_form_scalar_signed! { test_binary_form_round_trip_i8, i8 }
315 impl_test_binary_form_scalar_signed! { test_binary_form_round_trip_i16, i16 }
316 impl_test_binary_form_scalar_signed! { test_binary_form_round_trip_i32, i32 }
317 impl_test_binary_form_scalar_signed! { test_binary_form_round_trip_i64, i64 }
318 impl_test_binary_form_scalar_signed! { test_binary_form_round_trip_i128, i128 }
319
320 #[test]
323 fn test_binary_form_vec_u8() {
324 let values = [vec![], vec![1], vec![1, 2, 3], vec![255; 100]];
325 assert_round_trip_eq(&values);
326 }
327
328 #[test]
329 fn test_binary_form_bool_correct() {
330 let values = [true, false];
331 assert_round_trip_eq(&values);
332 }
333
334 #[test]
335 #[should_panic(expected = "Invalid value for bool: 2")]
336 fn test_binary_form_bool_incorrect() {
337 let bytes = 2_u8.to_bytes();
338 <bool as BinaryValue>::from_bytes(bytes.into()).unwrap();
339 }
340
341 #[test]
342 fn test_binary_form_string() {
343 let values: Vec<_> = ["", "e", "2", "hello"]
344 .iter()
345 .map(ToString::to_string)
346 .collect();
347 assert_round_trip_eq(&values);
348 }
349
350 #[test]
351 fn test_binary_form_datetime() {
352 use chrono::TimeZone;
353
354 let times = [
355 Utc.timestamp(0, 0),
356 Utc.timestamp(13, 23),
357 Utc::now(),
358 Utc::now() + Duration::seconds(17) + Duration::nanoseconds(15),
359 Utc.timestamp(0, 999_999_999),
360 Utc.timestamp(0, 1_500_000_000), ];
362 assert_round_trip_eq(×);
363 }
364
365 #[test]
366 fn test_binary_form_uuid() {
367 let values = [
368 Uuid::nil(),
369 Uuid::parse_str("936DA01F9ABD4d9d80C702AF85C822A8").unwrap(),
370 Uuid::parse_str("0000002a-000c-0005-0c03-0938362b0809").unwrap(),
371 ];
372 assert_round_trip_eq(&values);
373 }
374
375 #[test]
376 fn test_binary_form_decimal() {
377 let values = [
378 Decimal::from_str("3.14").unwrap(),
379 Decimal::from_parts(1_102_470_952, 185_874_565, 1_703_060_790, false, 28),
380 Decimal::new(9_497_628_354_687_268, 12),
381 Decimal::from_str("0").unwrap(),
382 Decimal::from_str("-0.000000000000000000019").unwrap(),
383 ];
384 assert_round_trip_eq(&values);
385 }
386
387 #[test]
388 fn test_binary_form_array_hash_size() {
389 let values = [[1; HASH_SIZE]];
390 assert_round_trip_eq(&values);
391 }
392}