1use crate::error::Result;
2use crate::io::BitReader;
3
4use super::field_value::FieldValue;
5use super::quantized_float::QuantizedFloat;
6
7pub struct FieldDecodeContext {
9 pub tick_interval: f32,
11 pub string_buf: Vec<u8>,
13}
14
15impl FieldDecodeContext {
16 pub fn new(tick_interval: f32) -> Self {
17 Self {
18 tick_interval,
19 string_buf: Vec::with_capacity(512),
20 }
21 }
22}
23
24#[derive(Debug, Clone)]
29pub enum Decoder {
30 Bool,
31 I64,
32 U64,
33 U64Fixed64,
34 F32NoScale,
35 F32SimulationTime,
36 F32Coord,
37 F32Normal,
38 F32Quantized(QuantizedFloat),
39 String,
40 Vector2(Box<Decoder>),
41 Vector3(Box<Decoder>),
42 Vector3Normal,
43 Vector4(Box<Decoder>),
44 FloatVecN {
47 count: usize,
48 inner: Box<Decoder>,
49 },
50 BinaryBlock,
53 Poly,
57 QAnglePitchYaw {
58 bit_count: usize,
59 },
60 QAnglePrecise,
61 QAngleBitCount {
62 bit_count: usize,
63 },
64 QAngleCoord,
65 Default,
67}
68
69impl Decoder {
70 pub fn decode(&self, ctx: &mut FieldDecodeContext, br: &mut BitReader) -> Result<FieldValue> {
72 match self {
73 Decoder::Bool => Ok(FieldValue::Bool(br.read_bool()?)),
74
75 Decoder::I64 => Ok(FieldValue::I64(br.read_varint64()?)),
76
77 Decoder::U64 => Ok(FieldValue::U64(br.read_uvarint64()?)),
78
79 Decoder::U64Fixed64 => {
80 let mut buf = [0u8; 8];
81 br.read_bytes(&mut buf)?;
82 Ok(FieldValue::U64(u64::from_le_bytes(buf)))
83 }
84
85 Decoder::F32NoScale => Ok(FieldValue::F32(br.read_f32()?)),
86
87 Decoder::F32SimulationTime => {
88 let ticks = br.read_uvarint32()?;
89 Ok(FieldValue::F32(ticks as f32 * ctx.tick_interval))
90 }
91
92 Decoder::F32Coord => Ok(FieldValue::F32(br.read_bitcoord()?)),
93
94 Decoder::F32Normal => Ok(FieldValue::F32(br.read_bitnormal()?)),
95
96 Decoder::F32Quantized(qf) => Ok(FieldValue::F32(qf.decode(br)?)),
97
98 Decoder::String => {
99 ctx.string_buf.clear();
100 br.read_string_raw(&mut ctx.string_buf)?;
101 Ok(FieldValue::String(ctx.string_buf.clone()))
102 }
103
104 Decoder::Vector2(inner) => {
105 let x = inner.decode_f32(ctx, br)?;
106 let y = inner.decode_f32(ctx, br)?;
107 Ok(FieldValue::Vector2([x, y]))
108 }
109
110 Decoder::Vector3(inner) => {
111 let x = inner.decode_f32(ctx, br)?;
112 let y = inner.decode_f32(ctx, br)?;
113 let z = inner.decode_f32(ctx, br)?;
114 Ok(FieldValue::Vector3([x, y, z]))
115 }
116
117 Decoder::Vector3Normal => Ok(FieldValue::Vector3(br.read_bitvec3normal()?)),
118
119 Decoder::FloatVecN { count, inner } => {
120 let mut v = Vec::with_capacity(*count);
121 for _ in 0..*count {
122 v.push(inner.decode_f32(ctx, br)?);
123 }
124 Ok(FieldValue::FloatVector(v))
125 }
126
127 Decoder::BinaryBlock => {
128 let n = br.read_uvarint32()? as usize;
129 ctx.string_buf.clear();
130 ctx.string_buf.resize(n, 0);
131 br.read_bytes(&mut ctx.string_buf)?;
132 Ok(FieldValue::String(ctx.string_buf.clone()))
133 }
134
135 Decoder::Poly => {
136 let present = br.read_bool()?;
137 let _poly_type_index = br.read_ubitvar()?;
138 Ok(FieldValue::Bool(present))
139 }
140
141 Decoder::Vector4(inner) => {
142 let x = inner.decode_f32(ctx, br)?;
143 let y = inner.decode_f32(ctx, br)?;
144 let z = inner.decode_f32(ctx, br)?;
145 let w = inner.decode_f32(ctx, br)?;
146 Ok(FieldValue::Vector4([x, y, z, w]))
147 }
148
149 Decoder::QAnglePitchYaw { bit_count } => {
150 let pitch = br.read_bitangle(*bit_count)?;
151 let yaw = br.read_bitangle(*bit_count)?;
152 Ok(FieldValue::QAngle([pitch, yaw, 0.0]))
153 }
154
155 Decoder::QAnglePrecise => {
156 let mut v = [0.0f32; 3];
157 let rx = br.read_bool()?;
158 let ry = br.read_bool()?;
159 let rz = br.read_bool()?;
160 if rx {
161 v[0] = br.read_bitangle(20)?;
162 }
163 if ry {
164 v[1] = br.read_bitangle(20)?;
165 }
166 if rz {
167 v[2] = br.read_bitangle(20)?;
168 }
169 Ok(FieldValue::QAngle(v))
170 }
171
172 Decoder::QAngleBitCount { bit_count } => {
173 let x = br.read_bitangle(*bit_count)?;
174 let y = br.read_bitangle(*bit_count)?;
175 let z = br.read_bitangle(*bit_count)?;
176 Ok(FieldValue::QAngle([x, y, z]))
177 }
178
179 Decoder::QAngleCoord => Ok(FieldValue::QAngle(br.read_bitvec3coord()?)),
180
181 Decoder::Default => Ok(FieldValue::U64(br.read_uvarint64()?)),
182 }
183 }
184
185 fn decode_f32(&self, ctx: &mut FieldDecodeContext, br: &mut BitReader) -> Result<f32> {
187 match self.decode(ctx, br)? {
188 FieldValue::F32(v) => Ok(v),
189 _ => Ok(0.0),
190 }
191 }
192
193 #[allow(clippy::only_used_in_recursion)]
196 pub fn skip(&self, ctx: &mut FieldDecodeContext, br: &mut BitReader) -> Result<()> {
197 match self {
198 Decoder::Bool => {
199 br.skip_bits(1)?;
200 }
201
202 Decoder::I64 => {
203 br.skip_varint()?;
204 }
205
206 Decoder::U64 => {
207 br.skip_varint()?;
208 }
209
210 Decoder::U64Fixed64 => {
211 br.skip_bits(64)?;
212 }
213
214 Decoder::F32NoScale => {
215 br.skip_bits(32)?;
216 }
217
218 Decoder::F32SimulationTime => {
219 br.skip_varint()?;
220 }
221
222 Decoder::F32Coord => {
223 br.skip_bitcoord()?;
224 }
225
226 Decoder::F32Normal => {
227 br.skip_bitnormal()?;
228 }
229
230 Decoder::F32Quantized(qf) => {
231 qf.skip(br)?;
232 }
233
234 Decoder::String => {
235 br.skip_string()?;
236 }
237
238 Decoder::Vector2(inner) => {
239 inner.skip(ctx, br)?;
240 inner.skip(ctx, br)?;
241 }
242
243 Decoder::Vector3(inner) => {
244 inner.skip(ctx, br)?;
245 inner.skip(ctx, br)?;
246 inner.skip(ctx, br)?;
247 }
248
249 Decoder::Vector3Normal => {
250 br.skip_bitvec3normal()?;
251 }
252
253 Decoder::FloatVecN { count, inner } => {
254 for _ in 0..*count {
255 inner.skip(ctx, br)?;
256 }
257 }
258
259 Decoder::BinaryBlock => {
260 let n = br.read_uvarint32()? as usize;
261 br.skip_bits(n * 8)?;
262 }
263
264 Decoder::Poly => {
265 br.skip_bits(1)?;
266 br.read_ubitvar()?;
267 }
268
269 Decoder::Vector4(inner) => {
270 inner.skip(ctx, br)?;
271 inner.skip(ctx, br)?;
272 inner.skip(ctx, br)?;
273 inner.skip(ctx, br)?;
274 }
275
276 Decoder::QAnglePitchYaw { bit_count } => {
277 br.skip_bits(*bit_count * 2)?;
278 }
279
280 Decoder::QAnglePrecise => {
281 let rx = br.read_bool()?;
282 let ry = br.read_bool()?;
283 let rz = br.read_bool()?;
284 if rx {
285 br.skip_bits(20)?;
286 }
287 if ry {
288 br.skip_bits(20)?;
289 }
290 if rz {
291 br.skip_bits(20)?;
292 }
293 }
294
295 Decoder::QAngleBitCount { bit_count } => {
296 br.skip_bits(*bit_count * 3)?;
297 }
298
299 Decoder::QAngleCoord => {
300 br.skip_bitvec3coord()?;
301 }
302
303 Decoder::Default => {
304 br.skip_varint()?;
305 }
306 }
307 Ok(())
308 }
309}
310
311#[derive(Debug, Clone)]
314pub enum FieldSpecialDescriptor {
315 FixedArray { length: usize },
317 DynamicArray { inner_decoder: Decoder },
319 DynamicSerializerArray,
321 Pointer,
323}
324
325#[derive(Debug, Clone)]
327pub struct FieldMetadata {
328 pub decoder: Decoder,
329 pub special: Option<FieldSpecialDescriptor>,
330}
331
332impl Default for FieldMetadata {
333 fn default() -> Self {
334 Self {
335 decoder: Decoder::Default,
336 special: None,
337 }
338 }
339}
340
341impl FieldMetadata {
342 pub fn is_dynamic_array(&self) -> bool {
343 matches!(
344 self.special,
345 Some(FieldSpecialDescriptor::DynamicArray { .. })
346 | Some(FieldSpecialDescriptor::DynamicSerializerArray)
347 )
348 }
349
350 pub fn is_fixed_array(&self) -> bool {
351 matches!(
352 self.special,
353 Some(FieldSpecialDescriptor::FixedArray { .. })
354 )
355 }
356
357 pub fn fixed_array_length(&self) -> Option<usize> {
358 match &self.special {
359 Some(FieldSpecialDescriptor::FixedArray { length }) => Some(*length),
360 _ => None,
361 }
362 }
363
364 pub fn is_dynamic_serializer_array(&self) -> bool {
365 matches!(
366 self.special,
367 Some(FieldSpecialDescriptor::DynamicSerializerArray)
368 )
369 }
370
371 pub fn is_pointer(&self) -> bool {
372 matches!(self.special, Some(FieldSpecialDescriptor::Pointer))
373 }
374
375 pub fn dynamic_array_inner_metadata(&self) -> FieldMetadata {
376 match &self.special {
377 Some(FieldSpecialDescriptor::DynamicArray { inner_decoder }) => FieldMetadata {
378 decoder: inner_decoder.clone(),
379 special: None,
380 },
381 _ => FieldMetadata::default(),
382 }
383 }
384}
385
386fn build_f32_decoder(
388 var_name: &str,
389 bit_count: Option<i32>,
390 low_value: Option<f32>,
391 high_value: Option<f32>,
392 encode_flags: Option<i32>,
393 var_encoder: Option<&str>,
394) -> Decoder {
395 if var_name == "m_flSimulationTime" || var_name == "m_flAnimTime" {
397 return Decoder::F32SimulationTime;
398 }
399
400 if let Some(encoder) = var_encoder {
402 match encoder {
403 "coord" => return Decoder::F32Coord,
404 "normal" => return Decoder::F32Normal,
405 _ => {}
406 }
407 }
408
409 let bc = bit_count.unwrap_or(0);
410 if bc == 0 || bc == 32 {
411 return Decoder::F32NoScale;
412 }
413
414 match QuantizedFloat::new(
416 bc,
417 encode_flags.unwrap_or(0),
418 low_value.unwrap_or(0.0),
419 high_value.unwrap_or(0.0),
420 ) {
421 Ok(qf) => Decoder::F32Quantized(qf),
422 Err(_) => Decoder::F32NoScale,
423 }
424}
425
426#[allow(clippy::too_many_arguments)]
450pub fn get_field_metadata(
451 var_type: &str,
452 var_name: &str,
453 bit_count: Option<i32>,
454 low_value: Option<f32>,
455 high_value: Option<f32>,
456 encode_flags: Option<i32>,
457 var_encoder: Option<&str>,
458 has_field_serializer: bool,
459) -> FieldMetadata {
460 let trimmed = var_type.trim();
462
463 if trimmed.ends_with('*') {
465 return FieldMetadata {
466 decoder: Decoder::Bool,
467 special: Some(FieldSpecialDescriptor::Pointer),
468 };
469 }
470
471 if let Some(bracket_pos) = trimmed.find('[')
473 && trimmed.ends_with(']')
474 {
475 let base = trimmed[..bracket_pos].trim();
476 let len_str = trimmed[bracket_pos + 1..trimmed.len() - 1].trim();
477
478 if base == "char" {
480 return FieldMetadata {
481 decoder: Decoder::String,
482 special: None,
483 };
484 }
485
486 let length = len_str.parse::<usize>().unwrap_or(match len_str {
487 "MAX_ABILITY_DRAFT_ABILITIES" => 48,
488 "DOTA_ABILITY_DRAFT_HEROES_PER_GAME" => 10,
489 _ => 64,
490 });
491
492 let inner = get_field_metadata(
493 base,
494 var_name,
495 bit_count,
496 low_value,
497 high_value,
498 encode_flags,
499 var_encoder,
500 has_field_serializer,
501 );
502
503 return FieldMetadata {
504 decoder: inner.decoder,
505 special: Some(FieldSpecialDescriptor::FixedArray { length }),
506 };
507 }
508
509 if let Some(angle_pos) = trimmed.find('<')
511 && let Some(close_pos) = trimmed.rfind('>')
512 {
513 let base = trimmed[..angle_pos].trim();
514 let inner_type = trimmed[angle_pos + 1..close_pos].trim();
515
516 let is_vector_base = matches!(
517 base,
518 "CNetworkUtlVectorBase" | "CUtlVectorEmbeddedNetworkVar" | "CUtlVector"
519 );
520
521 if is_vector_base {
522 if has_field_serializer {
523 return FieldMetadata {
524 decoder: Decoder::U64,
525 special: Some(FieldSpecialDescriptor::DynamicSerializerArray),
526 };
527 }
528
529 let inner = get_field_metadata(
530 inner_type,
531 var_name,
532 bit_count,
533 low_value,
534 high_value,
535 encode_flags,
536 var_encoder,
537 has_field_serializer,
538 );
539
540 return FieldMetadata {
541 decoder: Decoder::U64,
542 special: Some(FieldSpecialDescriptor::DynamicArray {
543 inner_decoder: inner.decoder,
544 }),
545 };
546 }
547
548 return get_field_metadata(
550 base,
551 var_name,
552 bit_count,
553 low_value,
554 high_value,
555 encode_flags,
556 var_encoder,
557 has_field_serializer,
558 );
559 }
560
561 match trimmed {
563 "int8" | "int16" | "int32" | "int64" => FieldMetadata {
565 decoder: Decoder::I64,
566 special: None,
567 },
568
569 "bool" => FieldMetadata {
570 decoder: Decoder::Bool,
571 special: None,
572 },
573
574 "float32" | "CNetworkedQuantizedFloat" | "GameTime_t" => {
575 let decoder = build_f32_decoder(
576 var_name,
577 bit_count,
578 low_value,
579 high_value,
580 encode_flags,
581 var_encoder,
582 );
583 FieldMetadata {
584 decoder,
585 special: None,
586 }
587 }
588
589 "CBodyComponentDCGBaseAnimating"
592 | "CBodyComponentBaseAnimating"
593 | "CBodyComponentBaseAnimatingOverlay"
594 | "CBodyComponentBaseModelEntity"
595 | "CBodyComponent"
596 | "CBodyComponentSkeletonInstance"
597 | "CBodyComponentPoint"
598 | "CLightComponent"
599 | "CRenderComponent"
600 | "C_BodyComponentBaseAnimating"
601 | "C_BodyComponentBaseAnimatingOverlay"
602 | "CPhysicsComponent" => FieldMetadata {
603 decoder: Decoder::Bool,
604 special: Some(FieldSpecialDescriptor::Pointer),
605 },
606
607 "CUtlSymbolLarge" | "CUtlString" | "CGlobalSymbol" | "char" => FieldMetadata {
609 decoder: Decoder::String,
610 special: None,
611 },
612
613 "CUtlBinaryBlock" => FieldMetadata {
615 decoder: Decoder::BinaryBlock,
616 special: None,
617 },
618
619 "Quaternion" | "CTransform" => {
621 let count = if trimmed == "CTransform" { 6 } else { 4 };
622 let inner = build_f32_decoder(
623 var_name,
624 bit_count,
625 low_value,
626 high_value,
627 encode_flags,
628 var_encoder,
629 );
630 FieldMetadata {
631 decoder: Decoder::FloatVecN {
632 count,
633 inner: Box::new(inner),
634 },
635 special: None,
636 }
637 }
638
639 "QAngle" => {
641 let bc = bit_count.unwrap_or(0) as usize;
642 let decoder = if let Some(encoder) = var_encoder {
643 match encoder {
644 "qangle_pitch_yaw" => Decoder::QAnglePitchYaw { bit_count: bc },
645 "qangle_precise" => Decoder::QAnglePrecise,
646 _ => {
647 if bc == 0 {
648 Decoder::QAngleCoord
649 } else {
650 Decoder::QAngleBitCount { bit_count: bc }
651 }
652 }
653 }
654 } else if bc == 0 {
655 Decoder::QAngleCoord
656 } else {
657 Decoder::QAngleBitCount { bit_count: bc }
658 };
659 FieldMetadata {
660 decoder,
661 special: None,
662 }
663 }
664
665 "Vector" | "VectorWS" => {
667 if var_encoder == Some("normal") {
668 FieldMetadata {
669 decoder: Decoder::Vector3Normal,
670 special: None,
671 }
672 } else {
673 let inner = build_f32_decoder(
674 var_name,
675 bit_count,
676 low_value,
677 high_value,
678 encode_flags,
679 var_encoder,
680 );
681 FieldMetadata {
682 decoder: Decoder::Vector3(Box::new(inner)),
683 special: None,
684 }
685 }
686 }
687
688 "Vector2D" => {
689 let inner = build_f32_decoder(
690 var_name,
691 bit_count,
692 low_value,
693 high_value,
694 encode_flags,
695 var_encoder,
696 );
697 FieldMetadata {
698 decoder: Decoder::Vector2(Box::new(inner)),
699 special: None,
700 }
701 }
702
703 "Vector4D" => {
704 let inner = build_f32_decoder(
705 var_name,
706 bit_count,
707 low_value,
708 high_value,
709 encode_flags,
710 var_encoder,
711 );
712 FieldMetadata {
713 decoder: Decoder::Vector4(Box::new(inner)),
714 special: None,
715 }
716 }
717
718 "m_SpeechBubbles" | "DOTA_CombatLogQueryProgress" => FieldMetadata {
720 decoder: Decoder::U64,
721 special: Some(FieldSpecialDescriptor::DynamicSerializerArray),
722 },
723
724 _ => {
726 let decoder = if var_encoder == Some("fixed64") {
727 Decoder::U64Fixed64
728 } else {
729 Decoder::U64
730 };
731 FieldMetadata {
732 decoder,
733 special: None,
734 }
735 }
736 }
737}
738
739#[cfg(test)]
740mod tests {
741 use super::*;
742 use crate::io::BitReader;
743
744 fn meta(var_type: &str, var_name: &str) -> FieldMetadata {
745 get_field_metadata(var_type, var_name, None, None, None, None, None, false)
746 }
747
748 #[allow(clippy::too_many_arguments)]
749 fn meta_full(
750 var_type: &str,
751 var_name: &str,
752 bit_count: Option<i32>,
753 low: Option<f32>,
754 high: Option<f32>,
755 encode_flags: Option<i32>,
756 var_encoder: Option<&str>,
757 has_fs: bool,
758 ) -> FieldMetadata {
759 get_field_metadata(
760 var_type,
761 var_name,
762 bit_count,
763 low,
764 high,
765 encode_flags,
766 var_encoder,
767 has_fs,
768 )
769 }
770
771 #[test]
774 fn pointer_type() {
775 let m = meta("CBaseEntity*", "m_hOwner");
776 assert!(matches!(m.decoder, Decoder::Bool));
777 assert!(m.is_pointer());
778 }
779
780 #[test]
781 fn bool_type() {
782 let m = meta("bool", "m_bActive");
783 assert!(matches!(m.decoder, Decoder::Bool));
784 assert!(m.special.is_none());
785 }
786
787 #[test]
788 fn int32_type() {
789 let m = meta("int32", "m_iHealth");
790 assert!(matches!(m.decoder, Decoder::I64));
791 }
792
793 #[test]
794 fn float32_no_scale() {
795 let m = meta("float32", "m_flValue");
796 assert!(matches!(m.decoder, Decoder::F32NoScale));
797 }
798
799 #[test]
800 fn simulation_time() {
801 let m = meta("float32", "m_flSimulationTime");
802 assert!(matches!(m.decoder, Decoder::F32SimulationTime));
803 }
804
805 #[test]
806 fn coord_encoder() {
807 let m = meta_full(
808 "float32",
809 "m_x",
810 None,
811 None,
812 None,
813 None,
814 Some("coord"),
815 false,
816 );
817 assert!(matches!(m.decoder, Decoder::F32Coord));
818 }
819
820 #[test]
821 fn quantized_float() {
822 let m = meta_full(
823 "float32",
824 "m_val",
825 Some(8),
826 Some(0.0),
827 Some(255.0),
828 None,
829 None,
830 false,
831 );
832 assert!(matches!(m.decoder, Decoder::F32Quantized(_)));
833 }
834
835 #[test]
836 fn string_utl_symbol() {
837 let m = meta("CUtlSymbolLarge", "m_iszName");
838 assert!(matches!(m.decoder, Decoder::String));
839 }
840
841 #[test]
842 fn char_array_is_string() {
843 let m = meta("char[256]", "m_szName");
844 assert!(matches!(m.decoder, Decoder::String));
845 assert!(!m.is_fixed_array());
846 }
847
848 #[test]
849 fn int32_array_is_fixed_array() {
850 let m = meta("int32[4]", "m_values");
851 assert!(m.is_fixed_array());
852 assert_eq!(m.fixed_array_length(), Some(4));
853 }
854
855 #[test]
856 fn dynamic_array_without_serializer() {
857 let m = meta("CNetworkUtlVectorBase< int32 >", "m_items");
858 assert!(m.is_dynamic_array());
859 assert!(!m.is_dynamic_serializer_array());
860 }
861
862 #[test]
863 fn dynamic_serializer_array() {
864 let m = meta_full(
865 "CNetworkUtlVectorBase< SomeType >",
866 "m_items",
867 None,
868 None,
869 None,
870 None,
871 None,
872 true,
873 );
874 assert!(m.is_dynamic_serializer_array());
875 }
876
877 #[test]
878 fn qangle_no_encoder_no_bits() {
879 let m = meta("QAngle", "m_angle");
880 assert!(matches!(m.decoder, Decoder::QAngleCoord));
881 }
882
883 #[test]
884 fn qangle_with_bitcount() {
885 let m = meta_full("QAngle", "m_angle", Some(16), None, None, None, None, false);
886 assert!(matches!(
887 m.decoder,
888 Decoder::QAngleBitCount { bit_count: 16 }
889 ));
890 }
891
892 #[test]
893 fn qangle_pitch_yaw() {
894 let m = meta_full(
895 "QAngle",
896 "m_angle",
897 Some(10),
898 None,
899 None,
900 None,
901 Some("qangle_pitch_yaw"),
902 false,
903 );
904 assert!(matches!(
905 m.decoder,
906 Decoder::QAnglePitchYaw { bit_count: 10 }
907 ));
908 }
909
910 #[test]
913 fn decode_bool_from_1bit() {
914 let data = [0x01];
915 let mut br = BitReader::new(&data);
916 let mut ctx = FieldDecodeContext::new(1.0 / 64.0);
917 let val = Decoder::Bool.decode(&mut ctx, &mut br).unwrap();
918 assert!(matches!(val, FieldValue::Bool(true)));
919 }
920
921 #[test]
922 fn decode_f32_no_scale() {
923 let bytes = 1.5f32.to_le_bytes();
924 let mut br = BitReader::new(&bytes);
925 let mut ctx = FieldDecodeContext::new(1.0 / 64.0);
926 let val = Decoder::F32NoScale.decode(&mut ctx, &mut br).unwrap();
927 if let FieldValue::F32(f) = val {
928 assert!((f - 1.5).abs() < f32::EPSILON);
929 } else {
930 panic!("expected F32");
931 }
932 }
933
934 #[test]
935 fn decode_string_null_terminated() {
936 let data = b"hello\0";
937 let mut br = BitReader::new(data);
938 let mut ctx = FieldDecodeContext::new(1.0 / 64.0);
939 let val = Decoder::String.decode(&mut ctx, &mut br).unwrap();
940 if let FieldValue::String(s) = val {
941 assert_eq!(&s, b"hello");
942 } else {
943 panic!("expected String");
944 }
945 }
946
947 #[test]
950 fn field_metadata_helpers() {
951 let dyn_arr = FieldMetadata {
952 decoder: Decoder::U64,
953 special: Some(FieldSpecialDescriptor::DynamicArray {
954 inner_decoder: Decoder::I64,
955 }),
956 };
957 assert!(dyn_arr.is_dynamic_array());
958 assert!(!dyn_arr.is_fixed_array());
959 assert!(!dyn_arr.is_pointer());
960
961 let fixed = FieldMetadata {
962 decoder: Decoder::I64,
963 special: Some(FieldSpecialDescriptor::FixedArray { length: 8 }),
964 };
965 assert!(fixed.is_fixed_array());
966 assert_eq!(fixed.fixed_array_length(), Some(8));
967
968 let ptr = FieldMetadata {
969 decoder: Decoder::Bool,
970 special: Some(FieldSpecialDescriptor::Pointer),
971 };
972 assert!(ptr.is_pointer());
973 }
974}