Skip to main content

libmagic_rs/evaluator/
types.rs

1// Copyright (c) 2025-2026 the libmagic-rs contributors
2// SPDX-License-Identifier: Apache-2.0
3
4//! Type interpretation for reading and converting bytes from file buffers
5//!
6//! This module provides functions for safely reading different data types from byte buffers
7//! with proper bounds checking and error handling.
8
9use crate::parser::ast::{Endianness, TypeKind, Value};
10use byteorder::{BigEndian, ByteOrder, LittleEndian, NativeEndian};
11use thiserror::Error;
12
13/// Errors that can occur during type reading operations
14#[derive(Debug, Error, PartialEq, Eq)]
15pub enum TypeReadError {
16    /// Buffer access beyond available data
17    #[error(
18        "Buffer overrun: attempted to read at offset {offset} but buffer length is {buffer_len}"
19    )]
20    BufferOverrun {
21        /// The offset that was attempted to be accessed
22        offset: usize,
23        /// The actual length of the buffer
24        buffer_len: usize,
25    },
26    /// Unsupported type variant
27    #[error("Unsupported type: {type_name}")]
28    UnsupportedType {
29        /// The name of the unsupported type
30        type_name: String,
31    },
32}
33
34/// Safely reads a single byte from the buffer at the specified offset
35///
36/// This function provides secure byte reading with comprehensive bounds checking
37/// to prevent buffer overruns and potential security vulnerabilities.
38///
39/// # Arguments
40///
41/// * `buffer` - The byte buffer to read from
42/// * `offset` - The offset position to read the byte from
43/// * `signed` - Whether to interpret the byte as signed (`i8`) or unsigned (`u8`)
44///
45/// # Returns
46///
47/// Returns `Ok(Value::Uint(byte_value))` for unsigned reads or
48/// `Ok(Value::Int(byte_value))` for signed reads if the read is successful, or
49/// `Err(TypeReadError::BufferOverrun)` if the offset is beyond the buffer bounds.
50///
51/// # Security
52///
53/// This function performs strict bounds checking to prevent:
54/// - Buffer overruns that could lead to memory safety issues
55/// - Reading uninitialized or out-of-bounds memory
56/// - Integer overflow in offset calculations
57///
58/// # Examples
59///
60/// ```
61/// use libmagic_rs::evaluator::types::read_byte;
62/// use libmagic_rs::parser::ast::Value;
63///
64/// let buffer = &[0x7f, 0x80, 0x4c, 0x46]; // example bytes
65///
66/// // Read unsigned byte (0x80 = 128)
67/// let result = read_byte(buffer, 1, false).unwrap();
68/// assert_eq!(result, Value::Uint(0x80));
69///
70/// // Read signed byte (0x80 = -128)
71/// let result = read_byte(buffer, 1, true).unwrap();
72/// assert_eq!(result, Value::Int(-128));
73/// ```
74///
75/// # Errors
76///
77/// Returns `TypeReadError::BufferOverrun` if the offset is greater than or equal to
78/// the buffer length.
79pub fn read_byte(buffer: &[u8], offset: usize, signed: bool) -> Result<Value, TypeReadError> {
80    buffer
81        .get(offset)
82        .map(|&byte| {
83            if signed {
84                // Wrapping is intentional: e.g., 0x80 -> -128 as i8
85                #[allow(clippy::cast_possible_wrap)]
86                Value::Int(i64::from(byte as i8))
87            } else {
88                Value::Uint(u64::from(byte))
89            }
90        })
91        .ok_or(TypeReadError::BufferOverrun {
92            offset,
93            buffer_len: buffer.len(),
94        })
95}
96
97/// Safely reads a 16-bit integer from the buffer at the specified offset
98///
99/// # Arguments
100///
101/// * `buffer` - The byte buffer to read from
102/// * `offset` - The offset position to read the 16-bit value from
103/// * `endian` - The byte order to use for interpretation
104/// * `signed` - Whether to interpret the value as signed or unsigned
105///
106/// # Returns
107///
108/// Returns `Ok(Value::Uint(value))` for unsigned values or `Ok(Value::Int(value))` for signed values
109/// if the read is successful, or `Err(TypeReadError::BufferOverrun)` if there are insufficient bytes.
110///
111/// # Examples
112///
113/// ```
114/// use libmagic_rs::evaluator::types::read_short;
115/// use libmagic_rs::parser::ast::{Endianness, Value};
116///
117/// let buffer = &[0x34, 0x12, 0xff, 0x7f]; // Little-endian data
118///
119/// // Read unsigned little-endian short (0x1234)
120/// let result = read_short(buffer, 0, Endianness::Little, false).unwrap();
121/// assert_eq!(result, Value::Uint(0x1234));
122///
123/// // Read signed little-endian short (0x7fff = 32767)
124/// let result = read_short(buffer, 2, Endianness::Little, true).unwrap();
125/// assert_eq!(result, Value::Int(32767));
126/// ```
127///
128/// # Errors
129///
130/// Returns `TypeReadError::BufferOverrun` if there are fewer than 2 bytes available
131/// starting at the specified offset.
132pub fn read_short(
133    buffer: &[u8],
134    offset: usize,
135    endian: Endianness,
136    signed: bool,
137) -> Result<Value, TypeReadError> {
138    let bytes = buffer
139        .get(offset..offset + 2)
140        .ok_or(TypeReadError::BufferOverrun {
141            offset,
142            buffer_len: buffer.len(),
143        })?;
144
145    let value = match endian {
146        Endianness::Little => LittleEndian::read_u16(bytes),
147        Endianness::Big => BigEndian::read_u16(bytes),
148        Endianness::Native => NativeEndian::read_u16(bytes),
149    };
150
151    if signed {
152        #[allow(clippy::cast_possible_wrap)]
153        Ok(Value::Int(i64::from(value as i16)))
154    } else {
155        Ok(Value::Uint(u64::from(value)))
156    }
157}
158
159/// Safely reads a 32-bit integer from the buffer at the specified offset
160///
161/// # Arguments
162///
163/// * `buffer` - The byte buffer to read from
164/// * `offset` - The offset position to read the 32-bit value from
165/// * `endian` - The byte order to use for interpretation
166/// * `signed` - Whether to interpret the value as signed or unsigned
167///
168/// # Returns
169///
170/// Returns `Ok(Value::Uint(value))` for unsigned values or `Ok(Value::Int(value))` for signed values
171/// if the read is successful, or `Err(TypeReadError::BufferOverrun)` if there are insufficient bytes.
172///
173/// # Examples
174///
175/// ```
176/// use libmagic_rs::evaluator::types::read_long;
177/// use libmagic_rs::parser::ast::{Endianness, Value};
178///
179/// let buffer = &[0x78, 0x56, 0x34, 0x12, 0xff, 0xff, 0xff, 0x7f];
180///
181/// // Read unsigned little-endian long (0x12345678)
182/// let result = read_long(buffer, 0, Endianness::Little, false).unwrap();
183/// assert_eq!(result, Value::Uint(0x12345678));
184///
185/// // Read signed little-endian long (0x7fffffff = 2147483647)
186/// let result = read_long(buffer, 4, Endianness::Little, true).unwrap();
187/// assert_eq!(result, Value::Int(2147483647));
188/// ```
189///
190/// # Errors
191///
192/// Returns `TypeReadError::BufferOverrun` if there are fewer than 4 bytes available
193/// starting at the specified offset.
194pub fn read_long(
195    buffer: &[u8],
196    offset: usize,
197    endian: Endianness,
198    signed: bool,
199) -> Result<Value, TypeReadError> {
200    let bytes = buffer
201        .get(offset..offset + 4)
202        .ok_or(TypeReadError::BufferOverrun {
203            offset,
204            buffer_len: buffer.len(),
205        })?;
206
207    let value = match endian {
208        Endianness::Little => LittleEndian::read_u32(bytes),
209        Endianness::Big => BigEndian::read_u32(bytes),
210        Endianness::Native => NativeEndian::read_u32(bytes),
211    };
212
213    if signed {
214        #[allow(clippy::cast_possible_wrap)]
215        Ok(Value::Int(i64::from(value as i32)))
216    } else {
217        Ok(Value::Uint(u64::from(value)))
218    }
219}
220
221/// Safely reads a null-terminated string from the buffer at the specified offset
222///
223/// This function reads bytes from the buffer starting at the given offset until it encounters
224/// a null byte (0x00) or reaches the maximum length limit. The resulting bytes are converted
225/// to a UTF-8 string with proper error handling for invalid sequences.
226///
227/// # Arguments
228///
229/// * `buffer` - The byte buffer to read from
230/// * `offset` - The offset position to start reading the string from
231/// * `max_length` - Optional maximum number of bytes to read excluding the null terminator.
232///   If a NUL is found within `max_length` bytes, it is not counted in the result length.
233///   If no NUL is found, up to `max_length` bytes are returned with no trailing NUL.
234///   When `None`, reads until the first NUL or end of buffer.
235///
236/// # Returns
237///
238/// Returns `Ok(Value::String(string))` if the read is successful, or an appropriate error
239/// if the read fails due to buffer overrun or invalid UTF-8 sequences.
240///
241/// # Security
242///
243/// This function provides several security guarantees:
244/// - Bounds checking prevents reading beyond buffer limits
245/// - Length limits prevent excessive memory allocation
246/// - UTF-8 validation ensures string safety
247/// - Null termination handling prevents runaway reads
248///
249/// # Examples
250///
251/// ```
252/// use libmagic_rs::evaluator::types::read_string;
253/// use libmagic_rs::parser::ast::Value;
254///
255/// // Null-terminated string
256/// let buffer = b"Hello\x00World";
257/// let result = read_string(buffer, 0, None).unwrap();
258/// assert_eq!(result, Value::String("Hello".to_string()));
259///
260/// // String with length limit
261/// let buffer = b"VeryLongString\x00";
262/// let result = read_string(buffer, 0, Some(4)).unwrap();
263/// assert_eq!(result, Value::String("Very".to_string()));
264///
265/// // String without null terminator (reads to max_length)
266/// let buffer = b"NoNull";
267/// let result = read_string(buffer, 0, Some(6)).unwrap();
268/// assert_eq!(result, Value::String("NoNull".to_string()));
269///
270/// // NUL found within max_length (NUL not counted in result)
271/// let buffer = b"Hello\x00World";
272/// let result = read_string(buffer, 0, Some(10)).unwrap();
273/// assert_eq!(result, Value::String("Hello".to_string()));
274///
275/// // No NUL found, returns exactly max_length bytes
276/// let buffer = b"ABCDEF";
277/// let result = read_string(buffer, 0, Some(4)).unwrap();
278/// assert_eq!(result, Value::String("ABCD".to_string()));
279/// ```
280///
281/// # Errors
282///
283/// Returns `TypeReadError::BufferOverrun` if the offset is greater than or equal to the buffer length.
284pub fn read_string(
285    buffer: &[u8],
286    offset: usize,
287    max_length: Option<usize>,
288) -> Result<Value, TypeReadError> {
289    // Check if offset is within buffer bounds
290    if offset >= buffer.len() {
291        return Err(TypeReadError::BufferOverrun {
292            offset,
293            buffer_len: buffer.len(),
294        });
295    }
296
297    // Get the slice starting from offset
298    let remaining_buffer = &buffer[offset..];
299
300    // Determine the actual length to read (uses memchr for efficient null byte scanning)
301    let read_length = if let Some(max_len) = max_length {
302        // Find null terminator within max_length, or use max_length if no null found
303        let search_len = std::cmp::min(max_len, remaining_buffer.len());
304        memchr::memchr(0, &remaining_buffer[..search_len]).unwrap_or(search_len)
305    } else {
306        // Find null terminator in entire remaining buffer
307        memchr::memchr(0, remaining_buffer).unwrap_or(remaining_buffer.len())
308    };
309
310    // Extract the string bytes (excluding null terminator)
311    let string_bytes = &remaining_buffer[..read_length];
312
313    // Convert to UTF-8 string, replacing invalid sequences with replacement character
314    let string_value = String::from_utf8_lossy(string_bytes).into_owned();
315
316    Ok(Value::String(string_value))
317}
318
319/// Reads and interprets bytes according to the specified `TypeKind`
320///
321/// This is the main interface for type interpretation that dispatches to the appropriate
322/// reading function based on the `TypeKind` variant.
323///
324/// # Arguments
325///
326/// * `buffer` - The byte buffer to read from
327/// * `offset` - The offset position to read from
328/// * `type_kind` - The type specification that determines how to interpret the bytes
329///
330/// # Returns
331///
332/// Returns the interpreted value as a `Value` enum variant, or an error if the read fails.
333///
334/// # Examples
335///
336/// ```
337/// use libmagic_rs::evaluator::types::read_typed_value;
338/// use libmagic_rs::parser::ast::{TypeKind, Endianness, Value};
339///
340/// let buffer = &[0x7f, 0x45, 0x4c, 0x46, 0x34, 0x12];
341///
342/// // Read an unsigned byte
343/// let byte_result = read_typed_value(buffer, 0, &TypeKind::Byte { signed: false }).unwrap();
344/// assert_eq!(byte_result, Value::Uint(0x7f));
345///
346/// // Read a little-endian short
347/// let short_type = TypeKind::Short {
348///     endian: Endianness::Little,
349///     signed: false,
350/// };
351/// let short_result = read_typed_value(buffer, 4, &short_type).unwrap();
352/// assert_eq!(short_result, Value::Uint(0x1234));
353/// ```
354///
355/// # Errors
356///
357/// Returns `TypeReadError::BufferOverrun` if there are insufficient bytes for the requested type,
358/// or `TypeReadError::UnsupportedType` for type variants that are not yet implemented.
359pub fn read_typed_value(
360    buffer: &[u8],
361    offset: usize,
362    type_kind: &TypeKind,
363) -> Result<Value, TypeReadError> {
364    match type_kind {
365        TypeKind::Byte { signed } => read_byte(buffer, offset, *signed),
366        TypeKind::Short { endian, signed } => read_short(buffer, offset, *endian, *signed),
367        TypeKind::Long { endian, signed } => read_long(buffer, offset, *endian, *signed),
368        TypeKind::String { max_length } => read_string(buffer, offset, *max_length),
369    }
370}
371
372/// Coerce a rule's expected value to match the type's signedness and width.
373///
374/// In libmagic, comparison values like `0xff` in `0 byte =0xff` are interpreted
375/// at the type's bit width. For a signed byte, `0xff` means `-1` (the signed
376/// interpretation of that bit pattern). This function performs that coercion so
377/// that comparisons work correctly regardless of how the value was parsed.
378///
379/// Only affects `Value::Uint` values paired with signed types whose values exceed
380/// the signed range. All other combinations pass through unchanged.
381///
382/// # Examples
383///
384/// ```
385/// use libmagic_rs::evaluator::types::coerce_value_to_type;
386/// use libmagic_rs::parser::ast::{TypeKind, Value};
387///
388/// // 0xff for signed byte -> -1
389/// let coerced = coerce_value_to_type(&Value::Uint(0xff), &TypeKind::Byte { signed: true });
390/// assert_eq!(coerced, Value::Int(-1));
391///
392/// // 0x7f for signed byte -> unchanged (fits in signed range)
393/// let coerced = coerce_value_to_type(&Value::Uint(0x7f), &TypeKind::Byte { signed: true });
394/// assert_eq!(coerced, Value::Uint(0x7f));
395///
396/// // Unsigned types pass through unchanged
397/// let coerced = coerce_value_to_type(&Value::Uint(0xff), &TypeKind::Byte { signed: false });
398/// assert_eq!(coerced, Value::Uint(0xff));
399/// ```
400#[must_use]
401pub fn coerce_value_to_type(value: &Value, type_kind: &TypeKind) -> Value {
402    match (value, type_kind) {
403        (Value::Uint(v), TypeKind::Byte { signed: true }) if *v > i8::MAX as u64 =>
404        {
405            #[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
406            Value::Int(i64::from(*v as u8 as i8))
407        }
408        (Value::Uint(v), TypeKind::Short { signed: true, .. }) if *v > i16::MAX as u64 =>
409        {
410            #[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
411            Value::Int(i64::from(*v as u16 as i16))
412        }
413        (Value::Uint(v), TypeKind::Long { signed: true, .. }) if *v > i32::MAX as u64 =>
414        {
415            #[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
416            Value::Int(i64::from(*v as u32 as i32))
417        }
418        _ => value.clone(),
419    }
420}
421
422#[cfg(test)]
423mod tests {
424    use super::*;
425
426    #[test]
427    fn test_read_byte_values() {
428        // All 256 unsigned values
429        let buffer: Vec<u8> = (0..=255).collect();
430        for (i, &byte) in buffer.iter().enumerate() {
431            assert_eq!(
432                read_byte(&buffer, i, false).unwrap(),
433                Value::Uint(u64::from(byte))
434            );
435        }
436    }
437
438    #[test]
439    fn test_read_byte_out_of_bounds() {
440        // Empty buffer
441        assert_eq!(
442            read_byte(&[], 0, false).unwrap_err(),
443            TypeReadError::BufferOverrun {
444                offset: 0,
445                buffer_len: 0
446            }
447        );
448        // Just past end
449        assert_eq!(
450            read_byte(&[0x42], 1, false).unwrap_err(),
451            TypeReadError::BufferOverrun {
452                offset: 1,
453                buffer_len: 1
454            }
455        );
456        // Way past end
457        assert_eq!(
458            read_byte(&[1, 2, 3], 100, false).unwrap_err(),
459            TypeReadError::BufferOverrun {
460                offset: 100,
461                buffer_len: 3
462            }
463        );
464    }
465
466    #[test]
467    fn test_read_byte_signedness() {
468        let cases: Vec<(u8, bool, Value)> = vec![
469            (0x00, false, Value::Uint(0)),
470            (0x7f, false, Value::Uint(127)),
471            (0x80, false, Value::Uint(128)),
472            (0xff, false, Value::Uint(255)),
473            (0x00, true, Value::Int(0)),
474            (0x7f, true, Value::Int(127)),
475            (0x80, true, Value::Int(-128)),
476            (0xff, true, Value::Int(-1)),
477        ];
478        for (byte, signed, expected) in cases {
479            let result = read_byte(&[byte], 0, signed).unwrap();
480            assert_eq!(result, expected, "byte=0x{byte:02x}, signed={signed}");
481        }
482    }
483
484    #[test]
485    fn test_type_read_error_display() {
486        let error = TypeReadError::BufferOverrun {
487            offset: 10,
488            buffer_len: 5,
489        };
490        let msg = format!("{error}");
491        assert!(msg.contains("offset 10"));
492        assert!(msg.contains("buffer length is 5"));
493    }
494
495    // Tests for read_short function
496    #[test]
497    fn test_read_short_little_endian_unsigned() {
498        let buffer = &[0x34, 0x12, 0x78, 0x56]; // 0x1234, 0x5678 in little-endian
499
500        // Read first short (0x1234)
501        let result = read_short(buffer, 0, Endianness::Little, false).unwrap();
502        assert_eq!(result, Value::Uint(0x1234));
503
504        // Read second short (0x5678)
505        let result = read_short(buffer, 2, Endianness::Little, false).unwrap();
506        assert_eq!(result, Value::Uint(0x5678));
507    }
508
509    #[test]
510    fn test_read_short_big_endian_unsigned() {
511        let buffer = &[0x12, 0x34, 0x56, 0x78]; // 0x1234, 0x5678 in big-endian
512
513        // Read first short (0x1234)
514        let result = read_short(buffer, 0, Endianness::Big, false).unwrap();
515        assert_eq!(result, Value::Uint(0x1234));
516
517        // Read second short (0x5678)
518        let result = read_short(buffer, 2, Endianness::Big, false).unwrap();
519        assert_eq!(result, Value::Uint(0x5678));
520    }
521
522    #[test]
523    fn test_read_short_native_endian_unsigned() {
524        let buffer = &[0x34, 0x12, 0x78, 0x56];
525
526        // Read using native endianness
527        let result = read_short(buffer, 0, Endianness::Native, false).unwrap();
528
529        // The exact value depends on the system's endianness, but it should be valid
530        match result {
531            Value::Uint(val) => {
532                // Should be either 0x1234 (little-endian) or 0x3412 (big-endian)
533                assert!(val == 0x1234 || val == 0x3412);
534            }
535            _ => panic!("Expected Value::Uint variant"),
536        }
537    }
538
539    #[test]
540    fn test_read_short_signed_positive() {
541        let buffer = &[0xff, 0x7f]; // 0x7fff = 32767 in little-endian
542
543        let result = read_short(buffer, 0, Endianness::Little, true).unwrap();
544        assert_eq!(result, Value::Int(32767));
545    }
546
547    #[test]
548    fn test_read_short_signed_negative() {
549        let buffer = &[0x00, 0x80]; // 0x8000 = -32768 in little-endian (signed)
550
551        let result = read_short(buffer, 0, Endianness::Little, true).unwrap();
552        assert_eq!(result, Value::Int(-32768));
553    }
554
555    #[test]
556    fn test_read_short_signed_vs_unsigned() {
557        let buffer = &[0xff, 0xff]; // 0xffff
558
559        // Unsigned interpretation
560        let unsigned_result = read_short(buffer, 0, Endianness::Little, false).unwrap();
561        assert_eq!(unsigned_result, Value::Uint(65535));
562
563        // Signed interpretation
564        let signed_result = read_short(buffer, 0, Endianness::Little, true).unwrap();
565        assert_eq!(signed_result, Value::Int(-1));
566    }
567
568    #[test]
569    fn test_read_short_buffer_overrun() {
570        let buffer = &[0x12]; // Only 1 byte available
571
572        // Should fail when trying to read 2 bytes
573        let result = read_short(buffer, 0, Endianness::Little, false);
574        assert!(result.is_err());
575        assert_eq!(
576            result.unwrap_err(),
577            TypeReadError::BufferOverrun {
578                offset: 0,
579                buffer_len: 1
580            }
581        );
582    }
583
584    #[test]
585    fn test_read_short_offset_out_of_bounds() {
586        let buffer = &[0x12, 0x34, 0x56];
587
588        // Should fail when trying to read 2 bytes starting at offset 2 (only 1 byte left)
589        let result = read_short(buffer, 2, Endianness::Little, false);
590        assert!(result.is_err());
591        assert_eq!(
592            result.unwrap_err(),
593            TypeReadError::BufferOverrun {
594                offset: 2,
595                buffer_len: 3
596            }
597        );
598    }
599
600    #[test]
601    fn test_read_short_empty_buffer() {
602        let buffer = &[];
603
604        let result = read_short(buffer, 0, Endianness::Little, false);
605        assert!(result.is_err());
606        assert_eq!(
607            result.unwrap_err(),
608            TypeReadError::BufferOverrun {
609                offset: 0,
610                buffer_len: 0
611            }
612        );
613    }
614
615    #[test]
616    fn test_read_short_all_endianness_variants() {
617        let buffer = &[0x12, 0x34];
618
619        // Test all endianness variants
620        let little = read_short(buffer, 0, Endianness::Little, false).unwrap();
621        let big = read_short(buffer, 0, Endianness::Big, false).unwrap();
622        let native = read_short(buffer, 0, Endianness::Native, false).unwrap();
623
624        // Little-endian: 0x3412, Big-endian: 0x1234
625        assert_eq!(little, Value::Uint(0x3412));
626        assert_eq!(big, Value::Uint(0x1234));
627
628        // Native should match one of them
629        match native {
630            Value::Uint(val) => assert!(val == 0x1234 || val == 0x3412),
631            _ => panic!("Expected Value::Uint variant"),
632        }
633    }
634
635    // Tests for read_long function
636    #[test]
637    fn test_read_long_little_endian_unsigned() {
638        let buffer = &[0x78, 0x56, 0x34, 0x12, 0xbc, 0x9a, 0x78, 0x56]; // 0x12345678, 0x56789abc
639
640        // Read first long (0x12345678)
641        let result = read_long(buffer, 0, Endianness::Little, false).unwrap();
642        assert_eq!(result, Value::Uint(0x1234_5678));
643
644        // Read second long (0x56789abc)
645        let result = read_long(buffer, 4, Endianness::Little, false).unwrap();
646        assert_eq!(result, Value::Uint(0x5678_9abc));
647    }
648
649    #[test]
650    fn test_read_long_big_endian_unsigned() {
651        let buffer = &[0x12, 0x34, 0x56, 0x78, 0x56, 0x78, 0x9a, 0xbc]; // 0x12345678, 0x56789abc
652
653        // Read first long (0x12345678)
654        let result = read_long(buffer, 0, Endianness::Big, false).unwrap();
655        assert_eq!(result, Value::Uint(0x1234_5678));
656
657        // Read second long (0x56789abc)
658        let result = read_long(buffer, 4, Endianness::Big, false).unwrap();
659        assert_eq!(result, Value::Uint(0x5678_9abc));
660    }
661
662    #[test]
663    fn test_read_long_native_endian_unsigned() {
664        let buffer = &[0x78, 0x56, 0x34, 0x12];
665
666        // Read using native endianness
667        let result = read_long(buffer, 0, Endianness::Native, false).unwrap();
668
669        // The exact value depends on the system's endianness, but it should be valid
670        match result {
671            Value::Uint(val) => {
672                // Should be either 0x12345678 (little-endian) or 0x78563412 (big-endian)
673                assert!(val == 0x1234_5678 || val == 0x7856_3412);
674            }
675            _ => panic!("Expected Value::Uint variant"),
676        }
677    }
678
679    #[test]
680    fn test_read_long_signed_positive() {
681        let buffer = &[0xff, 0xff, 0xff, 0x7f]; // 0x7fffffff = 2147483647 in little-endian
682
683        let result = read_long(buffer, 0, Endianness::Little, true).unwrap();
684        assert_eq!(result, Value::Int(2_147_483_647));
685    }
686
687    #[test]
688    fn test_read_long_signed_negative() {
689        let buffer = &[0x00, 0x00, 0x00, 0x80]; // 0x80000000 = -2147483648 in little-endian (signed)
690
691        let result = read_long(buffer, 0, Endianness::Little, true).unwrap();
692        assert_eq!(result, Value::Int(-2_147_483_648));
693    }
694
695    #[test]
696    fn test_read_long_signed_vs_unsigned() {
697        let buffer = &[0xff, 0xff, 0xff, 0xff]; // 0xffffffff
698
699        // Unsigned interpretation
700        let unsigned_result = read_long(buffer, 0, Endianness::Little, false).unwrap();
701        assert_eq!(unsigned_result, Value::Uint(4_294_967_295));
702
703        // Signed interpretation
704        let signed_result = read_long(buffer, 0, Endianness::Little, true).unwrap();
705        assert_eq!(signed_result, Value::Int(-1));
706    }
707
708    #[test]
709    fn test_read_long_buffer_overrun() {
710        let buffer = &[0x12, 0x34, 0x56]; // Only 3 bytes available
711
712        // Should fail when trying to read 4 bytes
713        let result = read_long(buffer, 0, Endianness::Little, false);
714        assert!(result.is_err());
715        assert_eq!(
716            result.unwrap_err(),
717            TypeReadError::BufferOverrun {
718                offset: 0,
719                buffer_len: 3
720            }
721        );
722    }
723
724    #[test]
725    fn test_read_long_offset_out_of_bounds() {
726        let buffer = &[0x12, 0x34, 0x56, 0x78, 0x9a];
727
728        // Should fail when trying to read 4 bytes starting at offset 2 (only 3 bytes left)
729        let result = read_long(buffer, 2, Endianness::Little, false);
730        assert!(result.is_err());
731        assert_eq!(
732            result.unwrap_err(),
733            TypeReadError::BufferOverrun {
734                offset: 2,
735                buffer_len: 5
736            }
737        );
738    }
739
740    #[test]
741    fn test_read_long_empty_buffer() {
742        let buffer = &[];
743
744        let result = read_long(buffer, 0, Endianness::Little, false);
745        assert!(result.is_err());
746        assert_eq!(
747            result.unwrap_err(),
748            TypeReadError::BufferOverrun {
749                offset: 0,
750                buffer_len: 0
751            }
752        );
753    }
754
755    #[test]
756    fn test_read_long_all_endianness_variants() {
757        let buffer = &[0x12, 0x34, 0x56, 0x78];
758
759        // Test all endianness variants
760        let little = read_long(buffer, 0, Endianness::Little, false).unwrap();
761        let big = read_long(buffer, 0, Endianness::Big, false).unwrap();
762        let native = read_long(buffer, 0, Endianness::Native, false).unwrap();
763
764        // Little-endian: 0x78563412, Big-endian: 0x12345678
765        assert_eq!(little, Value::Uint(0x7856_3412));
766        assert_eq!(big, Value::Uint(0x1234_5678));
767
768        // Native should match one of them
769        match native {
770            Value::Uint(val) => assert!(val == 0x1234_5678 || val == 0x7856_3412),
771            _ => panic!("Expected Value::Uint variant"),
772        }
773    }
774
775    #[test]
776    fn test_read_long_extreme_values() {
777        // Test maximum unsigned 32-bit value
778        let max_buffer = &[0xff, 0xff, 0xff, 0xff];
779        let max_result = read_long(max_buffer, 0, Endianness::Little, false).unwrap();
780        assert_eq!(max_result, Value::Uint(u64::from(u32::MAX)));
781
782        // Test zero value
783        let zero_buffer = &[0x00, 0x00, 0x00, 0x00];
784        let zero_result = read_long(zero_buffer, 0, Endianness::Little, false).unwrap();
785        assert_eq!(zero_result, Value::Uint(0));
786    }
787
788    #[test]
789    fn test_read_short_extreme_values() {
790        // Test maximum unsigned 16-bit value
791        let max_buffer = &[0xff, 0xff];
792        let max_result = read_short(max_buffer, 0, Endianness::Little, false).unwrap();
793        assert_eq!(max_result, Value::Uint(u64::from(u16::MAX)));
794
795        // Test zero value
796        let zero_buffer = &[0x00, 0x00];
797        let zero_result = read_short(zero_buffer, 0, Endianness::Little, false).unwrap();
798        assert_eq!(zero_result, Value::Uint(0));
799    }
800
801    #[test]
802    fn test_multi_byte_reading_consistency() {
803        // Test that reading the same bytes with different functions gives consistent results
804        let buffer = &[0x34, 0x12, 0x78, 0x56, 0xbc, 0x9a, 0xde, 0xf0];
805
806        // Read as individual bytes
807        let byte0 = read_byte(buffer, 0, false).unwrap();
808        let byte1 = read_byte(buffer, 1, false).unwrap();
809
810        // Read as short
811        let short = read_short(buffer, 0, Endianness::Little, false).unwrap();
812
813        // Verify consistency
814        match (byte0, byte1, short) {
815            (Value::Uint(b0), Value::Uint(b1), Value::Uint(s)) => {
816                assert_eq!(s, b0 + (b1 << 8)); // Little-endian composition
817            }
818            _ => panic!("Expected all Uint values"),
819        }
820    }
821
822    // Tests for UnsupportedType error
823    #[test]
824    fn test_unsupported_type_error() {
825        let error = TypeReadError::UnsupportedType {
826            type_name: "CustomType".to_string(),
827        };
828
829        let error_string = format!("{error}");
830        assert!(error_string.contains("Unsupported type"));
831        assert!(error_string.contains("CustomType"));
832    }
833
834    #[test]
835    fn test_unsupported_type_error_debug() {
836        let error = TypeReadError::UnsupportedType {
837            type_name: "TestType".to_string(),
838        };
839
840        let debug_string = format!("{error:?}");
841        assert!(debug_string.contains("UnsupportedType"));
842        assert!(debug_string.contains("TestType"));
843    }
844
845    #[test]
846    fn test_unsupported_type_error_equality() {
847        let error1 = TypeReadError::UnsupportedType {
848            type_name: "Type1".to_string(),
849        };
850        let error2 = TypeReadError::UnsupportedType {
851            type_name: "Type1".to_string(),
852        };
853        let error3 = TypeReadError::UnsupportedType {
854            type_name: "Type2".to_string(),
855        };
856
857        assert_eq!(error1, error2);
858        assert_ne!(error1, error3);
859    }
860
861    // Tests for read_typed_value function
862    #[test]
863    fn test_read_typed_value_byte() {
864        let buffer = &[0x7f, 0x45, 0x4c, 0x46];
865        let type_kind = TypeKind::Byte { signed: false };
866
867        let result = read_typed_value(buffer, 0, &type_kind).unwrap();
868        assert_eq!(result, Value::Uint(0x7f));
869
870        let result = read_typed_value(buffer, 3, &type_kind).unwrap();
871        assert_eq!(result, Value::Uint(0x46));
872    }
873
874    #[test]
875    fn test_read_typed_value_short_unsigned_little_endian() {
876        let buffer = &[0x34, 0x12, 0x78, 0x56];
877        let type_kind = TypeKind::Short {
878            endian: Endianness::Little,
879            signed: false,
880        };
881
882        let result = read_typed_value(buffer, 0, &type_kind).unwrap();
883        assert_eq!(result, Value::Uint(0x1234));
884
885        let result = read_typed_value(buffer, 2, &type_kind).unwrap();
886        assert_eq!(result, Value::Uint(0x5678));
887    }
888
889    #[test]
890    fn test_read_typed_value_short_signed_big_endian() {
891        let buffer = &[0x80, 0x00, 0x7f, 0xff];
892        let type_kind = TypeKind::Short {
893            endian: Endianness::Big,
894            signed: true,
895        };
896
897        // 0x8000 = -32768 in signed 16-bit
898        let result = read_typed_value(buffer, 0, &type_kind).unwrap();
899        assert_eq!(result, Value::Int(-32768));
900
901        // 0x7fff = 32767 in signed 16-bit
902        let result = read_typed_value(buffer, 2, &type_kind).unwrap();
903        assert_eq!(result, Value::Int(32767));
904    }
905
906    #[test]
907    fn test_read_typed_value_long_unsigned_little_endian() {
908        let buffer = &[0x78, 0x56, 0x34, 0x12, 0xbc, 0x9a, 0x78, 0x56];
909        let type_kind = TypeKind::Long {
910            endian: Endianness::Little,
911            signed: false,
912        };
913
914        let result = read_typed_value(buffer, 0, &type_kind).unwrap();
915        assert_eq!(result, Value::Uint(0x1234_5678));
916
917        let result = read_typed_value(buffer, 4, &type_kind).unwrap();
918        assert_eq!(result, Value::Uint(0x5678_9abc));
919    }
920
921    #[test]
922    fn test_read_typed_value_long_signed_big_endian() {
923        let buffer = &[0x80, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff];
924        let type_kind = TypeKind::Long {
925            endian: Endianness::Big,
926            signed: true,
927        };
928
929        // 0x80000000 = -2147483648 in signed 32-bit
930        let result = read_typed_value(buffer, 0, &type_kind).unwrap();
931        assert_eq!(result, Value::Int(-2_147_483_648));
932
933        // 0x7fffffff = 2147483647 in signed 32-bit
934        let result = read_typed_value(buffer, 4, &type_kind).unwrap();
935        assert_eq!(result, Value::Int(2_147_483_647));
936    }
937
938    #[test]
939    fn test_read_typed_value_native_endian() {
940        let buffer = &[0x34, 0x12, 0x78, 0x56, 0xbc, 0x9a, 0xde, 0xf0];
941
942        // Test short with native endianness
943        let short_type = TypeKind::Short {
944            endian: Endianness::Native,
945            signed: false,
946        };
947
948        let result = read_typed_value(buffer, 0, &short_type).unwrap();
949        match result {
950            Value::Uint(val) => {
951                // Should be either 0x1234 (little-endian) or 0x3412 (big-endian)
952                assert!(val == 0x1234 || val == 0x3412);
953            }
954            _ => panic!("Expected Value::Uint variant"),
955        }
956    }
957
958    #[test]
959    fn test_read_typed_value_string() {
960        let buffer = b"Hello\x00World\x00";
961        let type_kind = TypeKind::String { max_length: None };
962
963        let result = read_typed_value(buffer, 0, &type_kind).unwrap();
964        assert_eq!(result, Value::String("Hello".to_string()));
965
966        let result = read_typed_value(buffer, 6, &type_kind).unwrap();
967        assert_eq!(result, Value::String("World".to_string()));
968    }
969
970    #[test]
971    fn test_read_typed_value_string_with_max_length() {
972        let buffer = b"VeryLongString\x00";
973        let type_kind = TypeKind::String {
974            max_length: Some(4),
975        };
976
977        let result = read_typed_value(buffer, 0, &type_kind).unwrap();
978        assert_eq!(result, Value::String("Very".to_string()));
979    }
980
981    #[test]
982    fn test_read_typed_value_buffer_overrun() {
983        let buffer = &[0x12];
984        let type_kind = TypeKind::Short {
985            endian: Endianness::Little,
986            signed: false,
987        };
988
989        let result = read_typed_value(buffer, 0, &type_kind);
990        assert!(result.is_err());
991        assert_eq!(
992            result.unwrap_err(),
993            TypeReadError::BufferOverrun {
994                offset: 0,
995                buffer_len: 1
996            }
997        );
998    }
999
1000    // Tests for read_string function
1001    #[test]
1002    fn test_read_string_null_terminated() {
1003        let buffer = b"Hello\x00World";
1004
1005        let result = read_string(buffer, 0, None).unwrap();
1006        assert_eq!(result, Value::String("Hello".to_string()));
1007    }
1008
1009    #[test]
1010    fn test_read_string_null_terminated_at_offset() {
1011        let buffer = b"Prefix\x00Hello\x00Suffix";
1012
1013        let result = read_string(buffer, 7, None).unwrap();
1014        assert_eq!(result, Value::String("Hello".to_string()));
1015    }
1016
1017    #[test]
1018    fn test_read_string_with_max_length_shorter_than_null() {
1019        let buffer = b"VeryLongString\x00";
1020
1021        // Max length is shorter than the null terminator position
1022        let result = read_string(buffer, 0, Some(4)).unwrap();
1023        assert_eq!(result, Value::String("Very".to_string()));
1024    }
1025
1026    #[test]
1027    fn test_read_string_with_max_length_longer_than_null() {
1028        let buffer = b"Short\x00LongerSuffix";
1029
1030        // Max length is longer than the null terminator position
1031        let result = read_string(buffer, 0, Some(10)).unwrap();
1032        assert_eq!(result, Value::String("Short".to_string()));
1033    }
1034
1035    #[test]
1036    fn test_read_string_no_null_terminator_with_max_length() {
1037        let buffer = b"NoNullTerminator";
1038
1039        // Should read up to max_length when no null terminator is found
1040        let result = read_string(buffer, 0, Some(6)).unwrap();
1041        assert_eq!(result, Value::String("NoNull".to_string()));
1042    }
1043
1044    #[test]
1045    fn test_read_string_no_null_terminator_no_max_length() {
1046        let buffer = b"NoNullTerminator";
1047
1048        // Should read entire remaining buffer when no null terminator and no max_length
1049        let result = read_string(buffer, 0, None).unwrap();
1050        assert_eq!(result, Value::String("NoNullTerminator".to_string()));
1051    }
1052
1053    #[test]
1054    fn test_read_string_empty_string() {
1055        let buffer = b"\x00Hello";
1056
1057        // Should return empty string when null terminator is at offset
1058        let result = read_string(buffer, 0, None).unwrap();
1059        assert_eq!(result, Value::String(String::new()));
1060    }
1061
1062    #[test]
1063    fn test_read_string_empty_buffer() {
1064        let buffer = b"";
1065
1066        // Should fail with buffer overrun for empty buffer
1067        let result = read_string(buffer, 0, None);
1068        assert!(result.is_err());
1069        assert_eq!(
1070            result.unwrap_err(),
1071            TypeReadError::BufferOverrun {
1072                offset: 0,
1073                buffer_len: 0
1074            }
1075        );
1076    }
1077
1078    #[test]
1079    fn test_read_string_offset_out_of_bounds() {
1080        let buffer = b"Hello";
1081
1082        // Should fail when offset is beyond buffer length
1083        let result = read_string(buffer, 10, None);
1084        assert!(result.is_err());
1085        assert_eq!(
1086            result.unwrap_err(),
1087            TypeReadError::BufferOverrun {
1088                offset: 10,
1089                buffer_len: 5
1090            }
1091        );
1092    }
1093
1094    #[test]
1095    fn test_read_string_offset_at_buffer_end() {
1096        let buffer = b"Hello";
1097
1098        // Should fail when offset equals buffer length
1099        let result = read_string(buffer, 5, None);
1100        assert!(result.is_err());
1101        assert_eq!(
1102            result.unwrap_err(),
1103            TypeReadError::BufferOverrun {
1104                offset: 5,
1105                buffer_len: 5
1106            }
1107        );
1108    }
1109
1110    #[test]
1111    fn test_read_string_max_length_zero() {
1112        let buffer = b"Hello\x00World";
1113
1114        // Should return empty string when max_length is 0
1115        let result = read_string(buffer, 0, Some(0)).unwrap();
1116        assert_eq!(result, Value::String(String::new()));
1117    }
1118
1119    #[test]
1120    fn test_read_string_max_length_larger_than_buffer() {
1121        let buffer = b"Short";
1122
1123        // Should read entire buffer when max_length exceeds buffer size
1124        let result = read_string(buffer, 0, Some(100)).unwrap();
1125        assert_eq!(result, Value::String("Short".to_string()));
1126    }
1127
1128    #[test]
1129    fn test_read_string_utf8_valid() {
1130        let buffer = b"Caf\xc3\xa9\x00"; // "Café" in UTF-8
1131
1132        let result = read_string(buffer, 0, None).unwrap();
1133        assert_eq!(result, Value::String("Café".to_string()));
1134    }
1135
1136    #[test]
1137    fn test_read_string_utf8_invalid() {
1138        let buffer = b"Invalid\xff\xfe\x00"; // Invalid UTF-8 sequence
1139
1140        let result = read_string(buffer, 0, None).unwrap();
1141        // Should use replacement characters for invalid UTF-8
1142        assert!(matches!(result, Value::String(_)));
1143        if let Value::String(s) = result {
1144            assert!(s.starts_with("Invalid"));
1145            assert!(s.contains('\u{FFFD}')); // UTF-8 replacement character
1146        }
1147    }
1148
1149    #[test]
1150    fn test_read_string_binary_data() {
1151        let buffer = &[0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x00, 0x80, 0x90]; // "Hello" + binary
1152
1153        let result = read_string(buffer, 0, None).unwrap();
1154        assert_eq!(result, Value::String("Hello".to_string()));
1155    }
1156
1157    #[test]
1158    fn test_read_string_multiple_nulls() {
1159        let buffer = b"First\x00\x00Second\x00";
1160
1161        // Should stop at first null terminator
1162        let result = read_string(buffer, 0, None).unwrap();
1163        assert_eq!(result, Value::String("First".to_string()));
1164
1165        // Reading from second null should return empty string
1166        let result = read_string(buffer, 6, None).unwrap();
1167        assert_eq!(result, Value::String(String::new()));
1168    }
1169
1170    #[test]
1171    fn test_read_string_ascii_control_characters() {
1172        let buffer = b"Hello\x09World\x00"; // Tab character in string
1173
1174        let result = read_string(buffer, 0, None).unwrap();
1175        assert_eq!(result, Value::String("Hello\tWorld".to_string()));
1176    }
1177
1178    #[test]
1179    fn test_read_string_single_character() {
1180        let buffer = b"A\x00";
1181
1182        let result = read_string(buffer, 0, None).unwrap();
1183        assert_eq!(result, Value::String("A".to_string()));
1184    }
1185
1186    #[test]
1187    fn test_read_string_max_length_exact_match() {
1188        let buffer = b"Exact\x00";
1189
1190        // Max length exactly matches string length (excluding null)
1191        let result = read_string(buffer, 0, Some(5)).unwrap();
1192        assert_eq!(result, Value::String("Exact".to_string()));
1193    }
1194
1195    #[test]
1196    fn test_read_string_at_buffer_boundary() {
1197        let buffer = b"Hello";
1198
1199        // Reading from last character position
1200        let result = read_string(buffer, 4, Some(1)).unwrap();
1201        assert_eq!(result, Value::String("o".to_string()));
1202    }
1203
1204    #[test]
1205    fn test_read_string_whitespace_handling() {
1206        let buffer = b"  Spaces  \x00";
1207
1208        // Should preserve whitespace in strings
1209        let result = read_string(buffer, 0, None).unwrap();
1210        assert_eq!(result, Value::String("  Spaces  ".to_string()));
1211    }
1212
1213    #[test]
1214    fn test_read_string_newline_characters() {
1215        let buffer = b"Line1\nLine2\r\n\x00";
1216
1217        let result = read_string(buffer, 0, None).unwrap();
1218        assert_eq!(result, Value::String("Line1\nLine2\r\n".to_string()));
1219    }
1220
1221    #[test]
1222    fn test_read_string_consistency_with_typed_value() {
1223        let buffer = b"Test\x00String";
1224
1225        // Test that read_string and read_typed_value produce same results
1226        let direct_result = read_string(buffer, 0, None).unwrap();
1227
1228        let type_kind = TypeKind::String { max_length: None };
1229        let typed_result = read_typed_value(buffer, 0, &type_kind).unwrap();
1230
1231        assert_eq!(direct_result, typed_result);
1232        assert_eq!(typed_result, Value::String("Test".to_string()));
1233    }
1234
1235    #[test]
1236    fn test_read_string_consistency_with_max_length() {
1237        let buffer = b"LongString\x00";
1238
1239        // Test consistency between direct call and typed_value call with max_length
1240        let direct_result = read_string(buffer, 0, Some(4)).unwrap();
1241
1242        let type_kind = TypeKind::String {
1243            max_length: Some(4),
1244        };
1245        let typed_result = read_typed_value(buffer, 0, &type_kind).unwrap();
1246
1247        assert_eq!(direct_result, typed_result);
1248        assert_eq!(typed_result, Value::String("Long".to_string()));
1249    }
1250
1251    #[test]
1252    fn test_read_string_edge_case_combinations() {
1253        // Test various edge case combinations
1254        let test_cases = [
1255            (b"" as &[u8], 0, None, true), // Empty buffer should fail
1256            (b"\x00", 0, None, false),     // Just null terminator
1257            (b"A", 0, Some(0), false),     // Zero max length
1258            (b"AB", 1, Some(1), false),    // Single char at offset
1259        ];
1260
1261        for (buffer, offset, max_length, should_fail) in test_cases {
1262            let result = read_string(buffer, offset, max_length);
1263
1264            if should_fail {
1265                assert!(
1266                    result.is_err(),
1267                    "Expected failure for buffer {buffer:?}, offset {offset}, max_length {max_length:?}"
1268                );
1269            } else {
1270                assert!(
1271                    result.is_ok(),
1272                    "Expected success for buffer {buffer:?}, offset {offset}, max_length {max_length:?}"
1273                );
1274            }
1275        }
1276    }
1277}
1278
1279#[test]
1280fn test_read_typed_value_buffer_overrun() {
1281    let buffer = &[0x12, 0x34];
1282
1283    // Try to read a long (4 bytes) from a 2-byte buffer
1284    let long_type = TypeKind::Long {
1285        endian: Endianness::Little,
1286        signed: false,
1287    };
1288    let result = read_typed_value(buffer, 0, &long_type);
1289    assert!(result.is_err());
1290    assert_eq!(
1291        result.unwrap_err(),
1292        TypeReadError::BufferOverrun {
1293            offset: 0,
1294            buffer_len: 2
1295        }
1296    );
1297
1298    // Try to read a short (2 bytes) at offset 1 from a 2-byte buffer
1299    let short_type = TypeKind::Short {
1300        endian: Endianness::Little,
1301        signed: false,
1302    };
1303    let result = read_typed_value(buffer, 1, &short_type);
1304    assert!(result.is_err());
1305    assert_eq!(
1306        result.unwrap_err(),
1307        TypeReadError::BufferOverrun {
1308            offset: 1,
1309            buffer_len: 2
1310        }
1311    );
1312}
1313
1314#[test]
1315fn test_read_typed_value_all_supported_types() {
1316    let buffer = &[0x7f, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12, 0xbc, 0x9a];
1317
1318    // Test all supported TypeKind variants
1319    let test_cases = vec![
1320        (TypeKind::Byte { signed: false }, 0, Value::Uint(0x7f)),
1321        (
1322            TypeKind::Short {
1323                endian: Endianness::Little,
1324                signed: false,
1325            },
1326            1,
1327            Value::Uint(0x1234), // bytes [0x34, 0x12] -> 0x1234 little-endian
1328        ),
1329        (
1330            TypeKind::Short {
1331                endian: Endianness::Big,
1332                signed: false,
1333            },
1334            1,
1335            Value::Uint(0x3412), // bytes [0x34, 0x12] -> 0x3412 big-endian
1336        ),
1337        (
1338            TypeKind::Long {
1339                endian: Endianness::Little,
1340                signed: false,
1341            },
1342            1,
1343            Value::Uint(0x5678_1234), // bytes [0x34, 0x12, 0x78, 0x56] -> 0x56781234 little-endian
1344        ),
1345        (
1346            TypeKind::Long {
1347                endian: Endianness::Big,
1348                signed: false,
1349            },
1350            1,
1351            Value::Uint(0x3412_7856), // bytes [0x34, 0x12, 0x78, 0x56] -> 0x34127856 big-endian
1352        ),
1353    ];
1354
1355    for (type_kind, offset, expected) in test_cases {
1356        let result = read_typed_value(buffer, offset, &type_kind).unwrap();
1357        assert_eq!(result, expected, "Failed for type: {type_kind:?}");
1358    }
1359}
1360
1361#[test]
1362fn test_read_typed_value_signed_vs_unsigned() {
1363    let buffer = &[0xff, 0xff, 0xff, 0xff, 0xff, 0xff];
1364
1365    // Test signed vs unsigned interpretation for shorts
1366    let unsigned_short = TypeKind::Short {
1367        endian: Endianness::Little,
1368        signed: false,
1369    };
1370    let signed_short = TypeKind::Short {
1371        endian: Endianness::Little,
1372        signed: true,
1373    };
1374
1375    let unsigned_result = read_typed_value(buffer, 0, &unsigned_short).unwrap();
1376    let signed_result = read_typed_value(buffer, 0, &signed_short).unwrap();
1377
1378    assert_eq!(unsigned_result, Value::Uint(65535));
1379    assert_eq!(signed_result, Value::Int(-1));
1380
1381    // Test signed vs unsigned interpretation for longs
1382    let unsigned_long = TypeKind::Long {
1383        endian: Endianness::Little,
1384        signed: false,
1385    };
1386    let signed_long = TypeKind::Long {
1387        endian: Endianness::Little,
1388        signed: true,
1389    };
1390
1391    let unsigned_result = read_typed_value(buffer, 0, &unsigned_long).unwrap();
1392    let signed_result = read_typed_value(buffer, 0, &signed_long).unwrap();
1393
1394    assert_eq!(unsigned_result, Value::Uint(4_294_967_295));
1395    assert_eq!(signed_result, Value::Int(-1));
1396}
1397
1398#[test]
1399fn test_read_typed_value_consistency_with_direct_calls() {
1400    let buffer = &[0x34, 0x12, 0x78, 0x56, 0xbc, 0x9a, 0xde, 0xf0];
1401
1402    // Test that read_typed_value gives same results as direct function calls
1403    let byte_type = TypeKind::Byte { signed: false };
1404    let direct_byte = read_byte(buffer, 0, false).unwrap();
1405    let typed_byte = read_typed_value(buffer, 0, &byte_type).unwrap();
1406    assert_eq!(direct_byte, typed_byte);
1407
1408    let short_type = TypeKind::Short {
1409        endian: Endianness::Little,
1410        signed: false,
1411    };
1412    let direct_short = read_short(buffer, 0, Endianness::Little, false).unwrap();
1413    let typed_short = read_typed_value(buffer, 0, &short_type).unwrap();
1414    assert_eq!(direct_short, typed_short);
1415
1416    let long_type = TypeKind::Long {
1417        endian: Endianness::Big,
1418        signed: true,
1419    };
1420    let direct_long = read_long(buffer, 0, Endianness::Big, true).unwrap();
1421    let typed_long = read_typed_value(buffer, 0, &long_type).unwrap();
1422    assert_eq!(direct_long, typed_long);
1423}
1424
1425#[test]
1426fn test_read_typed_value_empty_buffer() {
1427    let buffer = &[];
1428
1429    // All types should fail on empty buffer
1430    let types = vec![
1431        TypeKind::Byte { signed: false },
1432        TypeKind::Short {
1433            endian: Endianness::Little,
1434            signed: false,
1435        },
1436        TypeKind::Long {
1437            endian: Endianness::Little,
1438            signed: false,
1439        },
1440    ];
1441
1442    for type_kind in types {
1443        let result = read_typed_value(buffer, 0, &type_kind);
1444        assert!(result.is_err());
1445        match result.unwrap_err() {
1446            TypeReadError::BufferOverrun { offset, buffer_len } => {
1447                assert_eq!(offset, 0);
1448                assert_eq!(buffer_len, 0);
1449            }
1450            TypeReadError::UnsupportedType { .. } => panic!("Expected BufferOverrun error"),
1451        }
1452    }
1453}
1454
1455#[test]
1456#[allow(clippy::too_many_lines)]
1457fn test_coerce_value_to_type() {
1458    let cases = [
1459        // Signed byte: values above i8::MAX get coerced
1460        (
1461            Value::Uint(0xff),
1462            TypeKind::Byte { signed: true },
1463            Value::Int(-1),
1464        ),
1465        (
1466            Value::Uint(0x80),
1467            TypeKind::Byte { signed: true },
1468            Value::Int(-128),
1469        ),
1470        (
1471            Value::Uint(0xfe),
1472            TypeKind::Byte { signed: true },
1473            Value::Int(-2),
1474        ),
1475        // Signed byte: values in signed range pass through
1476        (
1477            Value::Uint(0x7f),
1478            TypeKind::Byte { signed: true },
1479            Value::Uint(0x7f),
1480        ),
1481        (
1482            Value::Uint(0),
1483            TypeKind::Byte { signed: true },
1484            Value::Uint(0),
1485        ),
1486        (
1487            Value::Uint(1),
1488            TypeKind::Byte { signed: true },
1489            Value::Uint(1),
1490        ),
1491        // Unsigned byte: all values pass through
1492        (
1493            Value::Uint(0xff),
1494            TypeKind::Byte { signed: false },
1495            Value::Uint(0xff),
1496        ),
1497        (
1498            Value::Uint(0x80),
1499            TypeKind::Byte { signed: false },
1500            Value::Uint(0x80),
1501        ),
1502        // Signed short: values above i16::MAX get coerced
1503        (
1504            Value::Uint(0xffff),
1505            TypeKind::Short {
1506                endian: Endianness::Native,
1507                signed: true,
1508            },
1509            Value::Int(-1),
1510        ),
1511        (
1512            Value::Uint(0x8000),
1513            TypeKind::Short {
1514                endian: Endianness::Native,
1515                signed: true,
1516            },
1517            Value::Int(-32768),
1518        ),
1519        (
1520            Value::Uint(0xffd8),
1521            TypeKind::Short {
1522                endian: Endianness::Big,
1523                signed: true,
1524            },
1525            Value::Int(-40),
1526        ),
1527        // Signed short: values in signed range pass through
1528        (
1529            Value::Uint(0x7fff),
1530            TypeKind::Short {
1531                endian: Endianness::Native,
1532                signed: true,
1533            },
1534            Value::Uint(0x7fff),
1535        ),
1536        // Unsigned short: all values pass through
1537        (
1538            Value::Uint(0xffff),
1539            TypeKind::Short {
1540                endian: Endianness::Native,
1541                signed: false,
1542            },
1543            Value::Uint(0xffff),
1544        ),
1545        // Signed long: values above i32::MAX get coerced
1546        (
1547            Value::Uint(0xffff_ffff),
1548            TypeKind::Long {
1549                endian: Endianness::Native,
1550                signed: true,
1551            },
1552            Value::Int(-1),
1553        ),
1554        (
1555            Value::Uint(0x8000_0000),
1556            TypeKind::Long {
1557                endian: Endianness::Native,
1558                signed: true,
1559            },
1560            Value::Int(-2_147_483_648),
1561        ),
1562        (
1563            Value::Uint(0x8950_4e47),
1564            TypeKind::Long {
1565                endian: Endianness::Big,
1566                signed: true,
1567            },
1568            Value::Int(-1_991_225_785),
1569        ),
1570        // Signed long: values in signed range pass through
1571        (
1572            Value::Uint(0x7fff_ffff),
1573            TypeKind::Long {
1574                endian: Endianness::Native,
1575                signed: true,
1576            },
1577            Value::Uint(0x7fff_ffff),
1578        ),
1579        // Unsigned long: all values pass through
1580        (
1581            Value::Uint(0xffff_ffff),
1582            TypeKind::Long {
1583                endian: Endianness::Native,
1584                signed: false,
1585            },
1586            Value::Uint(0xffff_ffff),
1587        ),
1588        // Non-Uint values pass through unchanged
1589        (
1590            Value::Int(-1),
1591            TypeKind::Byte { signed: true },
1592            Value::Int(-1),
1593        ),
1594        (
1595            Value::Int(42),
1596            TypeKind::Long {
1597                endian: Endianness::Native,
1598                signed: true,
1599            },
1600            Value::Int(42),
1601        ),
1602        // String type: values pass through
1603        (
1604            Value::Uint(0xff),
1605            TypeKind::String { max_length: None },
1606            Value::Uint(0xff),
1607        ),
1608    ];
1609
1610    for (i, (input, type_kind, expected)) in cases.iter().enumerate() {
1611        let result = coerce_value_to_type(input, type_kind);
1612        assert_eq!(
1613            result, *expected,
1614            "Case {i}: coerce({input:?}, {type_kind:?})"
1615        );
1616    }
1617}