1use alloc::{string::String, vec::Vec};
2use core::fmt;
3use core::ops::Deref;
4
5use serde::{de, ser, Deserialize, Deserializer, Serialize};
6
7use crate::{
8 encoding::{from_base64, to_base64},
9 errors::{StdError, StdResult},
10};
11
12#[derive(Clone, Default, PartialEq, Eq, Hash, PartialOrd, Ord, schemars::JsonSchema)]
18pub struct Binary(#[schemars(with = "String")] Vec<u8>);
19
20impl Binary {
21 pub const fn new(data: Vec<u8>) -> Self {
23 Self(data)
24 }
25
26 pub fn from_base64(encoded: &str) -> StdResult<Self> {
29 from_base64(encoded).map(Self::new)
30 }
31
32 pub fn to_base64(&self) -> String {
35 to_base64(&self.0)
36 }
37
38 pub fn as_slice(&self) -> &[u8] {
39 self.0.as_slice()
40 }
41
42 pub fn to_array<const LENGTH: usize>(&self) -> StdResult<[u8; LENGTH]> {
64 if self.len() != LENGTH {
65 return Err(StdError::invalid_data_size(LENGTH, self.len()));
66 }
67
68 let mut out: [u8; LENGTH] = [0; LENGTH];
69 out.copy_from_slice(&self.0);
70 Ok(out)
71 }
72}
73
74impl fmt::Display for Binary {
75 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
76 write!(f, "{}", self.to_base64())
77 }
78}
79
80impl fmt::Debug for Binary {
81 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82 write!(f, "Binary(")?;
85 for byte in self.0.iter() {
86 write!(f, "{byte:02x}")?;
87 }
88 write!(f, ")")?;
89 Ok(())
90 }
91}
92
93impl Deref for Binary {
99 type Target = [u8];
100
101 fn deref(&self) -> &Self::Target {
102 self.as_slice()
103 }
104}
105
106impl AsRef<[u8]> for Binary {
107 fn as_ref(&self) -> &[u8] {
108 self.as_slice()
109 }
110}
111
112impl From<&[u8]> for Binary {
114 fn from(binary: &[u8]) -> Self {
115 Self(binary.to_vec())
116 }
117}
118
119impl<const LENGTH: usize> From<&[u8; LENGTH]> for Binary {
121 fn from(source: &[u8; LENGTH]) -> Self {
122 Self(source.to_vec())
123 }
124}
125
126impl<const LENGTH: usize> From<[u8; LENGTH]> for Binary {
128 fn from(source: [u8; LENGTH]) -> Self {
129 Self(source.into())
130 }
131}
132
133impl From<Vec<u8>> for Binary {
134 fn from(vec: Vec<u8>) -> Self {
135 Self(vec)
136 }
137}
138
139impl From<Binary> for Vec<u8> {
140 fn from(original: Binary) -> Vec<u8> {
141 original.0
142 }
143}
144
145impl PartialEq<Vec<u8>> for Binary {
147 fn eq(&self, rhs: &Vec<u8>) -> bool {
148 self.0 == *rhs
150 }
151}
152
153impl PartialEq<Binary> for Vec<u8> {
155 fn eq(&self, rhs: &Binary) -> bool {
156 *self == rhs.0
158 }
159}
160
161impl PartialEq<&[u8]> for Binary {
163 fn eq(&self, rhs: &&[u8]) -> bool {
164 self.as_slice() == *rhs
166 }
167}
168
169impl PartialEq<Binary> for &[u8] {
171 fn eq(&self, rhs: &Binary) -> bool {
172 *self == rhs.as_slice()
174 }
175}
176
177impl<const LENGTH: usize> PartialEq<&[u8; LENGTH]> for Binary {
179 fn eq(&self, rhs: &&[u8; LENGTH]) -> bool {
180 self.as_slice() == rhs.as_slice()
181 }
182}
183
184impl<const LENGTH: usize> PartialEq<Binary> for &[u8; LENGTH] {
186 fn eq(&self, rhs: &Binary) -> bool {
187 self.as_slice() == rhs.as_slice()
188 }
189}
190
191impl<const LENGTH: usize> PartialEq<[u8; LENGTH]> for Binary {
193 fn eq(&self, rhs: &[u8; LENGTH]) -> bool {
194 self.as_slice() == rhs.as_slice()
195 }
196}
197
198impl<const LENGTH: usize> PartialEq<Binary> for [u8; LENGTH] {
200 fn eq(&self, rhs: &Binary) -> bool {
201 self.as_slice() == rhs.as_slice()
202 }
203}
204
205impl Serialize for Binary {
207 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
208 where
209 S: ser::Serializer,
210 {
211 if serializer.is_human_readable() {
212 serializer.serialize_str(&self.to_base64())
213 } else {
214 panic!("Binary is only intended to be used with JSON serialization for now. If you are hitting this panic please open an issue at https://github.com/CosmWasm/cosmwasm describing your use case.")
215 }
216 }
217}
218
219impl<'de> Deserialize<'de> for Binary {
221 fn deserialize<D>(deserializer: D) -> Result<Binary, D::Error>
222 where
223 D: Deserializer<'de>,
224 {
225 if deserializer.is_human_readable() {
226 deserializer.deserialize_str(Base64Visitor)
227 } else {
228 panic!("Binary is only intended to be used with JSON serialization for now. If you are hitting this panic please open an issue at https://github.com/CosmWasm/cosmwasm describing your use case.")
229 }
230 }
231}
232
233struct Base64Visitor;
234
235impl<'de> de::Visitor<'de> for Base64Visitor {
236 type Value = Binary;
237
238 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
239 formatter.write_str("valid base64 encoded string")
240 }
241
242 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
243 where
244 E: de::Error,
245 {
246 match Binary::from_base64(v) {
247 Ok(binary) => Ok(binary),
248 Err(_) => Err(E::custom(format_args!("invalid base64: {v}"))),
249 }
250 }
251}
252
253#[cfg(test)]
254mod tests {
255 use super::*;
256 use crate::assert_hash_works;
257 use crate::errors::StdError;
258
259 #[test]
260 fn to_array_works() {
261 let binary = Binary::from(&[1, 2, 3]);
263 let array: [u8; 3] = binary.to_array().unwrap();
264 assert_eq!(array, [1, 2, 3]);
265
266 let binary = Binary::from(&[]);
268 let array: [u8; 0] = binary.to_array().unwrap();
269 assert_eq!(array, [] as [u8; 0]);
270
271 let binary = Binary::from(&[1, 2, 3]);
273 let error = binary.to_array::<8>().unwrap_err();
274 match error {
275 StdError::InvalidDataSize {
276 expected, actual, ..
277 } => {
278 assert_eq!(expected, 8);
279 assert_eq!(actual, 3);
280 }
281 err => panic!("Unexpected error: {err:?}"),
282 }
283
284 let binary = Binary::from_base64("t119JOQox4WUQEmO/nyqOZfO+wjJm91YG2sfn4ZglvA=").unwrap();
286 let array: [u8; 32] = binary.to_array().unwrap();
287 assert_eq!(
288 array,
289 [
290 0xb7, 0x5d, 0x7d, 0x24, 0xe4, 0x28, 0xc7, 0x85, 0x94, 0x40, 0x49, 0x8e, 0xfe, 0x7c,
291 0xaa, 0x39, 0x97, 0xce, 0xfb, 0x08, 0xc9, 0x9b, 0xdd, 0x58, 0x1b, 0x6b, 0x1f, 0x9f,
292 0x86, 0x60, 0x96, 0xf0,
293 ]
294 );
295
296 let binary =
298 Binary::from_base64("t119JOQox4WUQEmO/nyqOZfO+wjJm91YG2sfn4ZglvBzyMOwMWq+").unwrap();
299 let array: [u8; 39] = binary.to_array().unwrap();
300 assert_eq!(
301 array,
302 [
303 0xb7, 0x5d, 0x7d, 0x24, 0xe4, 0x28, 0xc7, 0x85, 0x94, 0x40, 0x49, 0x8e, 0xfe, 0x7c,
304 0xaa, 0x39, 0x97, 0xce, 0xfb, 0x08, 0xc9, 0x9b, 0xdd, 0x58, 0x1b, 0x6b, 0x1f, 0x9f,
305 0x86, 0x60, 0x96, 0xf0, 0x73, 0xc8, 0xc3, 0xb0, 0x31, 0x6a, 0xbe,
306 ]
307 );
308 }
309
310 #[test]
311 fn test_base64_encoding_success() {
312 for (value, encoded, encoded_no_pad) in [
313 (&b""[..], "", ""),
314 (&b"hello"[..], "aGVsbG8=", "aGVsbG8"),
315 (&b"\x0C\xBB\x00\x11\xFA\x01"[..], "DLsAEfoB", "DLsAEfoB"),
316 (&b"rand"[..], "cmFuZA==", "cmFuZA"),
317 (&b"rand"[..], "cmFuZA==", "cmFuZA="),
318 (&b"randomiZ"[..], "cmFuZG9taVo=", "cmFuZG9taVo"),
319 ] {
320 let value = Binary::from(value);
321 assert_eq!(encoded, value.to_base64());
322 assert_eq!(Ok(value.clone()), Binary::from_base64(encoded));
323 assert_eq!(Ok(value.clone()), Binary::from_base64(encoded_no_pad));
324 }
325 }
326
327 #[test]
328 fn test_base64_encoding_error() {
329 for (invalid_base64, want) in [
330 ("cm%uZG9taVo", "Invalid symbol 37, offset 2."),
331 ("cmFuZ", "Invalid input length: 5"),
332 ] {
333 match Binary::from_base64(invalid_base64) {
334 Err(StdError::InvalidBase64 { msg, .. }) => assert_eq!(want, msg),
335 result => panic!("Unexpected result: {result:?}"),
336 }
337 }
338 }
339
340 #[test]
341 fn from_slice_works() {
342 let original: &[u8] = &[0u8, 187, 61, 11, 250, 0];
343 let binary: Binary = original.into();
344 assert_eq!(binary.as_slice(), [0u8, 187, 61, 11, 250, 0]);
345 }
346
347 #[test]
348 fn from_fixed_length_array_works() {
349 let original = &[];
350 let binary: Binary = original.into();
351 assert_eq!(binary.len(), 0);
352
353 let original = &[0u8];
354 let binary: Binary = original.into();
355 assert_eq!(binary.as_slice(), [0u8]);
356
357 let original = &[0u8, 187, 61, 11, 250, 0];
358 let binary: Binary = original.into();
359 assert_eq!(binary.as_slice(), [0u8, 187, 61, 11, 250, 0]);
360
361 let original = &[
362 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
363 1, 1, 1,
364 ];
365 let binary: Binary = original.into();
366 assert_eq!(
367 binary.as_slice(),
368 [
369 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
370 1, 1, 1, 1,
371 ]
372 );
373 }
374
375 #[test]
376 fn from_owned_fixed_length_array_works() {
377 let original = [];
378 let binary: Binary = original.into();
379 assert_eq!(binary.len(), 0);
380
381 let original = [0u8];
382 let binary: Binary = original.into();
383 assert_eq!(binary.as_slice(), [0u8]);
384
385 let original = [0u8, 187, 61, 11, 250, 0];
386 let binary: Binary = original.into();
387 assert_eq!(binary.as_slice(), [0u8, 187, 61, 11, 250, 0]);
388
389 let original = [
390 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
391 1, 1, 1,
392 ];
393 let binary: Binary = original.into();
394 assert_eq!(
395 binary.as_slice(),
396 [
397 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
398 1, 1, 1, 1,
399 ]
400 );
401 }
402
403 #[test]
404 fn from_literal_works() {
405 let a: Binary = b"".into();
406 assert_eq!(a.len(), 0);
407
408 let a: Binary = b".".into();
409 assert_eq!(a.len(), 1);
410
411 let a: Binary = b"...".into();
412 assert_eq!(a.len(), 3);
413
414 let a: Binary = b"...............................".into();
415 assert_eq!(a.len(), 31);
416
417 let a: Binary = b"................................".into();
418 assert_eq!(a.len(), 32);
419
420 let a: Binary = b".................................".into();
421 assert_eq!(a.len(), 33);
422 }
423
424 #[test]
425 fn from_vec_works() {
426 let original = vec![0u8, 187, 61, 11, 250, 0];
427 let original_ptr = original.as_ptr();
428 let binary: Binary = original.into();
429 assert_eq!(binary.as_slice(), [0u8, 187, 61, 11, 250, 0]);
430 assert_eq!(binary.0.as_ptr(), original_ptr, "vector must not be copied");
431 }
432
433 #[test]
434 fn into_vec_works() {
435 let original = Binary(vec![0u8, 187, 61, 11, 250, 0]);
437 let original_ptr = original.0.as_ptr();
438 let vec: Vec<u8> = original.into();
439 assert_eq!(vec.as_slice(), [0u8, 187, 61, 11, 250, 0]);
440 assert_eq!(vec.as_ptr(), original_ptr, "vector must not be copied");
441
442 let original = Binary(vec![7u8, 35, 49, 101, 0, 255]);
444 let original_ptr = original.0.as_ptr();
445 let vec = Vec::<u8>::from(original);
446 assert_eq!(vec.as_slice(), [7u8, 35, 49, 101, 0, 255]);
447 assert_eq!(vec.as_ptr(), original_ptr, "vector must not be copied");
448 }
449
450 #[test]
451 fn serialization_works() {
452 let binary = Binary(vec![0u8, 187, 61, 11, 250, 0]);
453
454 let json = serde_json::to_vec(&binary).unwrap();
455 let deserialized: Binary = serde_json::from_slice(&json).unwrap();
456
457 assert_eq!(binary, deserialized);
458 }
459
460 #[test]
461 fn deserialize_from_valid_string() {
462 let b64_str = "ALs9C/oA";
463 let expected = vec![0u8, 187, 61, 11, 250, 0];
465
466 let serialized = serde_json::to_vec(&b64_str).unwrap();
467 let deserialized: Binary = serde_json::from_slice(&serialized).unwrap();
468 assert_eq!(expected, deserialized.as_slice());
469 }
470
471 #[test]
472 fn deserialize_from_invalid_string() {
473 let invalid_str = "**BAD!**";
474 let serialized = serde_json::to_vec(&invalid_str).unwrap();
475 let res = serde_json::from_slice::<Binary>(&serialized);
476 assert!(res.is_err());
477 }
478
479 #[test]
480 fn binary_implements_debug() {
481 let binary = Binary(vec![0x07, 0x35, 0xAA, 0xcb, 0x00, 0xff]);
483 assert_eq!(format!("{binary:?}"), "Binary(0735aacb00ff)",);
484
485 let binary = Binary(vec![]);
487 assert_eq!(format!("{binary:?}"), "Binary()",);
488 }
489
490 #[test]
491 fn binary_implements_deref() {
492 let binary = Binary(vec![7u8, 35, 49, 101, 0, 255]);
494 assert_eq!(*binary, [7u8, 35, 49, 101, 0, 255]);
495
496 let binary = Binary(vec![7u8, 35, 49, 101, 0, 255]);
498 assert_eq!(binary.len(), 6);
499 let binary_slice: &[u8] = &binary;
500 assert_eq!(binary_slice, &[7u8, 35, 49, 101, 0, 255]);
501 }
502
503 #[test]
504 fn binary_implements_as_ref() {
505 let want = &[7u8, 35, 49, 101, 0, 255];
506 let data = Binary(want.to_vec());
507 assert_eq!(want, AsRef::<[u8]>::as_ref(&data));
508 assert_eq!(want, AsRef::<[u8]>::as_ref(&&data));
509 }
510
511 #[test]
514 fn binary_implements_hash_eq() {
515 let a = Binary::from([0, 187, 61, 11, 250, 0]);
516 let b = Binary::from([16, 21, 33, 0, 255, 9]);
517 assert_hash_works!(a, b);
518 }
519
520 #[test]
521 fn binary_implements_partial_eq_with_vector() {
522 let a = Binary(vec![5u8; 3]);
523 let b = vec![5u8; 3];
524 let c = vec![9u8; 3];
525 assert_eq!(a, b);
526 assert_eq!(b, a);
527 assert_ne!(a, c);
528 assert_ne!(c, a);
529 }
530
531 #[test]
532 fn binary_implements_partial_eq_with_slice_and_array() {
533 let a = Binary(vec![0xAA, 0xBB]);
534
535 assert_eq!(a, b"\xAA\xBB" as &[u8]);
537 assert_eq!(b"\xAA\xBB" as &[u8], a);
538 assert_ne!(a, b"\x11\x22" as &[u8]);
539 assert_ne!(b"\x11\x22" as &[u8], a);
540
541 assert_eq!(a, b"\xAA\xBB");
543 assert_eq!(b"\xAA\xBB", a);
544 assert_ne!(a, b"\x11\x22");
545 assert_ne!(b"\x11\x22", a);
546
547 assert_eq!(a, [0xAA, 0xBB]);
549 assert_eq!([0xAA, 0xBB], a);
550 assert_ne!(a, [0x11, 0x22]);
551 assert_ne!([0x11, 0x22], a);
552 }
553}