1 | // SPDX-License-Identifier: Apache-2.0 |
2 | |
3 | use super::{Error, Integer, Value}; |
4 | |
5 | use alloc::{boxed::Box, string::String, vec::Vec}; |
6 | use core::iter::Peekable; |
7 | |
8 | use ciborium_ll::tag; |
9 | use serde::de::{self, Deserializer as _}; |
10 | |
11 | impl<'a> From<Integer> for de::Unexpected<'a> { |
12 | #[inline ] |
13 | fn from(value: Integer) -> Self { |
14 | u64::try_from(value) |
15 | .map(de::Unexpected::Unsigned) |
16 | .unwrap_or_else(|_| { |
17 | i64::try_from(value) |
18 | .map(de::Unexpected::Signed) |
19 | .unwrap_or_else(|_| de::Unexpected::Other("large integer" )) |
20 | }) |
21 | } |
22 | } |
23 | |
24 | impl<'a> From<&'a Value> for de::Unexpected<'a> { |
25 | #[inline ] |
26 | fn from(value: &'a Value) -> Self { |
27 | match value { |
28 | Value::Bool(x) => Self::Bool(*x), |
29 | Value::Integer(x) => Self::from(*x), |
30 | Value::Float(x) => Self::Float(*x), |
31 | Value::Bytes(x) => Self::Bytes(x), |
32 | Value::Text(x) => Self::Str(x), |
33 | Value::Array(..) => Self::Seq, |
34 | Value::Map(..) => Self::Map, |
35 | Value::Null => Self::Other("null" ), |
36 | Value::Tag(..) => Self::Other("tag" ), |
37 | } |
38 | } |
39 | } |
40 | |
41 | macro_rules! mkvisit { |
42 | ($($f:ident($v:ty)),+ $(,)?) => { |
43 | $( |
44 | #[inline] |
45 | fn $f<E: de::Error>(self, v: $v) -> Result<Self::Value, E> { |
46 | Ok(v.into()) |
47 | } |
48 | )+ |
49 | }; |
50 | } |
51 | |
52 | struct Visitor; |
53 | |
54 | impl<'de> serde::de::Visitor<'de> for Visitor { |
55 | type Value = Value; |
56 | |
57 | fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
58 | write!(formatter, "a valid CBOR item" ) |
59 | } |
60 | |
61 | mkvisit! { |
62 | visit_bool(bool), |
63 | visit_f32(f32), |
64 | visit_f64(f64), |
65 | |
66 | visit_i8(i8), |
67 | visit_i16(i16), |
68 | visit_i32(i32), |
69 | visit_i64(i64), |
70 | visit_i128(i128), |
71 | |
72 | visit_u8(u8), |
73 | visit_u16(u16), |
74 | visit_u32(u32), |
75 | visit_u64(u64), |
76 | visit_u128(u128), |
77 | |
78 | visit_char(char), |
79 | visit_str(&str), |
80 | visit_borrowed_str(&'de str), |
81 | visit_string(String), |
82 | |
83 | visit_bytes(&[u8]), |
84 | visit_borrowed_bytes(&'de [u8]), |
85 | visit_byte_buf(Vec<u8>), |
86 | } |
87 | |
88 | #[inline ] |
89 | fn visit_none<E: de::Error>(self) -> Result<Self::Value, E> { |
90 | Ok(Value::Null) |
91 | } |
92 | |
93 | #[inline ] |
94 | fn visit_some<D: de::Deserializer<'de>>( |
95 | self, |
96 | deserializer: D, |
97 | ) -> Result<Self::Value, D::Error> { |
98 | deserializer.deserialize_any(self) |
99 | } |
100 | |
101 | #[inline ] |
102 | fn visit_unit<E: de::Error>(self) -> Result<Self::Value, E> { |
103 | Ok(Value::Null) |
104 | } |
105 | |
106 | #[inline ] |
107 | fn visit_newtype_struct<D: de::Deserializer<'de>>( |
108 | self, |
109 | deserializer: D, |
110 | ) -> Result<Self::Value, D::Error> { |
111 | deserializer.deserialize_any(self) |
112 | } |
113 | |
114 | #[inline ] |
115 | fn visit_seq<A: de::SeqAccess<'de>>(self, mut acc: A) -> Result<Self::Value, A::Error> { |
116 | let mut seq = Vec::new(); |
117 | |
118 | while let Some(elem) = acc.next_element()? { |
119 | seq.push(elem); |
120 | } |
121 | |
122 | Ok(Value::Array(seq)) |
123 | } |
124 | |
125 | #[inline ] |
126 | fn visit_map<A: de::MapAccess<'de>>(self, mut acc: A) -> Result<Self::Value, A::Error> { |
127 | let mut map = Vec::<(Value, Value)>::new(); |
128 | |
129 | while let Some(kv) = acc.next_entry()? { |
130 | map.push(kv); |
131 | } |
132 | |
133 | Ok(Value::Map(map)) |
134 | } |
135 | |
136 | #[inline ] |
137 | fn visit_enum<A: de::EnumAccess<'de>>(self, acc: A) -> Result<Self::Value, A::Error> { |
138 | use serde::de::VariantAccess; |
139 | |
140 | struct Inner; |
141 | |
142 | impl<'de> serde::de::Visitor<'de> for Inner { |
143 | type Value = Value; |
144 | |
145 | fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
146 | write!(formatter, "a valid CBOR item" ) |
147 | } |
148 | |
149 | #[inline ] |
150 | fn visit_seq<A: de::SeqAccess<'de>>(self, mut acc: A) -> Result<Self::Value, A::Error> { |
151 | let tag: u64 = acc |
152 | .next_element()? |
153 | .ok_or_else(|| de::Error::custom("expected tag" ))?; |
154 | let val = acc |
155 | .next_element()? |
156 | .ok_or_else(|| de::Error::custom("expected val" ))?; |
157 | Ok(Value::Tag(tag, Box::new(val))) |
158 | } |
159 | } |
160 | |
161 | let (name, data): (String, _) = acc.variant()?; |
162 | assert_eq!("@@TAGGED@@" , name); |
163 | data.tuple_variant(2, Inner) |
164 | } |
165 | } |
166 | |
167 | impl<'de> de::Deserialize<'de> for Value { |
168 | #[inline ] |
169 | fn deserialize<D: de::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> { |
170 | deserializer.deserialize_any(Visitor) |
171 | } |
172 | } |
173 | |
174 | struct Deserializer<T>(T); |
175 | |
176 | impl<'a> Deserializer<&'a Value> { |
177 | fn integer<N>(&self, kind: &'static str) -> Result<N, Error> |
178 | where |
179 | N: TryFrom<u128>, |
180 | N: TryFrom<i128>, |
181 | { |
182 | fn raw(value: &Value) -> Result<u128, Error> { |
183 | let mut buffer = 0u128.to_ne_bytes(); |
184 | let length = buffer.len(); |
185 | |
186 | let bytes = match value { |
187 | Value::Bytes(bytes) => { |
188 | // Skip leading zeros... |
189 | let mut bytes: &[u8] = bytes.as_ref(); |
190 | while bytes.len() > buffer.len() && bytes[0] == 0 { |
191 | bytes = &bytes[1..]; |
192 | } |
193 | |
194 | if bytes.len() > buffer.len() { |
195 | return Err(de::Error::custom("bigint too large" )); |
196 | } |
197 | |
198 | bytes |
199 | } |
200 | |
201 | _ => return Err(de::Error::invalid_type(value.into(), &"bytes" )), |
202 | }; |
203 | |
204 | buffer[length - bytes.len()..].copy_from_slice(bytes); |
205 | Ok(u128::from_be_bytes(buffer)) |
206 | } |
207 | |
208 | let err = || de::Error::invalid_type(self.0.into(), &kind); |
209 | |
210 | Ok(match self.0 { |
211 | Value::Integer(x) => i128::from(*x).try_into().map_err(|_| err())?, |
212 | Value::Tag(t, v) if *t == tag::BIGPOS => raw(v)?.try_into().map_err(|_| err())?, |
213 | Value::Tag(t, v) if *t == tag::BIGNEG => i128::try_from(raw(v)?) |
214 | .map(|x| x ^ !0) |
215 | .map_err(|_| err()) |
216 | .and_then(|x| x.try_into().map_err(|_| err()))?, |
217 | _ => return Err(de::Error::invalid_type(self.0.into(), &"(big)int" )), |
218 | }) |
219 | } |
220 | } |
221 | |
222 | impl<'a, 'de> de::Deserializer<'de> for Deserializer<&'a Value> { |
223 | type Error = Error; |
224 | |
225 | #[inline ] |
226 | fn deserialize_any<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> { |
227 | match self.0 { |
228 | Value::Bytes(x) => visitor.visit_bytes(x), |
229 | Value::Text(x) => visitor.visit_str(x), |
230 | Value::Array(x) => visitor.visit_seq(Deserializer(x.iter())), |
231 | Value::Map(x) => visitor.visit_map(Deserializer(x.iter().peekable())), |
232 | Value::Bool(x) => visitor.visit_bool(*x), |
233 | Value::Null => visitor.visit_none(), |
234 | |
235 | Value::Tag(t, v) => { |
236 | let parent: Deserializer<&Value> = Deserializer(v); |
237 | let access = crate::tag::TagAccess::new(parent, Some(*t)); |
238 | visitor.visit_enum(access) |
239 | } |
240 | |
241 | Value::Integer(x) => { |
242 | if let Ok(x) = u64::try_from(*x) { |
243 | visitor.visit_u64(x) |
244 | } else if let Ok(x) = i64::try_from(*x) { |
245 | visitor.visit_i64(x) |
246 | } else if let Ok(x) = i128::try_from(*x) { |
247 | visitor.visit_i128(x) |
248 | } else { |
249 | unreachable!() |
250 | } |
251 | } |
252 | |
253 | Value::Float(x) => visitor.visit_f64(*x), |
254 | } |
255 | } |
256 | |
257 | #[inline ] |
258 | fn deserialize_bool<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> { |
259 | let mut value = self.0; |
260 | while let Value::Tag(.., v) = value { |
261 | value = v; |
262 | } |
263 | |
264 | match value { |
265 | Value::Bool(x) => visitor.visit_bool(*x), |
266 | _ => Err(de::Error::invalid_type(value.into(), &"bool" )), |
267 | } |
268 | } |
269 | |
270 | #[inline ] |
271 | fn deserialize_f32<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> { |
272 | self.deserialize_f64(visitor) |
273 | } |
274 | |
275 | #[inline ] |
276 | fn deserialize_f64<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> { |
277 | let mut value = self.0; |
278 | while let Value::Tag(.., v) = value { |
279 | value = v; |
280 | } |
281 | |
282 | match value { |
283 | Value::Float(x) => visitor.visit_f64(*x), |
284 | _ => Err(de::Error::invalid_type(value.into(), &"f64" )), |
285 | } |
286 | } |
287 | |
288 | fn deserialize_i8<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> { |
289 | visitor.visit_i8(self.integer("i8" )?) |
290 | } |
291 | |
292 | fn deserialize_i16<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> { |
293 | visitor.visit_i16(self.integer("i16" )?) |
294 | } |
295 | |
296 | fn deserialize_i32<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> { |
297 | visitor.visit_i32(self.integer("i32" )?) |
298 | } |
299 | |
300 | fn deserialize_i64<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> { |
301 | visitor.visit_i64(self.integer("i64" )?) |
302 | } |
303 | |
304 | fn deserialize_i128<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> { |
305 | visitor.visit_i128(self.integer("i128" )?) |
306 | } |
307 | |
308 | fn deserialize_u8<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> { |
309 | visitor.visit_u8(self.integer("u8" )?) |
310 | } |
311 | |
312 | fn deserialize_u16<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> { |
313 | visitor.visit_u16(self.integer("u16" )?) |
314 | } |
315 | |
316 | fn deserialize_u32<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> { |
317 | visitor.visit_u32(self.integer("u32" )?) |
318 | } |
319 | |
320 | fn deserialize_u64<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> { |
321 | visitor.visit_u64(self.integer("u64" )?) |
322 | } |
323 | |
324 | fn deserialize_u128<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> { |
325 | visitor.visit_u128(self.integer("u128" )?) |
326 | } |
327 | |
328 | fn deserialize_char<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> { |
329 | let mut value = self.0; |
330 | while let Value::Tag(.., v) = value { |
331 | value = v; |
332 | } |
333 | |
334 | match value { |
335 | Value::Text(x) => match x.chars().count() { |
336 | 1 => visitor.visit_char(x.chars().next().unwrap()), |
337 | _ => Err(de::Error::invalid_type(value.into(), &"char" )), |
338 | }, |
339 | |
340 | _ => Err(de::Error::invalid_type(value.into(), &"char" )), |
341 | } |
342 | } |
343 | |
344 | fn deserialize_str<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> { |
345 | let mut value = self.0; |
346 | while let Value::Tag(.., v) = value { |
347 | value = v; |
348 | } |
349 | |
350 | match value { |
351 | Value::Text(x) => visitor.visit_str(x), |
352 | _ => Err(de::Error::invalid_type(value.into(), &"str" )), |
353 | } |
354 | } |
355 | |
356 | fn deserialize_string<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> { |
357 | self.deserialize_str(visitor) |
358 | } |
359 | |
360 | fn deserialize_bytes<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> { |
361 | let mut value = self.0; |
362 | while let Value::Tag(.., v) = value { |
363 | value = v; |
364 | } |
365 | |
366 | match value { |
367 | Value::Bytes(x) => visitor.visit_bytes(x), |
368 | _ => Err(de::Error::invalid_type(value.into(), &"bytes" )), |
369 | } |
370 | } |
371 | |
372 | fn deserialize_byte_buf<V: de::Visitor<'de>>( |
373 | self, |
374 | visitor: V, |
375 | ) -> Result<V::Value, Self::Error> { |
376 | self.deserialize_bytes(visitor) |
377 | } |
378 | |
379 | fn deserialize_seq<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> { |
380 | let mut value = self.0; |
381 | while let Value::Tag(.., v) = value { |
382 | value = v; |
383 | } |
384 | |
385 | match value { |
386 | Value::Array(x) => visitor.visit_seq(Deserializer(x.iter())), |
387 | _ => Err(de::Error::invalid_type(value.into(), &"array" )), |
388 | } |
389 | } |
390 | |
391 | fn deserialize_map<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> { |
392 | let mut value = self.0; |
393 | while let Value::Tag(.., v) = value { |
394 | value = v; |
395 | } |
396 | |
397 | match value { |
398 | Value::Map(x) => visitor.visit_map(Deserializer(x.iter().peekable())), |
399 | _ => Err(de::Error::invalid_type(value.into(), &"map" )), |
400 | } |
401 | } |
402 | |
403 | fn deserialize_struct<V: de::Visitor<'de>>( |
404 | self, |
405 | _name: &'static str, |
406 | _fields: &'static [&'static str], |
407 | visitor: V, |
408 | ) -> Result<V::Value, Self::Error> { |
409 | self.deserialize_map(visitor) |
410 | } |
411 | |
412 | fn deserialize_tuple<V: de::Visitor<'de>>( |
413 | self, |
414 | _len: usize, |
415 | visitor: V, |
416 | ) -> Result<V::Value, Self::Error> { |
417 | self.deserialize_seq(visitor) |
418 | } |
419 | |
420 | fn deserialize_tuple_struct<V: de::Visitor<'de>>( |
421 | self, |
422 | _name: &'static str, |
423 | _len: usize, |
424 | visitor: V, |
425 | ) -> Result<V::Value, Self::Error> { |
426 | self.deserialize_seq(visitor) |
427 | } |
428 | |
429 | fn deserialize_identifier<V: de::Visitor<'de>>( |
430 | self, |
431 | visitor: V, |
432 | ) -> Result<V::Value, Self::Error> { |
433 | self.deserialize_str(visitor) |
434 | } |
435 | |
436 | fn deserialize_ignored_any<V: de::Visitor<'de>>( |
437 | self, |
438 | visitor: V, |
439 | ) -> Result<V::Value, Self::Error> { |
440 | self.deserialize_any(visitor) |
441 | } |
442 | |
443 | #[inline ] |
444 | fn deserialize_option<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> { |
445 | match self.0 { |
446 | Value::Null => visitor.visit_none(), |
447 | x => visitor.visit_some(Self(x)), |
448 | } |
449 | } |
450 | |
451 | #[inline ] |
452 | fn deserialize_unit<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> { |
453 | match self.0 { |
454 | Value::Null => visitor.visit_unit(), |
455 | _ => Err(de::Error::invalid_type(self.0.into(), &"null" )), |
456 | } |
457 | } |
458 | |
459 | #[inline ] |
460 | fn deserialize_unit_struct<V: de::Visitor<'de>>( |
461 | self, |
462 | _name: &'static str, |
463 | visitor: V, |
464 | ) -> Result<V::Value, Self::Error> { |
465 | self.deserialize_unit(visitor) |
466 | } |
467 | |
468 | #[inline ] |
469 | fn deserialize_newtype_struct<V: de::Visitor<'de>>( |
470 | self, |
471 | _name: &'static str, |
472 | visitor: V, |
473 | ) -> Result<V::Value, Self::Error> { |
474 | visitor.visit_newtype_struct(self) |
475 | } |
476 | |
477 | #[inline ] |
478 | fn deserialize_enum<V: de::Visitor<'de>>( |
479 | self, |
480 | name: &'static str, |
481 | variants: &'static [&'static str], |
482 | visitor: V, |
483 | ) -> Result<V::Value, Self::Error> { |
484 | if name == "@@TAG@@" { |
485 | let (tag, val) = match self.0 { |
486 | Value::Tag(t, v) => (Some(*t), v.as_ref()), |
487 | v => (None, v), |
488 | }; |
489 | |
490 | let parent: Deserializer<&Value> = Deserializer(val); |
491 | let access = crate::tag::TagAccess::new(parent, tag); |
492 | return visitor.visit_enum(access); |
493 | } |
494 | |
495 | match self.0 { |
496 | Value::Tag(.., v) => Deserializer(v.as_ref()).deserialize_enum(name, variants, visitor), |
497 | Value::Map(x) if x.len() == 1 => visitor.visit_enum(Deserializer(&x[0])), |
498 | x @ Value::Text(..) => visitor.visit_enum(Deserializer(x)), |
499 | _ => Err(de::Error::invalid_type(self.0.into(), &"map" )), |
500 | } |
501 | } |
502 | } |
503 | |
504 | impl<'a, 'de, T: Iterator<Item = &'a Value>> de::SeqAccess<'de> for Deserializer<T> { |
505 | type Error = Error; |
506 | |
507 | #[inline ] |
508 | fn next_element_seed<U: de::DeserializeSeed<'de>>( |
509 | &mut self, |
510 | seed: U, |
511 | ) -> Result<Option<U::Value>, Self::Error> { |
512 | match self.0.next() { |
513 | None => Ok(None), |
514 | Some(v) => seed.deserialize(Deserializer(v)).map(Some), |
515 | } |
516 | } |
517 | } |
518 | |
519 | impl<'a, 'de, T: Iterator<Item = &'a (Value, Value)>> de::MapAccess<'de> |
520 | for Deserializer<Peekable<T>> |
521 | { |
522 | type Error = Error; |
523 | |
524 | #[inline ] |
525 | fn next_key_seed<K: de::DeserializeSeed<'de>>( |
526 | &mut self, |
527 | seed: K, |
528 | ) -> Result<Option<K::Value>, Self::Error> { |
529 | match self.0.peek() { |
530 | None => Ok(None), |
531 | Some(x) => Ok(Some(seed.deserialize(Deserializer(&x.0))?)), |
532 | } |
533 | } |
534 | |
535 | #[inline ] |
536 | fn next_value_seed<V: de::DeserializeSeed<'de>>( |
537 | &mut self, |
538 | seed: V, |
539 | ) -> Result<V::Value, Self::Error> { |
540 | seed.deserialize(Deserializer(&self.0.next().unwrap().1)) |
541 | } |
542 | } |
543 | |
544 | impl<'a, 'de> de::EnumAccess<'de> for Deserializer<&'a (Value, Value)> { |
545 | type Error = Error; |
546 | type Variant = Deserializer<&'a Value>; |
547 | |
548 | #[inline ] |
549 | fn variant_seed<V: de::DeserializeSeed<'de>>( |
550 | self, |
551 | seed: V, |
552 | ) -> Result<(V::Value, Self::Variant), Self::Error> { |
553 | let k = seed.deserialize(Deserializer(&self.0 .0))?; |
554 | Ok((k, Deserializer(&self.0 .1))) |
555 | } |
556 | } |
557 | |
558 | impl<'a, 'de> de::EnumAccess<'de> for Deserializer<&'a Value> { |
559 | type Error = Error; |
560 | type Variant = Deserializer<&'a Value>; |
561 | |
562 | #[inline ] |
563 | fn variant_seed<V: de::DeserializeSeed<'de>>( |
564 | self, |
565 | seed: V, |
566 | ) -> Result<(V::Value, Self::Variant), Self::Error> { |
567 | let k = seed.deserialize(self)?; |
568 | Ok((k, Deserializer(&Value::Null))) |
569 | } |
570 | } |
571 | |
572 | impl<'a, 'de> de::VariantAccess<'de> for Deserializer<&'a Value> { |
573 | type Error = Error; |
574 | |
575 | #[inline ] |
576 | fn unit_variant(self) -> Result<(), Self::Error> { |
577 | match self.0 { |
578 | Value::Null => Ok(()), |
579 | _ => Err(de::Error::invalid_type(self.0.into(), &"unit" )), |
580 | } |
581 | } |
582 | |
583 | #[inline ] |
584 | fn newtype_variant_seed<U: de::DeserializeSeed<'de>>( |
585 | self, |
586 | seed: U, |
587 | ) -> Result<U::Value, Self::Error> { |
588 | seed.deserialize(self) |
589 | } |
590 | |
591 | #[inline ] |
592 | fn tuple_variant<V: de::Visitor<'de>>( |
593 | self, |
594 | _len: usize, |
595 | visitor: V, |
596 | ) -> Result<V::Value, Self::Error> { |
597 | self.deserialize_seq(visitor) |
598 | } |
599 | |
600 | #[inline ] |
601 | fn struct_variant<V: de::Visitor<'de>>( |
602 | self, |
603 | _fields: &'static [&'static str], |
604 | visitor: V, |
605 | ) -> Result<V::Value, Self::Error> { |
606 | self.deserialize_map(visitor) |
607 | } |
608 | } |
609 | |
610 | impl Value { |
611 | /// Deserializes the `Value` into an object |
612 | #[inline ] |
613 | pub fn deserialized<'de, T: de::Deserialize<'de>>(&self) -> Result<T, Error> { |
614 | T::deserialize(Deserializer(self)) |
615 | } |
616 | } |
617 | |