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