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