fst_reader/
io.rs

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