fst_native/
io.rs

1// Copyright 2023 The Regents of the University of California
2// released under BSD 3-Clause License
3// author: Kevin Laeufer <laeufer@berkeley.edu>
4// Contains basic read and write operations for FST files.
5
6use crate::types::*;
7use crate::FstSignalValue;
8use num_enum::{TryFromPrimitive, TryFromPrimitiveError};
9use std::cmp::Ordering;
10use std::io::{Read, Seek, SeekFrom, Write};
11use thiserror::Error;
12
13#[derive(Debug, Error)]
14pub enum ReaderError {
15    #[error("failed to read a null terminated string because it exceeds the expected size of {0} bytes.\n{1}")]
16    CStringTooLong(usize, String),
17    #[error("failed to parse an enum table string: {0}\n{1}")]
18    EnumTableString(String, String),
19    #[error("failed to read leb128 integer, more than the expected {0} bits")]
20    Leb128(u32),
21    #[error("failed to parse an integer")]
22    ParseInt(#[from] std::num::ParseIntError),
23    #[error("failed to decompress with lz4")]
24    Lz4Decompress(#[from] lz4_flex::block::DecompressError),
25    #[error("failed to decode string")]
26    Utf8(#[from] std::str::Utf8Error),
27    #[error("failed to decode string")]
28    Utf8String(#[from] std::string::FromUtf8Error),
29    #[error("I/O operation failed")]
30    Io(#[from] std::io::Error),
31    #[error("The FST file is still being compressed into its final GZIP wrapper.")]
32    NotFinishedCompressing(),
33    #[error("Unexpected block type")]
34    BlockType(#[from] TryFromPrimitiveError<BlockType>),
35    #[error("Unexpected file type")]
36    FileType(#[from] TryFromPrimitiveError<FileType>),
37    #[error("Unexpected vhdl variable type")]
38    FstVhdlVarType(#[from] TryFromPrimitiveError<FstVhdlVarType>),
39    #[error("Unexpected vhdl data type")]
40    FstVhdlDataType(#[from] TryFromPrimitiveError<FstVhdlDataType>),
41    #[error("Unexpected variable type")]
42    FstVarType(#[from] TryFromPrimitiveError<FstVarType>),
43    #[error("Unexpected scope type")]
44    FstScopeType(#[from] TryFromPrimitiveError<FstScopeType>),
45    #[error("Unexpected variable direction")]
46    FstVarDirection(#[from] TryFromPrimitiveError<FstVarDirection>),
47    #[error("Unexpected attribute type")]
48    AttributeType(#[from] TryFromPrimitiveError<AttributeType>),
49    #[error("Unexpected misc attribute type")]
50    MiscType(#[from] TryFromPrimitiveError<MiscType>),
51}
52
53pub type ReadResult<T> = Result<T, ReaderError>;
54
55pub type WriteResult<T> = Result<T, ReaderError>;
56
57//////////////// Primitives
58
59#[inline]
60pub(crate) fn read_variant_u32(input: &mut impl Read) -> ReadResult<(u32, u32)> {
61    let mut byte = [0u8; 1];
62    let mut res = 0u32;
63    // 32bit / 7bit = ~4.6
64    for ii in 0..5u32 {
65        input.read_exact(&mut byte)?;
66        let value = (byte[0] as u32) & 0x7f;
67        res |= value << (7 * ii);
68        if (byte[0] & 0x80) == 0 {
69            return Ok((res, ii + 1));
70        }
71    }
72    Err(ReaderError::Leb128(32))
73}
74
75#[inline]
76pub(crate) fn read_variant_i64(input: &mut impl Read) -> ReadResult<i64> {
77    let mut byte = [0u8; 1];
78    let mut res = 0i64;
79    // 64bit / 7bit = ~9.1
80    for ii in 0..10 {
81        input.read_exact(&mut byte)?;
82        let value = (byte[0] & 0x7f) as i64;
83        let shift_by = 7 * ii;
84        res |= value << shift_by;
85        if (byte[0] & 0x80) == 0 {
86            // sign extend
87            let sign_bit_set = (byte[0] & 0x40) != 0;
88            if shift_by < (8 * 8) && sign_bit_set {
89                res |= -(1i64 << (shift_by + 7))
90            }
91            return Ok(res);
92        }
93    }
94    Err(ReaderError::Leb128(64))
95}
96
97#[inline]
98pub(crate) fn read_variant_u64(input: &mut impl Read) -> ReadResult<(u64, usize)> {
99    let mut byte = [0u8; 1];
100    let mut res = 0u64;
101    for ii in 0..10 {
102        // 64bit / 7bit = ~9.1
103        input.read_exact(&mut byte)?;
104        let value = (byte[0] as u64) & 0x7f;
105        res |= value << (7 * ii);
106        if (byte[0] & 0x80) == 0 {
107            return Ok((res, ii + 1));
108        }
109    }
110    Err(ReaderError::Leb128(64))
111}
112
113#[inline]
114pub(crate) fn write_variant_u64(output: &mut impl Write, mut value: u64) -> WriteResult<usize> {
115    // often, the value is small
116    if value <= 0x7f {
117        let byte = [value as u8; 1];
118        output.write_all(&byte)?;
119        return Ok(1);
120    }
121
122    let mut bytes = Vec::with_capacity(10);
123    while value != 0 {
124        let next_value = value >> 7;
125        let mask: u8 = if next_value == 0 { 0 } else { 0x80 };
126        bytes.push((value & 0x7f) as u8 | mask);
127        value = next_value;
128    }
129    assert!(bytes.len() <= 10);
130    output.write_all(&bytes)?;
131    Ok(bytes.len())
132}
133
134#[inline]
135pub(crate) fn write_variant_u32(output: &mut impl Write, value: u32) -> WriteResult<usize> {
136    write_variant_u64(output, value as u64)
137}
138
139#[inline]
140pub(crate) fn read_u64(input: &mut impl Read) -> ReadResult<u64> {
141    let mut buf = [0u8; 8];
142    input.read_exact(&mut buf)?;
143    Ok(u64::from_be_bytes(buf))
144}
145
146#[inline]
147pub(crate) fn write_u64(output: &mut impl Write, value: u64) -> WriteResult<()> {
148    let buf = value.to_be_bytes();
149    output.write_all(&buf)?;
150    Ok(())
151}
152
153#[inline]
154pub(crate) fn read_u8(input: &mut impl Read) -> ReadResult<u8> {
155    let mut buf = [0u8; 1];
156    input.read_exact(&mut buf)?;
157    Ok(buf[0])
158}
159
160fn write_u8(output: &mut impl Write, value: u8) -> WriteResult<()> {
161    let buf = value.to_be_bytes();
162    output.write_all(&buf)?;
163    Ok(())
164}
165
166#[inline]
167pub(crate) fn read_i8(input: &mut impl Read) -> ReadResult<i8> {
168    let mut buf = [0u8; 1];
169    input.read_exact(&mut buf)?;
170    Ok(i8::from_be_bytes(buf))
171}
172
173#[inline]
174fn write_i8(output: &mut impl Write, value: i8) -> WriteResult<()> {
175    let buf = value.to_be_bytes();
176    output.write_all(&buf)?;
177    Ok(())
178}
179
180pub(crate) fn read_c_str(input: &mut impl Read, max_len: usize) -> ReadResult<String> {
181    let mut bytes: Vec<u8> = Vec::with_capacity(32);
182    for _ in 0..max_len {
183        let byte = read_u8(input)?;
184        if byte == 0 {
185            return Ok(String::from_utf8(bytes)?);
186        } else {
187            bytes.push(byte);
188        }
189    }
190    Err(ReaderError::CStringTooLong(
191        max_len,
192        String::from_utf8_lossy(&bytes).to_string(),
193    ))
194}
195
196fn write_c_str(output: &mut impl Write, value: &str) -> WriteResult<()> {
197    let bytes = value.as_bytes();
198    output.write_all(bytes)?;
199    write_u8(output, 0)?;
200    Ok(())
201}
202
203#[inline] // inline to specialize on length
204pub(crate) fn read_c_str_fixed_length(input: &mut impl Read, len: usize) -> ReadResult<String> {
205    let mut bytes = read_bytes(input, len)?;
206    let zero_index = bytes.iter().position(|b| *b == 0u8).unwrap_or(len - 1);
207    let str_len = zero_index;
208    bytes.truncate(str_len);
209    Ok(String::from_utf8(bytes)?)
210}
211
212#[inline]
213fn write_c_str_fixed_length(
214    output: &mut impl Write,
215    value: &str,
216    max_len: usize,
217) -> WriteResult<()> {
218    let bytes = value.as_bytes();
219    if bytes.len() >= max_len {
220        todo!("Return error.")
221    }
222    output.write_all(bytes)?;
223    let zeros = vec![0u8; max_len - bytes.len()];
224    output.write_all(&zeros)?;
225    Ok(())
226}
227
228const RCV_STR: [u8; 8] = [b'x', b'z', b'h', b'u', b'w', b'l', b'-', b'?'];
229#[inline]
230pub(crate) fn one_bit_signal_value_to_char(vli: u32) -> u8 {
231    if (vli & 1) == 0 {
232        (((vli >> 1) & 1) as u8) | b'0'
233    } else {
234        RCV_STR[((vli >> 1) & 7) as usize]
235    }
236}
237
238/// Decodes a digital (1/0) signal. This is indicated by bit0 in vli being cleared.
239#[inline]
240pub(crate) fn multi_bit_digital_signal_to_chars(bytes: &[u8], len: usize) -> Vec<u8> {
241    let mut chars = Vec::with_capacity(len);
242    for ii in 0..len {
243        let byte_id = ii / 8;
244        let bit_id = 7 - (ii & 7);
245        let bit = (bytes[byte_id] >> bit_id) & 1;
246        chars.push(bit | b'0');
247    }
248    chars
249}
250
251pub(crate) fn read_one_bit_signal_time_delta(bytes: &[u8], offset: u32) -> ReadResult<usize> {
252    let mut slice = &bytes[(offset as usize)..];
253    let (vli, _) = read_variant_u32(&mut slice)?;
254    let shift_count = 2u32 << (vli & 1);
255    Ok((vli >> shift_count) as usize)
256}
257
258pub(crate) fn read_multi_bit_signal_time_delta(bytes: &[u8], offset: u32) -> ReadResult<usize> {
259    let mut slice = &bytes[(offset as usize)..];
260    let (vli, _) = read_variant_u32(&mut slice)?;
261    Ok((vli >> 1) as usize)
262}
263
264/// Reads ZLib compressed bytes.
265pub(crate) fn read_zlib_compressed_bytes(
266    input: &mut (impl Read + Seek),
267    uncompressed_length: u64,
268    compressed_length: u64,
269    allow_uncompressed: bool,
270) -> ReadResult<Vec<u8>> {
271    let bytes = if uncompressed_length == compressed_length && allow_uncompressed {
272        read_bytes(input, compressed_length as usize)?
273    } else {
274        let start = input.stream_position().unwrap();
275        let mut d = flate2::read::ZlibDecoder::new(input);
276        let mut uncompressed: Vec<u8> = Vec::with_capacity(uncompressed_length as usize);
277        d.read_to_end(&mut uncompressed)?;
278        // sanity checks
279        assert_eq!(d.total_out(), uncompressed_length);
280        // the decoder often reads more bytes than it should, we fix this here
281        d.into_inner()
282            .seek(SeekFrom::Start(start + compressed_length))?;
283        uncompressed
284    };
285    assert_eq!(bytes.len(), uncompressed_length as usize);
286    Ok(bytes)
287}
288
289/// ZLib compresses bytes. If allow_uncompressed is true, we overwrite the compressed with the
290/// uncompressed bytes if it turns out that the compressed bytes are longer.
291pub(crate) fn write_compressed_bytes(
292    output: &mut (impl Write + Seek),
293    bytes: &[u8],
294    compression_level: flate2::Compression,
295    allow_uncompressed: bool,
296) -> WriteResult<usize> {
297    let start = output.stream_position()?;
298    let mut d = flate2::write::ZlibEncoder::new(output, compression_level);
299    d.write_all(bytes)?;
300    d.flush()?;
301    assert_eq!(d.total_in() as usize, bytes.len());
302    let compressed_written = d.total_out() as usize;
303    let output2 = d.finish()?;
304    if !allow_uncompressed || compressed_written < bytes.len() {
305        Ok(compressed_written)
306    } else {
307        // it turns out that the compression was futile!
308        output2.seek(SeekFrom::Start(start))?;
309        output2.write_all(bytes)?;
310        Ok(bytes.len())
311    }
312}
313
314#[inline]
315pub(crate) fn read_bytes(input: &mut impl Read, len: usize) -> ReadResult<Vec<u8>> {
316    let mut buf: Vec<u8> = Vec::with_capacity(len);
317    input.take(len as u64).read_to_end(&mut buf)?;
318    Ok(buf)
319}
320
321pub(crate) fn read_block_tpe(input: &mut impl Read) -> ReadResult<BlockType> {
322    Ok(BlockType::try_from(read_u8(input)?)?)
323}
324
325pub(crate) fn determine_f64_endian(
326    input: &mut impl Read,
327    needle: f64,
328) -> ReadResult<FloatingPointEndian> {
329    let bytes = read_bytes(input, 8)?;
330    let mut byte_reader: &[u8] = &bytes;
331    let le = read_f64(&mut byte_reader, FloatingPointEndian::Little)?;
332    if le == needle {
333        return Ok(FloatingPointEndian::Little);
334    }
335    byte_reader = &bytes;
336    let be = read_f64(&mut byte_reader, FloatingPointEndian::Big)?;
337    if be == needle {
338        Ok(FloatingPointEndian::Big)
339    } else {
340        todo!("should not get here")
341    }
342}
343
344#[inline]
345pub(crate) fn read_f64(input: &mut impl Read, endian: FloatingPointEndian) -> ReadResult<f64> {
346    let mut buf = [0u8; 8];
347    input.read_exact(&mut buf)?;
348    match endian {
349        FloatingPointEndian::Little => Ok(f64::from_le_bytes(buf)),
350        FloatingPointEndian::Big => Ok(f64::from_be_bytes(buf)),
351    }
352}
353
354#[inline]
355fn write_f64(output: &mut impl Write, value: f64) -> WriteResult<()> {
356    // for f64, we have the option to use either LE or BE, we just need to be consistent
357    let buf = value.to_le_bytes();
358    output.write_all(&buf)?;
359    Ok(())
360}
361
362fn read_lz4_compressed_bytes(
363    input: &mut impl Read,
364    uncompressed_length: usize,
365    compressed_length: usize,
366) -> ReadResult<Vec<u8>> {
367    let compressed = read_bytes(input, compressed_length)?;
368    let bytes = lz4_flex::decompress(&compressed, uncompressed_length)?;
369    Ok(bytes)
370}
371
372//////////////// Header
373
374const HEADER_LENGTH: u64 = 329;
375const HEADER_VERSION_MAX_LEN: usize = 128;
376const HEADER_DATE_MAX_LEN: usize = 119;
377pub(crate) fn read_header(input: &mut impl Read) -> ReadResult<(Header, FloatingPointEndian)> {
378    let section_length = read_u64(input)?;
379    assert_eq!(section_length, HEADER_LENGTH);
380    let start_time = read_u64(input)?;
381    let end_time = read_u64(input)?;
382    let float_endian = determine_f64_endian(input, DOUBLE_ENDIAN_TEST)?;
383    let memory_used_by_writer = read_u64(input)?;
384    let scope_count = read_u64(input)?;
385    let var_count = read_u64(input)?;
386    let max_var_id_code = read_u64(input)?;
387    let vc_section_count = read_u64(input)?;
388    let timescale_exponent = read_i8(input)?;
389    let version = read_c_str_fixed_length(input, HEADER_VERSION_MAX_LEN)?;
390    // this size was reduced compared to what is documented in block_format.txt
391    let date = read_c_str_fixed_length(input, HEADER_DATE_MAX_LEN)?;
392    let file_type = FileType::try_from(read_u8(input)?)?;
393    let time_zero = read_u64(input)?;
394
395    let header = Header {
396        start_time,
397        end_time,
398        memory_used_by_writer,
399        scope_count,
400        var_count,
401        max_var_id_code,
402        vc_section_count,
403        timescale_exponent,
404        version,
405        date,
406        file_type,
407        time_zero,
408    };
409    Ok((header, float_endian))
410}
411
412#[allow(dead_code)]
413pub(crate) fn write_header(output: &mut impl Write, header: &Header) -> WriteResult<()> {
414    write_u64(output, HEADER_LENGTH)?;
415    write_u64(output, header.start_time)?;
416    write_u64(output, header.end_time)?;
417    write_f64(output, DOUBLE_ENDIAN_TEST)?;
418    write_u64(output, header.memory_used_by_writer)?;
419    write_u64(output, header.scope_count)?;
420    write_u64(output, header.var_count)?;
421    write_u64(output, header.max_var_id_code)?;
422    write_u64(output, header.vc_section_count)?;
423    write_i8(output, header.timescale_exponent)?;
424    write_c_str_fixed_length(output, &header.version, HEADER_VERSION_MAX_LEN)?;
425    write_c_str_fixed_length(output, &header.date, HEADER_DATE_MAX_LEN)?;
426    write_u8(output, header.file_type as u8)?;
427    write_u64(output, header.time_zero)?;
428    Ok(())
429}
430
431//////////////// Geometry
432
433pub(crate) fn read_geometry(input: &mut (impl Read + Seek)) -> ReadResult<Vec<SignalInfo>> {
434    let section_length = read_u64(input)?;
435    let uncompressed_length = read_u64(input)?;
436    let max_handle = read_u64(input)?;
437    let compressed_length = section_length - 3 * 8;
438
439    let bytes = read_zlib_compressed_bytes(input, uncompressed_length, compressed_length, true)?;
440
441    let mut signals: Vec<SignalInfo> = Vec::with_capacity(max_handle as usize);
442    let mut byte_reader: &[u8] = &bytes;
443
444    for _ii in 0..max_handle {
445        let (value, _) = read_variant_u32(&mut byte_reader)?;
446        signals.push(SignalInfo::from_file_format(value));
447    }
448    Ok(signals)
449}
450
451#[allow(dead_code)]
452pub(crate) fn write_geometry(
453    output: &mut (impl Write + Seek),
454    signals: &Vec<SignalInfo>,
455    compression: flate2::Compression,
456) -> WriteResult<()> {
457    // remember start to fix the section length afterwards
458    let start = output.stream_position()?;
459    write_u64(output, 0)?; // dummy section length
460
461    // write uncompressed signal info
462    let mut bytes: Vec<u8> = Vec::with_capacity(signals.len() * 2);
463    for signal in signals {
464        write_variant_u64(&mut bytes, signal.to_file_format() as u64)?;
465    }
466    let uncompressed_length = bytes.len() as u64;
467    write_u64(output, uncompressed_length)?;
468    let max_handle = signals.len() as u64;
469    write_u64(output, max_handle)?;
470
471    // compress signals
472    let compressed_len = write_compressed_bytes(output, &bytes, compression, true)? as u64;
473
474    // fix section length
475    let section_length = compressed_len + 3 * 8;
476    let end = output.stream_position()?;
477    output.seek(SeekFrom::Start(start))?;
478    write_u64(output, section_length)?;
479    output.seek(SeekFrom::Start(end))?;
480
481    Ok(())
482}
483
484//////////////// Blackout
485
486pub(crate) fn read_blackout(input: &mut (impl Read + Seek)) -> ReadResult<Vec<BlackoutData>> {
487    // remember start for later sanity check
488    let start = input.stream_position()?;
489    let section_length = read_u64(input)?;
490    let (num_blackouts, _) = read_variant_u32(input)?;
491    let mut blackouts = Vec::with_capacity(num_blackouts as usize);
492    let mut current_blackout = 0u64;
493    for _ in 0..num_blackouts {
494        let activity = read_u8(input)? != 0;
495        let (delta, _) = read_variant_u64(input)?;
496        current_blackout += delta;
497        let bo = BlackoutData {
498            time: current_blackout,
499            contains_activity: activity,
500        };
501        blackouts.push(bo);
502    }
503    let end = input.stream_position()?;
504    assert_eq!(start + section_length, end);
505    Ok(blackouts)
506}
507
508#[allow(dead_code)]
509pub(crate) fn write_blackout(
510    output: &mut (impl Write + Seek),
511    blackouts: &[BlackoutData],
512) -> WriteResult<()> {
513    // remember start to fix the section length afterwards
514    let start = output.stream_position()?;
515    write_u64(output, 0)?; // dummy section length
516
517    let num_blackouts = blackouts.len() as u32;
518    write_variant_u32(output, num_blackouts)?;
519
520    let mut last_blackout = 0u64;
521    for blackout in blackouts {
522        let activity_byte = if blackout.contains_activity { 1 } else { 0 };
523        write_u8(output, activity_byte)?;
524        let delta = blackout.time - last_blackout;
525        last_blackout = blackout.time;
526        write_variant_u64(output, delta)?;
527    }
528
529    // fix section length
530    let end = output.stream_position()?;
531    output.seek(SeekFrom::Start(start))?;
532    write_u64(output, end - start)?;
533    output.seek(SeekFrom::Start(end))?;
534
535    Ok(())
536}
537
538//////////////// Hierarchy
539
540const HIERARCHY_GZIP_COMPRESSION_LEVEL: flate2::Compression = flate2::Compression::best();
541
542pub(crate) fn read_hierarchy_bytes(
543    input: &mut (impl Read + Seek),
544    compression: HierarchyCompression,
545) -> ReadResult<Vec<u8>> {
546    let section_length = read_u64(input)? as usize;
547    let uncompressed_length = read_u64(input)? as usize;
548    let compressed_length = section_length - 2 * 8;
549
550    let bytes = match compression {
551        HierarchyCompression::ZLib => {
552            let start = input.stream_position().unwrap();
553            let mut d = flate2::read::GzDecoder::new(input);
554            let mut uncompressed: Vec<u8> = Vec::with_capacity(uncompressed_length);
555            d.read_to_end(&mut uncompressed)?;
556            // the decoder often reads more bytes than it should, we fix this here
557            d.into_inner()
558                .seek(SeekFrom::Start(start + compressed_length as u64))?;
559            uncompressed
560        }
561        HierarchyCompression::Lz4 => {
562            read_lz4_compressed_bytes(input, uncompressed_length, compressed_length)?
563        }
564        HierarchyCompression::Lz4Duo => {
565            // the length after the _first_ decompression
566            let (len, skiplen) = read_variant_u64(input)?;
567            let lvl1_len = len as usize;
568            let lvl1 = read_lz4_compressed_bytes(input, lvl1_len, compressed_length - skiplen)?;
569            let mut lvl1_reader = lvl1.as_slice();
570            read_lz4_compressed_bytes(&mut lvl1_reader, uncompressed_length, lvl1_len)?
571        }
572    };
573    assert_eq!(bytes.len(), uncompressed_length);
574    Ok(bytes)
575}
576
577#[allow(dead_code)]
578pub(crate) fn write_hierarchy_bytes(
579    output: &mut (impl Write + Seek),
580    compression: HierarchyCompression,
581    bytes: &[u8],
582) -> WriteResult<()> {
583    // remember start to fix the section length afterwards
584    let start = output.stream_position()?;
585    write_u64(output, 0)?; // dummy section length
586    let uncompressed_length = bytes.len() as u64;
587    write_u64(output, uncompressed_length)?;
588
589    let out2 = match compression {
590        HierarchyCompression::ZLib => {
591            let mut e = flate2::write::GzEncoder::new(output, HIERARCHY_GZIP_COMPRESSION_LEVEL);
592            e.write_all(bytes)?;
593            e.finish()?
594        }
595        HierarchyCompression::Lz4 => {
596            let compressed = lz4_flex::compress(bytes);
597            output.write_all(&compressed)?;
598            output
599        }
600        HierarchyCompression::Lz4Duo => {
601            let compressed_lvl1 = lz4_flex::compress(bytes);
602            let lvl1_len = compressed_lvl1.len() as u64;
603            write_variant_u64(output, lvl1_len)?;
604            let compressed_lvl2 = lz4_flex::compress(&compressed_lvl1);
605            output.write_all(&compressed_lvl2)?;
606            output
607        }
608    };
609
610    // fix section length
611    let end = out2.stream_position()?;
612    out2.seek(SeekFrom::Start(start))?;
613    write_u64(out2, end - start)?;
614    out2.seek(SeekFrom::Start(end))?;
615    Ok(())
616}
617
618fn enum_table_from_string(value: String, handle: u64) -> ReadResult<FstHierarchyEntry> {
619    let parts: Vec<&str> = value.split(' ').collect();
620    if parts.len() < 2 {
621        return Err(ReaderError::EnumTableString(
622            "not enough spaces".to_string(),
623            value,
624        ));
625    }
626    let name = parts[0].to_string();
627    let element_count = parts[1].parse::<usize>()?;
628    let expected_part_len = element_count * 2;
629    if parts.len() - 2 != expected_part_len {
630        return Err(ReaderError::EnumTableString(
631            format!(
632                "expected {} parts got {}",
633                expected_part_len,
634                parts.len() - 2
635            ),
636            value,
637        ));
638    }
639    let mut mapping = Vec::with_capacity(element_count);
640    for ii in 0..element_count {
641        let name = parts[2 + ii].to_string();
642        let value = parts[2 + element_count + ii].to_string();
643        mapping.push((value, name));
644    }
645    // TODO: deal with correct de-escaping
646    Ok(FstHierarchyEntry::EnumTable {
647        name,
648        handle,
649        mapping,
650    })
651}
652
653fn enum_table_to_string(name: &str, mapping: &[(String, String)]) -> String {
654    let mut out = String::with_capacity(name.len() + mapping.len() * 32 + 32);
655    out.push_str(name);
656    out.push(' ');
657    out.push_str(&format!("{}", mapping.len()));
658    for (_value, name) in mapping {
659        out.push(' ');
660        out.push_str(name);
661    }
662    for (value, _name) in mapping {
663        out.push(' ');
664        out.push_str(value);
665    }
666    out
667}
668
669const FST_SUP_VAR_DATA_TYPE_BITS: u32 = 10;
670const FST_SUP_VAR_DATA_TYPE_MASK: u64 = (1 << FST_SUP_VAR_DATA_TYPE_BITS) - 1;
671
672fn parse_misc_attribute(
673    name: String,
674    tpe: MiscType,
675    arg: u64,
676    arg2: Option<u64>,
677) -> ReadResult<FstHierarchyEntry> {
678    let res = match tpe {
679        MiscType::Comment => FstHierarchyEntry::Comment { string: name },
680        MiscType::EnvVar => todo!("EnvVar Attribute"), // fstWriterSetEnvVar()
681        MiscType::SupVar => {
682            // This attribute supplies VHDL specific information and is used by GHDL
683            let var_type = (arg >> FST_SUP_VAR_DATA_TYPE_BITS) as u8;
684            let data_type = (arg & FST_SUP_VAR_DATA_TYPE_MASK) as u8;
685            FstHierarchyEntry::VhdlVarInfo {
686                type_name: name,
687                var_type: FstVhdlVarType::try_from_primitive(var_type)?,
688                data_type: FstVhdlDataType::try_from_primitive(data_type)?,
689            }
690        }
691        MiscType::PathName => FstHierarchyEntry::PathName { name, id: arg },
692        MiscType::SourceStem => FstHierarchyEntry::SourceStem {
693            is_instantiation: false,
694            path_id: arg2.unwrap(),
695            line: arg,
696        },
697        MiscType::SourceInstantiationStem => FstHierarchyEntry::SourceStem {
698            is_instantiation: true,
699            path_id: arg2.unwrap(),
700            line: arg,
701        },
702        MiscType::ValueList => todo!("ValueList Attribute"), // fstWriterSetValueList()
703        MiscType::EnumTable => {
704            if name.is_empty() {
705                FstHierarchyEntry::EnumTableRef { handle: arg }
706            } else {
707                enum_table_from_string(name, arg)?
708            }
709        }
710        MiscType::Unknown => todo!("unknown Attribute"),
711    };
712    Ok(res)
713}
714
715fn read_hierarchy_attribute_arg2_encoded_as_name(input: &mut impl Read) -> ReadResult<u64> {
716    let (value, _) = read_variant_u64(input)?;
717    let end_byte = read_u8(input)?;
718    assert_eq!(end_byte, 0, "expected to be zero terminated!");
719    Ok(value)
720}
721
722const HIERARCHY_TPE_VCD_SCOPE: u8 = 254;
723const HIERARCHY_TPE_VCD_UP_SCOPE: u8 = 255;
724const HIERARCHY_TPE_VCD_ATTRIBUTE_BEGIN: u8 = 252;
725const HIERARCHY_TPE_VCD_ATTRIBUTE_END: u8 = 253;
726
727pub(crate) fn read_hierarchy_entry(
728    input: &mut impl Read,
729    handle_count: &mut u32,
730) -> ReadResult<Option<FstHierarchyEntry>> {
731    let entry_tpe = match read_u8(input) {
732        Ok(tpe) => tpe,
733        Err(_) => return Ok(None),
734    };
735    let entry = match entry_tpe {
736        HIERARCHY_TPE_VCD_SCOPE => {
737            // VcdScope (ScopeType)
738            let tpe = FstScopeType::try_from_primitive(read_u8(input)?)?;
739            let name = read_c_str(input, HIERARCHY_NAME_MAX_SIZE)?;
740            let component = read_c_str(input, HIERARCHY_NAME_MAX_SIZE)?;
741            FstHierarchyEntry::Scope {
742                tpe,
743                name,
744                component,
745            }
746        }
747        0..=29 => {
748            // VcdEvent ... SvShortReal (VariableType)
749            let tpe = FstVarType::try_from_primitive(entry_tpe)?;
750            let direction = FstVarDirection::try_from_primitive(read_u8(input)?)?;
751            let name = read_c_str(input, HIERARCHY_NAME_MAX_SIZE)?;
752            let (raw_length, _) = read_variant_u32(input)?;
753            let length = if tpe == FstVarType::Port {
754                // remove delimiting spaces and adjust signal size
755                (raw_length - 2) / 3
756            } else {
757                raw_length
758            };
759            let (alias, _) = read_variant_u32(input)?;
760            let (is_alias, handle) = if alias == 0 {
761                *handle_count += 1;
762                (false, FstSignalHandle::new(*handle_count))
763            } else {
764                (true, FstSignalHandle::new(alias))
765            };
766            FstHierarchyEntry::Var {
767                tpe,
768                direction,
769                name,
770                length,
771                handle,
772                is_alias,
773            }
774        }
775        HIERARCHY_TPE_VCD_UP_SCOPE => {
776            // VcdUpScope (ScopeType)
777            FstHierarchyEntry::UpScope
778        }
779        HIERARCHY_TPE_VCD_ATTRIBUTE_BEGIN => {
780            let tpe = AttributeType::try_from_primitive(read_u8(input)?)?;
781            let subtype = MiscType::try_from_primitive(read_u8(input)?)?;
782            match tpe {
783                AttributeType::Misc => {
784                    let (name, arg2) = match subtype {
785                        MiscType::SourceStem | MiscType::SourceInstantiationStem => {
786                            let arg2 = read_hierarchy_attribute_arg2_encoded_as_name(input)?;
787                            ("".to_string(), Some(arg2))
788                        }
789                        _ => {
790                            let name = read_c_str(input, HIERARCHY_ATTRIBUTE_MAX_SIZE)?;
791                            (name, None)
792                        }
793                    };
794                    let (arg, _) = read_variant_u64(input)?;
795                    parse_misc_attribute(name, subtype, arg, arg2)?
796                }
797                AttributeType::Array => todo!("ARRAY attributes"),
798                AttributeType::Enum => todo!("ENUM attributes"),
799                AttributeType::Pack => todo!("PACK attributes"),
800            }
801        }
802        HIERARCHY_TPE_VCD_ATTRIBUTE_END => {
803            // GenAttributeEnd (ScopeType)
804            FstHierarchyEntry::AttributeEnd
805        }
806
807        other => todo!("Deal with hierarchy entry of type: {other}"),
808    };
809
810    Ok(Some(entry))
811}
812
813fn write_hierarchy_attribute(
814    output: &mut impl Write,
815    tpe: AttributeType,
816    subtype: MiscType,
817    name: &str,
818    arg: u64,
819    arg2: Option<u64>,
820) -> WriteResult<()> {
821    write_u8(output, HIERARCHY_TPE_VCD_ATTRIBUTE_BEGIN)?;
822    write_u8(output, tpe as u8)?;
823    write_u8(output, subtype as u8)?;
824    let raw_name_bytes = match arg2 {
825        None => {
826            assert!(name.len() <= HIERARCHY_ATTRIBUTE_MAX_SIZE);
827            name.to_string().into_bytes()
828        }
829        Some(value) => {
830            assert!(name.is_empty(), "cannot have a name + an arg2!");
831            let mut buf = vec![0u8; 10];
832            let mut buf_writer: &mut [u8] = buf.as_mut();
833            let len = write_variant_u64(&mut buf_writer, value)?;
834            buf.truncate(len);
835            buf
836        }
837    };
838    output.write_all(&raw_name_bytes)?;
839    write_u8(output, 0)?; // zero terminate string/variant
840    write_variant_u64(output, arg)?;
841    Ok(())
842}
843
844#[allow(dead_code)]
845pub(crate) fn write_hierarchy_entry(
846    output: &mut impl Write,
847    handle_count: &mut u32,
848    entry: &FstHierarchyEntry,
849) -> WriteResult<()> {
850    match entry {
851        FstHierarchyEntry::Scope {
852            tpe,
853            name,
854            component,
855        } => {
856            write_u8(output, HIERARCHY_TPE_VCD_SCOPE)?;
857            write_u8(output, *tpe as u8)?;
858            assert!(name.len() <= HIERARCHY_NAME_MAX_SIZE);
859            write_c_str(output, name)?;
860            assert!(component.len() <= HIERARCHY_NAME_MAX_SIZE);
861            write_c_str(output, component)?;
862        }
863        FstHierarchyEntry::UpScope => {
864            write_u8(output, HIERARCHY_TPE_VCD_UP_SCOPE)?;
865        }
866        FstHierarchyEntry::Var {
867            tpe,
868            direction,
869            name,
870            length,
871            handle,
872            is_alias,
873        } => {
874            write_u8(output, *tpe as u8)?;
875            write_u8(output, *direction as u8)?;
876            assert!(name.len() <= HIERARCHY_NAME_MAX_SIZE);
877            write_c_str(output, name)?;
878            let raw_length = if *tpe == FstVarType::Port {
879                3 * (*length) + 2
880            } else {
881                *length
882            };
883            write_variant_u32(output, raw_length)?;
884            if *is_alias {
885                write_variant_u32(output, handle.get_raw())?;
886            } else {
887                // sanity check handle
888                assert_eq!(handle.get_index(), *handle_count as usize);
889                *handle_count += 1;
890                // write no-alias
891                write_variant_u32(output, 0)?;
892            }
893        }
894        FstHierarchyEntry::PathName { name, id } => write_hierarchy_attribute(
895            output,
896            AttributeType::Misc,
897            MiscType::PathName,
898            name,
899            *id,
900            None,
901        )?,
902        FstHierarchyEntry::SourceStem {
903            is_instantiation,
904            path_id,
905            line,
906        } => {
907            let subtpe = if *is_instantiation {
908                MiscType::SourceInstantiationStem
909            } else {
910                MiscType::SourceStem
911            };
912            write_hierarchy_attribute(
913                output,
914                AttributeType::Misc,
915                subtpe,
916                "",
917                *line,
918                Some(*path_id),
919            )?
920        }
921        FstHierarchyEntry::Comment { string } => write_hierarchy_attribute(
922            output,
923            AttributeType::Misc,
924            MiscType::Comment,
925            string,
926            0,
927            None,
928        )?,
929        FstHierarchyEntry::EnumTable {
930            name,
931            handle,
932            mapping,
933        } => {
934            let table_str = enum_table_to_string(name, mapping);
935            write_hierarchy_attribute(
936                output,
937                AttributeType::Misc,
938                MiscType::EnumTable,
939                &table_str,
940                *handle,
941                None,
942            )?
943        }
944        FstHierarchyEntry::EnumTableRef { handle } => write_hierarchy_attribute(
945            output,
946            AttributeType::Misc,
947            MiscType::EnumTable,
948            "",
949            *handle,
950            None,
951        )?,
952        FstHierarchyEntry::VhdlVarInfo {
953            type_name,
954            var_type,
955            data_type,
956        } => {
957            let arg = ((*var_type as u64) << FST_SUP_VAR_DATA_TYPE_BITS) | (*data_type as u64);
958            write_hierarchy_attribute(
959                output,
960                AttributeType::Misc,
961                MiscType::SupVar,
962                type_name,
963                arg,
964                None,
965            )?;
966        }
967        FstHierarchyEntry::AttributeEnd => {
968            write_u8(output, HIERARCHY_TPE_VCD_ATTRIBUTE_END)?;
969        }
970    }
971
972    Ok(())
973}
974
975//////////////// Vale Change Data
976
977pub(crate) fn read_packed_signal_value_bytes(
978    input: &mut (impl Read + Seek),
979    len: u32,
980    tpe: ValueChangePackType,
981) -> ReadResult<Vec<u8>> {
982    let (value, skiplen) = read_variant_u32(input)?;
983    if value != 0 {
984        let uncompressed_length = value as u64;
985        let uncompressed: Vec<u8> = match tpe {
986            ValueChangePackType::Lz4 => {
987                let compressed_length = (len - skiplen) as u64;
988                read_lz4_compressed_bytes(
989                    input,
990                    uncompressed_length as usize,
991                    compressed_length as usize,
992                )?
993            }
994            ValueChangePackType::FastLz => {
995                let compressed_length = (len - skiplen) as u64;
996                crate::fastlz::decompress(
997                    input,
998                    compressed_length as usize,
999                    uncompressed_length as usize,
1000                )?
1001            }
1002            ValueChangePackType::Zlib => {
1003                let compressed_length = len as u64;
1004                // Important: for signals, we do not skip decompression,
1005                // even if the compressed and uncompressed length are the same
1006                read_zlib_compressed_bytes(input, uncompressed_length, compressed_length, false)?
1007            }
1008        };
1009        Ok(uncompressed)
1010    } else {
1011        let dest_length = len - skiplen;
1012        let bytes = read_bytes(input, dest_length as usize)?;
1013        Ok(bytes)
1014    }
1015}
1016
1017pub(crate) fn read_time_chain(
1018    input: &mut (impl Read + Seek),
1019    section_start: u64,
1020    section_length: u64,
1021) -> ReadResult<(u64, Vec<u64>)> {
1022    // the time block meta data is in the last 24 bytes at the end of the section
1023    input.seek(SeekFrom::Start(section_start + section_length - 3 * 8))?;
1024    let uncompressed_length = read_u64(input)?;
1025    let compressed_length = read_u64(input)?;
1026    let number_of_items = read_u64(input)?;
1027    assert!(compressed_length <= section_length);
1028
1029    // now that we know how long the block actually is, we can go back to it
1030    input.seek(SeekFrom::Current(-(3 * 8) - (compressed_length as i64)))?;
1031    let bytes = read_zlib_compressed_bytes(input, uncompressed_length, compressed_length, true)?;
1032    let mut byte_reader: &[u8] = &bytes;
1033    let mut time_table: Vec<u64> = Vec::with_capacity(number_of_items as usize);
1034    let mut time_val: u64 = 0; // running time counter
1035
1036    for _ in 0..number_of_items {
1037        let (value, _) = read_variant_u64(&mut byte_reader)?;
1038        time_val += value;
1039        time_table.push(time_val);
1040    }
1041
1042    let time_section_length = compressed_length + 3 * 8;
1043    Ok((time_section_length, time_table))
1044}
1045
1046#[allow(dead_code)]
1047pub(crate) fn write_time_chain(
1048    output: &mut (impl Write + Seek),
1049    compression: Option<flate2::Compression>,
1050    table: &[u64],
1051) -> WriteResult<()> {
1052    // write data
1053    let (uncompressed_len, compressed_len, out2) = match compression {
1054        Some(comp) => {
1055            let start = output.stream_position()?;
1056            let mut e = flate2::write::ZlibEncoder::new(output, comp);
1057            let uncompressed_len = write_time_chain_data(&mut e, table)? as u64;
1058            let out2 = e.finish()?;
1059            let end = out2.stream_position()?;
1060            let compressed_len = end - start;
1061
1062            // important: if the compression does not work, we need to save the data uncompressed
1063            if compressed_len >= uncompressed_len {
1064                out2.seek(SeekFrom::Start(start))?; // go back to start
1065                let len = write_time_chain_data(out2, table)? as u64;
1066                (len, len, out2)
1067            } else {
1068                (uncompressed_len, compressed_len, out2)
1069            }
1070        }
1071        None => {
1072            let len = write_time_chain_data(output, table)? as u64;
1073            (len, len, output)
1074        }
1075    };
1076    write_u64(out2, uncompressed_len)?;
1077    write_u64(out2, compressed_len)?;
1078    write_u64(out2, table.len() as u64)?;
1079
1080    Ok(())
1081}
1082
1083#[inline]
1084fn write_time_chain_data(output: &mut impl Write, table: &[u64]) -> WriteResult<usize> {
1085    let mut total_len = 0;
1086    let mut prev_time = 0u64;
1087    for time in table {
1088        let delta = *time - prev_time;
1089        prev_time = *time;
1090        total_len += write_variant_u64(output, delta)?;
1091    }
1092    Ok(total_len)
1093}
1094#[allow(clippy::too_many_arguments)]
1095#[inline]
1096pub(crate) fn read_frame(
1097    input: &mut (impl Read + Seek),
1098    section_start: u64,
1099    section_length: u64,
1100    signals: &[SignalInfo],
1101    signal_filter: &BitMask,
1102    float_endian: FloatingPointEndian,
1103    start_time: u64,
1104    callback: &mut impl FnMut(u64, FstSignalHandle, FstSignalValue),
1105) -> ReadResult<()> {
1106    // we skip the section header (section_length, start_time, end_time, ???)
1107    input.seek(SeekFrom::Start(section_start + 4 * 8))?;
1108    let (uncompressed_length, _) = read_variant_u64(input)?;
1109    let (compressed_length, _) = read_variant_u64(input)?;
1110    let (max_handle, _) = read_variant_u64(input)?;
1111    assert!(compressed_length <= section_length);
1112    let bytes_vec =
1113        read_zlib_compressed_bytes(input, uncompressed_length, compressed_length, true)?;
1114    let mut bytes = std::io::Cursor::new(bytes_vec);
1115
1116    assert_eq!(signals.len(), max_handle as usize);
1117    for (idx, signal) in signals.iter().enumerate() {
1118        let signal_length = signal.len();
1119        if signal_filter.is_set(idx) {
1120            let handle = FstSignalHandle::from_index(idx);
1121            match signal_length {
1122                0 => {} // ignore since variable-length records have no initial value
1123                len => {
1124                    if !signal.is_real() {
1125                        let value = read_bytes(&mut bytes, len as usize)?;
1126                        callback(start_time, handle, FstSignalValue::String(&value));
1127                    } else {
1128                        let value = read_f64(&mut bytes, float_endian)?;
1129                        callback(start_time, handle, FstSignalValue::Real(value));
1130                    }
1131                }
1132            }
1133        } else {
1134            // skip
1135            bytes.seek(SeekFrom::Current(signal_length as i64))?;
1136        }
1137    }
1138    Ok(())
1139}
1140
1141#[inline]
1142pub(crate) fn skip_frame(input: &mut (impl Read + Seek), section_start: u64) -> ReadResult<()> {
1143    // we skip the section header (section_length, start_time, end_time, ???)
1144    input.seek(SeekFrom::Start(section_start + 4 * 8))?;
1145    let (_uncompressed_length, _) = read_variant_u64(input)?;
1146    let (compressed_length, _) = read_variant_u64(input)?;
1147    let (_max_handle, _) = read_variant_u64(input)?;
1148    input.seek(SeekFrom::Current(compressed_length as i64))?;
1149    Ok(())
1150}
1151
1152#[inline]
1153fn push_zeros(chain_table: &mut Vec<u64>, zeros: u32) {
1154    for _ in 0..zeros {
1155        chain_table.push(0);
1156    }
1157}
1158
1159fn read_value_change_alias2(
1160    mut chain_bytes: &[u8],
1161    max_handle: u64,
1162) -> ReadResult<(Vec<u64>, Vec<u32>, usize)> {
1163    let mut chain_table: Vec<u64> = Vec::with_capacity(max_handle as usize);
1164    let mut chain_table_lengths: Vec<u32> = vec![0u32; (max_handle + 1) as usize];
1165    let mut value = 0u64;
1166    let mut prev_alias = 0u32;
1167    let mut prev_idx = 0usize;
1168    while !chain_bytes.is_empty() {
1169        let idx = chain_table.len();
1170        let kind = chain_bytes[0];
1171        if (kind & 1) == 1 {
1172            let shval = read_variant_i64(&mut chain_bytes)? >> 1;
1173            match shval.cmp(&0) {
1174                Ordering::Greater => {
1175                    value = (value as i64 + shval) as u64;
1176                    if !chain_table.is_empty() {
1177                        let len = (value - chain_table[prev_idx]) as u32;
1178                        chain_table_lengths[prev_idx] = len;
1179                    }
1180                    prev_idx = idx;
1181                    chain_table.push(value);
1182                }
1183                Ordering::Less => {
1184                    chain_table.push(0);
1185                    prev_alias = shval as u32;
1186                    chain_table_lengths[idx] = prev_alias;
1187                }
1188                Ordering::Equal => {
1189                    chain_table.push(0);
1190                    chain_table_lengths[idx] = prev_alias;
1191                }
1192            }
1193        } else {
1194            let (value, _) = read_variant_u32(&mut chain_bytes)?;
1195            let zeros = value >> 1;
1196            push_zeros(&mut chain_table, zeros);
1197        }
1198    }
1199
1200    Ok((chain_table, chain_table_lengths, prev_idx))
1201}
1202
1203fn read_value_change_alias(
1204    mut chain_bytes: &[u8],
1205    max_handle: u64,
1206) -> ReadResult<(Vec<u64>, Vec<u32>, usize)> {
1207    let mut chain_table: Vec<u64> = Vec::with_capacity(max_handle as usize);
1208    let mut chain_table_lengths: Vec<u32> = vec![0u32; (max_handle + 1) as usize];
1209    let mut prev_idx = 0usize;
1210    let mut value = 0u64;
1211    while !chain_bytes.is_empty() {
1212        let (raw_val, _) = read_variant_u32(&mut chain_bytes)?;
1213        let idx = chain_table.len();
1214        if raw_val == 0 {
1215            chain_table.push(0); // alias
1216            let (len, _) = read_variant_u32(&mut chain_bytes)?;
1217            chain_table_lengths[idx] = (-(len as i64)) as u32;
1218        } else if (raw_val & 1) == 1 {
1219            value += (raw_val as u64) >> 1;
1220            if idx > 0 {
1221                let len = (value - chain_table[prev_idx]) as u32;
1222                chain_table_lengths[prev_idx] = len;
1223            }
1224            chain_table.push(value);
1225            prev_idx = idx; // only take non-alias signals into account
1226        } else {
1227            let zeros = raw_val >> 1;
1228            push_zeros(&mut chain_table, zeros);
1229        }
1230    }
1231
1232    Ok((chain_table, chain_table_lengths, prev_idx))
1233}
1234
1235fn fixup_chain_table(chain_table: &mut [u64], chain_lengths: &mut [u32]) {
1236    assert_eq!(chain_table.len(), chain_lengths.len());
1237    for ii in 0..chain_table.len() {
1238        let v32 = chain_lengths[ii] as i32;
1239        if (v32 < 0) && (chain_table[ii] == 0) {
1240            // two's complement
1241            let v32_index = (-v32 - 1) as usize;
1242            if v32_index < ii {
1243                // "sanity check"
1244                chain_table[ii] = chain_table[v32_index];
1245                chain_lengths[ii] = chain_lengths[v32_index];
1246            }
1247        }
1248    }
1249}
1250
1251pub(crate) fn read_chain_table(
1252    input: &mut (impl Read + Seek),
1253    chain_len_offset: u64,
1254    section_kind: DataSectionKind,
1255    max_handle: u64,
1256    start: u64,
1257) -> ReadResult<(Vec<u64>, Vec<u32>)> {
1258    input.seek(SeekFrom::Start(chain_len_offset))?;
1259    let chain_compressed_length = read_u64(input)?;
1260
1261    // the chain starts _chain_length_ bytes before the chain length
1262    let chain_start = chain_len_offset - chain_compressed_length;
1263    input.seek(SeekFrom::Start(chain_start))?;
1264    let chain_bytes = read_bytes(input, chain_compressed_length as usize)?;
1265
1266    let (mut chain_table, mut chain_table_lengths, prev_idx) =
1267        if section_kind == DataSectionKind::DynamicAlias2 {
1268            read_value_change_alias2(&chain_bytes, max_handle)?
1269        } else {
1270            read_value_change_alias(&chain_bytes, max_handle)?
1271        };
1272    let last_table_entry = chain_start - start; // indx_pos - vc_start
1273    chain_table.push(last_table_entry);
1274    chain_table_lengths[prev_idx] = (last_table_entry - chain_table[prev_idx]) as u32;
1275
1276    fixup_chain_table(&mut chain_table, &mut chain_table_lengths);
1277
1278    Ok((chain_table, chain_table_lengths))
1279}
1280
1281#[cfg(test)]
1282mod tests {
1283    use super::*;
1284    use proptest::prelude::*;
1285
1286    #[test]
1287    fn test_read_variant_i64() {
1288        // a positive value from a real fst file (solution from gtkwave)
1289        let in1 = [0x13];
1290        assert_eq!(read_variant_i64(&mut in1.as_slice()).unwrap(), 19);
1291        // a negative value from a real fst file (solution from gtkwave)
1292        let in0 = [0x7b];
1293        assert_eq!(read_variant_i64(&mut in0.as_slice()).unwrap(), -5);
1294    }
1295
1296    proptest! {
1297         #[test]
1298        fn test_read_write_variant_u64(value: u64) {
1299            let mut buf = std::io::Cursor::new(vec![0u8; 24]);
1300            write_variant_u64(&mut buf, value).unwrap();
1301            buf.seek(SeekFrom::Start(0)).unwrap();
1302            let (read_value, _) = read_variant_u64(&mut buf).unwrap();
1303            assert_eq!(read_value, value);
1304        }
1305    }
1306
1307    #[test]
1308    fn test_read_c_str_fixed_length() {
1309        let input = [b'h', b'i', 0u8, b'x'];
1310        assert_eq!(
1311            read_c_str_fixed_length(&mut input.as_slice(), 4).unwrap(),
1312            "hi"
1313        );
1314        let input2 = [b'h', b'i', b'i', 0u8, b'x'];
1315        assert_eq!(
1316            read_c_str_fixed_length(&mut input2.as_slice(), 5).unwrap(),
1317            "hii"
1318        );
1319    }
1320
1321    /// makes sure that there are no zero bytes inside the string and that the max length is obeyed
1322    fn is_valid_c_str(value: &str, max_len: usize) -> bool {
1323        let string_bytes: &[u8] = value.as_bytes();
1324        let len_constraint = string_bytes.len() < max_len;
1325        let non_zero_constraint = !string_bytes.contains(&0u8);
1326        len_constraint && non_zero_constraint
1327    }
1328
1329    fn is_valid_alphanumeric_c_str(value: &str, max_len: usize) -> bool {
1330        let alphanumeric_constraint = value.chars().all(|c| c.is_alphanumeric());
1331        is_valid_c_str(value, max_len) && alphanumeric_constraint
1332    }
1333
1334    proptest! {
1335        #[test]
1336        fn test_write_c_str_fixed_length(string: String, max_len in 1 .. 400usize) {
1337            prop_assume!(is_valid_c_str(&string, max_len));
1338            let mut buf = std::io::Cursor::new(vec![0u8; max_len]);
1339            write_c_str_fixed_length(&mut buf, &string, max_len).unwrap();
1340            buf.seek(SeekFrom::Start(0)).unwrap();
1341            assert_eq!(
1342                read_c_str_fixed_length(&mut buf, max_len).unwrap(),
1343                string
1344            );
1345        }
1346    }
1347
1348    proptest! {
1349        #[test]
1350        fn test_write_c_str(string: String, max_len in 1 .. 400usize) {
1351            prop_assume!(is_valid_c_str(&string, max_len));
1352            let mut buf = std::io::Cursor::new(vec![0u8; max_len]);
1353            write_c_str(&mut buf, &string).unwrap();
1354            buf.seek(SeekFrom::Start(0)).unwrap();
1355            assert_eq!(
1356                read_c_str(&mut buf, max_len).unwrap(),
1357                string
1358            );
1359        }
1360    }
1361
1362    proptest! {
1363        #[test]
1364        fn test_read_write_header(header: Header) {
1365            // return early if the header strings are too long
1366            prop_assume!(header.version.len() <= HEADER_VERSION_MAX_LEN);
1367            prop_assume!(header.date.len() <= HEADER_DATE_MAX_LEN );
1368
1369            let mut buf = [0u8; 512];
1370            write_header(&mut buf.as_mut(), &header).unwrap();
1371            let (actual_header, endian) = read_header(&mut buf.as_slice()).unwrap();
1372            assert_eq!(endian, FloatingPointEndian::Little);
1373            assert_eq!(actual_header, header);
1374        }
1375    }
1376
1377    proptest! {
1378        #[test]
1379        fn test_compress_bytes(bytes: Vec<u8>, allow_uncompressed: bool) {
1380            let mut buf = std::io::Cursor::new(vec![0u8; bytes.len() * 2]);
1381            let compressed_len = write_compressed_bytes(&mut buf, &bytes, flate2::Compression::new(9), allow_uncompressed).unwrap();
1382            if allow_uncompressed {
1383                assert!(compressed_len <= bytes.len());
1384            }
1385            buf.seek(SeekFrom::Start(0)).unwrap();
1386            let uncompressed = read_zlib_compressed_bytes(&mut buf, bytes.len() as u64, compressed_len as u64, allow_uncompressed).unwrap();
1387            assert_eq!(uncompressed, bytes);
1388        }
1389    }
1390
1391    proptest! {
1392        #[test]
1393        fn test_read_write_blackout(mut blackouts: Vec<BlackoutData>) {
1394            // blackout times must be in increasing order => sort
1395            blackouts.sort_by(|a, b| a.time.cmp(&b.time));
1396
1397            // actual test
1398            let max_len = blackouts.len() * 5 + 3 * 8;
1399            let mut buf = std::io::Cursor::new(vec![0u8; max_len]);
1400            write_blackout(&mut buf, &blackouts).unwrap();
1401            buf.seek(SeekFrom::Start(0)).unwrap();
1402            let actual = read_blackout(&mut buf).unwrap();
1403            assert_eq!(actual.len(), blackouts.len());
1404            assert_eq!(actual, blackouts);
1405        }
1406    }
1407
1408    proptest! {
1409        #[test]
1410        fn test_read_write_geometry(signals: Vec<SignalInfo>) {
1411            let max_len = signals.len() * 4 + 3 * 8;
1412            let mut buf = std::io::Cursor::new(vec![0u8; max_len]);
1413            let comp = flate2::Compression::best();
1414            write_geometry(&mut buf, &signals, comp).unwrap();
1415            buf.seek(SeekFrom::Start(0)).unwrap();
1416            let actual = read_geometry(&mut buf).unwrap();
1417            assert_eq!(actual.len(), signals.len());
1418            assert_eq!(actual, signals);
1419        }
1420    }
1421
1422    /// ensures that no string contains zero bytes or is longer than max_len
1423    fn hierarchy_entry_with_valid_c_strings(entry: &FstHierarchyEntry) -> bool {
1424        match entry {
1425            FstHierarchyEntry::Scope {
1426                name, component, ..
1427            } => {
1428                is_valid_c_str(name, HIERARCHY_NAME_MAX_SIZE)
1429                    && is_valid_c_str(component, HIERARCHY_NAME_MAX_SIZE)
1430            }
1431            FstHierarchyEntry::UpScope => true,
1432            FstHierarchyEntry::Var { name, .. } => is_valid_c_str(name, HIERARCHY_NAME_MAX_SIZE),
1433            FstHierarchyEntry::PathName { name, .. } => {
1434                is_valid_c_str(name, HIERARCHY_ATTRIBUTE_MAX_SIZE)
1435            }
1436            FstHierarchyEntry::SourceStem { .. } => true,
1437            FstHierarchyEntry::Comment { string } => {
1438                is_valid_c_str(string, HIERARCHY_ATTRIBUTE_MAX_SIZE)
1439            }
1440            FstHierarchyEntry::EnumTable { name, mapping, .. } => {
1441                is_valid_alphanumeric_c_str(name, HIERARCHY_ATTRIBUTE_MAX_SIZE)
1442                    && mapping.iter().all(|(k, v)| {
1443                        is_valid_alphanumeric_c_str(k, HIERARCHY_ATTRIBUTE_MAX_SIZE)
1444                            && is_valid_alphanumeric_c_str(v, HIERARCHY_ATTRIBUTE_MAX_SIZE)
1445                    })
1446            }
1447            FstHierarchyEntry::EnumTableRef { .. } => true,
1448            FstHierarchyEntry::VhdlVarInfo { type_name, .. } => {
1449                is_valid_c_str(type_name, HIERARCHY_NAME_MAX_SIZE)
1450            }
1451            FstHierarchyEntry::AttributeEnd => true,
1452        }
1453    }
1454
1455    /// ensures that the mapping strings are non-empty and do not contain spaces
1456    fn hierarchy_entry_with_valid_mapping(entry: &FstHierarchyEntry) -> bool {
1457        match entry {
1458            FstHierarchyEntry::EnumTable { mapping, .. } => mapping
1459                .iter()
1460                .all(|(k, v)| is_valid_mapping_str(k) && is_valid_mapping_str(v)),
1461            _ => true,
1462        }
1463    }
1464    fn is_valid_mapping_str(value: &str) -> bool {
1465        !value.is_empty() && !value.contains(' ')
1466    }
1467
1468    /// ensures that ports are not too wide
1469    fn hierarchy_entry_with_valid_port_width(entry: &FstHierarchyEntry) -> bool {
1470        if let FstHierarchyEntry::Var {
1471            tpe: FstVarType::Port,
1472            length,
1473            ..
1474        } = entry
1475        {
1476            *length < (u32::MAX / 3) - 2
1477        } else {
1478            true
1479        }
1480    }
1481
1482    fn read_write_hierarchy_entry(entry: FstHierarchyEntry) {
1483        // the handle count is only important if we are writing a non-aliased variable
1484        let base_handle_count: u32 = match &entry {
1485            FstHierarchyEntry::Var {
1486                handle, is_alias, ..
1487            } => {
1488                if *is_alias {
1489                    0
1490                } else {
1491                    handle.get_index() as u32
1492                }
1493            }
1494            _ => 0,
1495        };
1496
1497        let max_len = 1024 * 64;
1498        let mut buf = std::io::Cursor::new(vec![0u8; max_len]);
1499        let mut handle_count = base_handle_count;
1500        write_hierarchy_entry(&mut buf, &mut handle_count, &entry).unwrap();
1501        if base_handle_count > 0 {
1502            assert_eq!(handle_count, base_handle_count + 1);
1503        }
1504        buf.seek(SeekFrom::Start(0)).unwrap();
1505        handle_count = base_handle_count;
1506        let actual = read_hierarchy_entry(&mut buf, &mut handle_count)
1507            .unwrap()
1508            .unwrap();
1509        assert_eq!(actual, entry);
1510    }
1511
1512    #[test]
1513    fn test_read_write_hierarchy_path_name_entry() {
1514        let entry = FstHierarchyEntry::PathName {
1515            id: 1,
1516            name: "".to_string(),
1517        };
1518        read_write_hierarchy_entry(entry);
1519    }
1520
1521    proptest! {
1522        #[test]
1523        fn test_prop_read_write_hierarchy_entry(entry: FstHierarchyEntry) {
1524            prop_assume!(hierarchy_entry_with_valid_c_strings(&entry));
1525            prop_assume!(hierarchy_entry_with_valid_mapping(&entry));
1526            prop_assume!(hierarchy_entry_with_valid_port_width(&entry));
1527            read_write_hierarchy_entry(entry);
1528        }
1529    }
1530
1531    // test with some manually chosen entries
1532    #[test]
1533    fn test_read_write_hierarchy_entry() {
1534        // make sure that we can write and read long attributes
1535        let entry = FstHierarchyEntry::Comment {
1536            string: "TEST ".repeat((8000 + 4) / 5),
1537        };
1538        read_write_hierarchy_entry(entry);
1539    }
1540
1541    proptest! {
1542        #[test]
1543        fn test_prop_read_write_hierarchy_bytes(tpe: HierarchyCompression, bytes: Vec<u8>) {
1544            let max_len = std::cmp::max(64, bytes.len() + 3 * 8);
1545            let mut buf = std::io::Cursor::new(vec![0u8; max_len]);
1546            write_hierarchy_bytes(&mut buf, tpe, &bytes).unwrap();
1547            buf.seek(SeekFrom::Start(0)).unwrap();
1548            let actual = read_hierarchy_bytes(&mut buf, tpe).unwrap();
1549            assert_eq!(actual, bytes);
1550        }
1551    }
1552
1553    fn read_write_time_table(mut table: Vec<u64>, compressed: bool) {
1554        // the table has to be sorted since we are computing and saving time deltas
1555        table.sort();
1556        let max_len = std::cmp::max(64, table.len() * 8 + 3 * 8);
1557        let mut buf = std::io::Cursor::new(vec![0u8; max_len]);
1558        let comp = if compressed {
1559            Some(flate2::Compression::best())
1560        } else {
1561            None
1562        };
1563        write_time_chain(&mut buf, comp, &table).unwrap();
1564        let section_start = 0u64;
1565        let section_length = buf.stream_position().unwrap();
1566        buf.seek(SeekFrom::Start(0)).unwrap();
1567        let (actual_len, actual_table) =
1568            read_time_chain(&mut buf, section_start, section_length).unwrap();
1569        assert_eq!(actual_len, section_length);
1570        assert_eq!(actual_table, table);
1571    }
1572
1573    #[test]
1574    fn test_read_write_time_table_uncompressed() {
1575        let table = vec![1, 0];
1576        read_write_time_table(table, false);
1577    }
1578
1579    proptest! {
1580        #[test]
1581        fn test_prop_read_write_time_table(table: Vec<u64>, compressed: bool) {
1582            read_write_time_table(table, compressed);
1583        }
1584    }
1585}