1use 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#[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 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 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 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 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 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 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 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] pub(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#[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
314pub(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 let first_byte = read_u8(input)?;
328 input.seek(SeekFrom::Start(start))?;
329 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#[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 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 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
423const 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 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
482pub(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 let start = output.stream_position()?;
510 write_u64(output, 0)?; 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 let compressed_len = write_compressed_bytes(output, &bytes, compression, true)? as u64;
524
525 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
535pub(crate) fn read_blackout(input: &mut (impl Read + Seek)) -> ReadResult<Vec<BlackoutData>> {
538 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 let start = output.stream_position()?;
566 write_u64(output, 0)?; 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 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#[cfg(test)]
591const HIERARCHY_GZIP_COMPRESSION_LEVEL: u8 = 4;
592
593fn 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 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 let section_length = read_u64(input)? as usize;
637 let uncompressed_length = read_u64(input)? as usize;
638 let compressed_length = section_length - 2 * 8;
639
640 let bytes = match compression {
641 HierarchyCompression::ZLib => {
642 read_gzip_compressed_bytes(input, uncompressed_length, compressed_length)?
643 }
644 HierarchyCompression::Lz4 => {
645 read_lz4_compressed_bytes(input, uncompressed_length, compressed_length)?
646 }
647 HierarchyCompression::Lz4Duo => {
648 let (len, skiplen) = read_variant_u64(input)?;
650 let lvl1_len = len as usize;
651 let lvl1 = read_lz4_compressed_bytes(input, lvl1_len, compressed_length - skiplen)?;
652 let mut lvl1_reader = lvl1.as_slice();
653 read_lz4_compressed_bytes(&mut lvl1_reader, uncompressed_length, lvl1_len)?
654 }
655 };
656 assert_eq!(bytes.len(), uncompressed_length);
657 Ok(bytes)
658}
659
660#[cfg(test)]
661const GZIP_HEADER: [u8; 10] = [
662 0x1f, 0x8b, 8, 0, 0, 0, 0, 0, 0, 255, ];
669
670#[cfg(test)]
672pub(crate) fn write_gzip_compressed_bytes(
673 output: &mut impl Write,
674 bytes: &[u8],
675 compression_level: u8,
676) -> ReadResult<()> {
677 output.write_all(GZIP_HEADER.as_slice())?;
678 let compressed = miniz_oxide::deflate::compress_to_vec(bytes, compression_level);
679 output.write_all(compressed.as_slice())?;
680 Ok(())
681}
682
683#[cfg(test)]
684pub(crate) fn write_hierarchy_bytes(
685 output: &mut (impl Write + Seek),
686 compression: HierarchyCompression,
687 bytes: &[u8],
688) -> WriteResult<()> {
689 let start = output.stream_position()?;
691 write_u64(output, 0)?; let uncompressed_length = bytes.len() as u64;
693 write_u64(output, uncompressed_length)?;
694
695 match compression {
696 HierarchyCompression::ZLib => {
697 write_gzip_compressed_bytes(output, bytes, HIERARCHY_GZIP_COMPRESSION_LEVEL)?;
698 }
699 HierarchyCompression::Lz4 => {
700 let compressed = lz4_flex::compress(bytes);
701 output.write_all(&compressed)?;
702 }
703 HierarchyCompression::Lz4Duo => {
704 let compressed_lvl1 = lz4_flex::compress(bytes);
705 let lvl1_len = compressed_lvl1.len() as u64;
706 write_variant_u64(output, lvl1_len)?;
707 let compressed_lvl2 = lz4_flex::compress(&compressed_lvl1);
708 output.write_all(&compressed_lvl2)?;
709 }
710 };
711
712 let end = output.stream_position()?;
714 output.seek(SeekFrom::Start(start))?;
715 write_u64(output, end - start)?;
716 output.seek(SeekFrom::Start(end))?;
717 Ok(())
718}
719
720fn enum_table_from_string(value: String, handle: u64) -> ReadResult<FstHierarchyEntry> {
721 let parts: Vec<&str> = value.split(' ').collect();
722 if parts.len() < 2 {
723 return Err(ReaderError::EnumTableString(
724 "not enough spaces".to_string(),
725 value,
726 ));
727 }
728 let name = parts[0].to_string();
729 let element_count = parts[1].parse::<usize>()?;
730 let expected_part_len = element_count * 2;
731 if parts.len() - 2 != expected_part_len {
732 return Err(ReaderError::EnumTableString(
733 format!(
734 "expected {} parts got {}",
735 expected_part_len,
736 parts.len() - 2
737 ),
738 value,
739 ));
740 }
741 let mut mapping = Vec::with_capacity(element_count);
742 for ii in 0..element_count {
743 let name = parts[2 + ii].to_string();
744 let value = parts[2 + element_count + ii].to_string();
745 mapping.push((value, name));
746 }
747 Ok(FstHierarchyEntry::EnumTable {
749 name,
750 handle,
751 mapping,
752 })
753}
754
755#[cfg(test)]
756fn enum_table_to_string(name: &str, mapping: &[(String, String)]) -> String {
757 let mut out = String::with_capacity(name.len() + mapping.len() * 32 + 32);
758 out.push_str(name);
759 out.push(' ');
760 out.push_str(&format!("{}", mapping.len()));
761 for (_value, name) in mapping {
762 out.push(' ');
763 out.push_str(name);
764 }
765 for (value, _name) in mapping {
766 out.push(' ');
767 out.push_str(value);
768 }
769 out
770}
771
772const FST_SUP_VAR_DATA_TYPE_BITS: u32 = 10;
773const FST_SUP_VAR_DATA_TYPE_MASK: u64 = (1 << FST_SUP_VAR_DATA_TYPE_BITS) - 1;
774
775fn parse_misc_attribute(
776 name: String,
777 tpe: MiscType,
778 arg: u64,
779 arg2: Option<u64>,
780) -> ReadResult<FstHierarchyEntry> {
781 let res = match tpe {
782 MiscType::Comment => FstHierarchyEntry::Comment { string: name },
783 MiscType::EnvVar => todo!("EnvVar Attribute"), MiscType::SupVar => {
785 let var_type = (arg >> FST_SUP_VAR_DATA_TYPE_BITS) as u8;
787 let data_type = (arg & FST_SUP_VAR_DATA_TYPE_MASK) as u8;
788 FstHierarchyEntry::VhdlVarInfo {
789 type_name: name,
790 var_type: FstVhdlVarType::try_from_primitive(var_type)?,
791 data_type: FstVhdlDataType::try_from_primitive(data_type)?,
792 }
793 }
794 MiscType::PathName => FstHierarchyEntry::PathName { name, id: arg },
795 MiscType::SourceStem => FstHierarchyEntry::SourceStem {
796 is_instantiation: false,
797 path_id: arg2.unwrap(),
798 line: arg,
799 },
800 MiscType::SourceInstantiationStem => FstHierarchyEntry::SourceStem {
801 is_instantiation: true,
802 path_id: arg2.unwrap(),
803 line: arg,
804 },
805 MiscType::ValueList => todo!("ValueList Attribute"), MiscType::EnumTable => {
807 if name.is_empty() {
808 FstHierarchyEntry::EnumTableRef { handle: arg }
809 } else {
810 enum_table_from_string(name, arg)?
811 }
812 }
813 MiscType::Unknown => todo!("unknown Attribute"),
814 };
815 Ok(res)
816}
817
818fn read_hierarchy_attribute_arg2_encoded_as_name(input: &mut impl Read) -> ReadResult<u64> {
819 let (value, _) = read_variant_u64(input)?;
820 let end_byte = read_u8(input)?;
821 assert_eq!(end_byte, 0, "expected to be zero terminated!");
822 Ok(value)
823}
824
825const HIERARCHY_TPE_VCD_SCOPE: u8 = 254;
826const HIERARCHY_TPE_VCD_UP_SCOPE: u8 = 255;
827const HIERARCHY_TPE_VCD_ATTRIBUTE_BEGIN: u8 = 252;
828const HIERARCHY_TPE_VCD_ATTRIBUTE_END: u8 = 253;
829
830pub(crate) fn read_hierarchy_entry(
831 input: &mut impl Read,
832 handle_count: &mut u32,
833) -> ReadResult<Option<FstHierarchyEntry>> {
834 let entry_tpe = match read_u8(input) {
835 Ok(tpe) => tpe,
836 Err(_) => return Ok(None),
837 };
838 let entry = match entry_tpe {
839 HIERARCHY_TPE_VCD_SCOPE => {
840 let tpe = FstScopeType::try_from_primitive(read_u8(input)?)?;
842 let name = read_c_str(input, HIERARCHY_NAME_MAX_SIZE)?;
843 let component = read_c_str(input, HIERARCHY_NAME_MAX_SIZE)?;
844 FstHierarchyEntry::Scope {
845 tpe,
846 name,
847 component,
848 }
849 }
850 0..=29 => {
851 let tpe = FstVarType::try_from_primitive(entry_tpe)?;
853 let direction = FstVarDirection::try_from_primitive(read_u8(input)?)?;
854 let name = read_c_str(input, HIERARCHY_NAME_MAX_SIZE)?;
855 let (raw_length, _) = read_variant_u32(input)?;
856 let length = if tpe == FstVarType::Port {
857 (raw_length - 2) / 3
859 } else {
860 raw_length
861 };
862 let (alias, _) = read_variant_u32(input)?;
863 let (is_alias, handle) = if alias == 0 {
864 *handle_count += 1;
865 (false, FstSignalHandle::new(*handle_count))
866 } else {
867 (true, FstSignalHandle::new(alias))
868 };
869 FstHierarchyEntry::Var {
870 tpe,
871 direction,
872 name,
873 length,
874 handle,
875 is_alias,
876 }
877 }
878 HIERARCHY_TPE_VCD_UP_SCOPE => {
879 FstHierarchyEntry::UpScope
881 }
882 HIERARCHY_TPE_VCD_ATTRIBUTE_BEGIN => {
883 let tpe = AttributeType::try_from_primitive(read_u8(input)?)?;
884 let subtype = MiscType::try_from_primitive(read_u8(input)?)?;
885 match tpe {
886 AttributeType::Misc => {
887 let (name, arg2) = match subtype {
888 MiscType::SourceStem | MiscType::SourceInstantiationStem => {
889 let arg2 = read_hierarchy_attribute_arg2_encoded_as_name(input)?;
890 ("".to_string(), Some(arg2))
891 }
892 _ => {
893 let name = read_c_str(input, HIERARCHY_ATTRIBUTE_MAX_SIZE)?;
894 (name, None)
895 }
896 };
897 let (arg, _) = read_variant_u64(input)?;
898 parse_misc_attribute(name, subtype, arg, arg2)?
899 }
900 AttributeType::Array => todo!("ARRAY attributes"),
901 AttributeType::Enum => todo!("ENUM attributes"),
902 AttributeType::Pack => todo!("PACK attributes"),
903 }
904 }
905 HIERARCHY_TPE_VCD_ATTRIBUTE_END => {
906 FstHierarchyEntry::AttributeEnd
908 }
909
910 other => todo!("Deal with hierarchy entry of type: {other}"),
911 };
912
913 Ok(Some(entry))
914}
915
916#[cfg(test)]
917fn write_hierarchy_attribute(
918 output: &mut impl Write,
919 tpe: AttributeType,
920 subtype: MiscType,
921 name: &str,
922 arg: u64,
923 arg2: Option<u64>,
924) -> WriteResult<()> {
925 write_u8(output, HIERARCHY_TPE_VCD_ATTRIBUTE_BEGIN)?;
926 write_u8(output, tpe as u8)?;
927 write_u8(output, subtype as u8)?;
928 let raw_name_bytes = match arg2 {
929 None => {
930 assert!(name.len() <= HIERARCHY_ATTRIBUTE_MAX_SIZE);
931 name.to_string().into_bytes()
932 }
933 Some(value) => {
934 assert!(name.is_empty(), "cannot have a name + an arg2!");
935 let mut buf = vec![0u8; 10];
936 let mut buf_writer: &mut [u8] = buf.as_mut();
937 let len = write_variant_u64(&mut buf_writer, value)?;
938 buf.truncate(len);
939 buf
940 }
941 };
942 output.write_all(&raw_name_bytes)?;
943 write_u8(output, 0)?; write_variant_u64(output, arg)?;
945 Ok(())
946}
947
948#[cfg(test)]
949pub(crate) fn write_hierarchy_entry(
950 output: &mut impl Write,
951 handle_count: &mut u32,
952 entry: &FstHierarchyEntry,
953) -> WriteResult<()> {
954 match entry {
955 FstHierarchyEntry::Scope {
956 tpe,
957 name,
958 component,
959 } => {
960 write_u8(output, HIERARCHY_TPE_VCD_SCOPE)?;
961 write_u8(output, *tpe as u8)?;
962 assert!(name.len() <= HIERARCHY_NAME_MAX_SIZE);
963 write_c_str(output, name)?;
964 assert!(component.len() <= HIERARCHY_NAME_MAX_SIZE);
965 write_c_str(output, component)?;
966 }
967 FstHierarchyEntry::UpScope => {
968 write_u8(output, HIERARCHY_TPE_VCD_UP_SCOPE)?;
969 }
970 FstHierarchyEntry::Var {
971 tpe,
972 direction,
973 name,
974 length,
975 handle,
976 is_alias,
977 } => {
978 write_u8(output, *tpe as u8)?;
979 write_u8(output, *direction as u8)?;
980 assert!(name.len() <= HIERARCHY_NAME_MAX_SIZE);
981 write_c_str(output, name)?;
982 let raw_length = if *tpe == FstVarType::Port {
983 3 * (*length) + 2
984 } else {
985 *length
986 };
987 write_variant_u32(output, raw_length)?;
988 if *is_alias {
989 write_variant_u32(output, handle.get_raw())?;
990 } else {
991 assert_eq!(handle.get_index(), *handle_count as usize);
993 *handle_count += 1;
994 write_variant_u32(output, 0)?;
996 }
997 }
998 FstHierarchyEntry::PathName { name, id } => write_hierarchy_attribute(
999 output,
1000 AttributeType::Misc,
1001 MiscType::PathName,
1002 name,
1003 *id,
1004 None,
1005 )?,
1006 FstHierarchyEntry::SourceStem {
1007 is_instantiation,
1008 path_id,
1009 line,
1010 } => {
1011 let subtpe = if *is_instantiation {
1012 MiscType::SourceInstantiationStem
1013 } else {
1014 MiscType::SourceStem
1015 };
1016 write_hierarchy_attribute(
1017 output,
1018 AttributeType::Misc,
1019 subtpe,
1020 "",
1021 *line,
1022 Some(*path_id),
1023 )?
1024 }
1025 FstHierarchyEntry::Comment { string } => write_hierarchy_attribute(
1026 output,
1027 AttributeType::Misc,
1028 MiscType::Comment,
1029 string,
1030 0,
1031 None,
1032 )?,
1033 FstHierarchyEntry::EnumTable {
1034 name,
1035 handle,
1036 mapping,
1037 } => {
1038 let table_str = enum_table_to_string(name, mapping);
1039 write_hierarchy_attribute(
1040 output,
1041 AttributeType::Misc,
1042 MiscType::EnumTable,
1043 &table_str,
1044 *handle,
1045 None,
1046 )?
1047 }
1048 FstHierarchyEntry::EnumTableRef { handle } => write_hierarchy_attribute(
1049 output,
1050 AttributeType::Misc,
1051 MiscType::EnumTable,
1052 "",
1053 *handle,
1054 None,
1055 )?,
1056 FstHierarchyEntry::VhdlVarInfo {
1057 type_name,
1058 var_type,
1059 data_type,
1060 } => {
1061 let arg = ((*var_type as u64) << FST_SUP_VAR_DATA_TYPE_BITS) | (*data_type as u64);
1062 write_hierarchy_attribute(
1063 output,
1064 AttributeType::Misc,
1065 MiscType::SupVar,
1066 type_name,
1067 arg,
1068 None,
1069 )?;
1070 }
1071 FstHierarchyEntry::AttributeEnd => {
1072 write_u8(output, HIERARCHY_TPE_VCD_ATTRIBUTE_END)?;
1073 }
1074 }
1075
1076 Ok(())
1077}
1078
1079pub(crate) fn read_packed_signal_value_bytes(
1082 input: &mut (impl Read + Seek),
1083 len: u32,
1084 tpe: ValueChangePackType,
1085) -> ReadResult<Vec<u8>> {
1086 let (value, skiplen) = read_variant_u32(input)?;
1087 if value != 0 {
1088 let uncompressed_length = value as u64;
1089 let uncompressed: Vec<u8> = match tpe {
1090 ValueChangePackType::Lz4 => {
1091 let compressed_length = (len - skiplen) as u64;
1092 read_lz4_compressed_bytes(
1093 input,
1094 uncompressed_length as usize,
1095 compressed_length as usize,
1096 )?
1097 }
1098 ValueChangePackType::FastLz => {
1099 let compressed_length = (len - skiplen) as u64;
1100 crate::fastlz::decompress(
1101 input,
1102 compressed_length as usize,
1103 uncompressed_length as usize,
1104 )?
1105 }
1106 ValueChangePackType::Zlib => {
1107 let compressed_length = len as u64;
1108 read_zlib_compressed_bytes(input, uncompressed_length, compressed_length, false)?
1111 }
1112 };
1113 Ok(uncompressed)
1114 } else {
1115 let dest_length = len - skiplen;
1116 let bytes = read_bytes(input, dest_length as usize)?;
1117 Ok(bytes)
1118 }
1119}
1120
1121pub(crate) fn read_time_table(
1122 input: &mut (impl Read + Seek),
1123 section_start: u64,
1124 section_length: u64,
1125) -> ReadResult<(u64, Vec<u64>)> {
1126 input.seek(SeekFrom::Start(section_start + section_length - 3 * 8))?;
1128 let uncompressed_length = read_u64(input)?;
1129 let compressed_length = read_u64(input)?;
1130 let number_of_items = read_u64(input)?;
1131 assert!(compressed_length <= section_length);
1132
1133 input.seek(SeekFrom::Current(-(3 * 8) - (compressed_length as i64)))?;
1135 let bytes = read_zlib_compressed_bytes(input, uncompressed_length, compressed_length, true)?;
1136 let mut byte_reader: &[u8] = &bytes;
1137 let mut time_table: Vec<u64> = Vec::with_capacity(number_of_items as usize);
1138 let mut time_val: u64 = 0; for _ in 0..number_of_items {
1141 let (value, _) = read_variant_u64(&mut byte_reader)?;
1142 time_val += value;
1143 time_table.push(time_val);
1144 }
1145
1146 let time_section_length = compressed_length + 3 * 8;
1147 Ok((time_section_length, time_table))
1148}
1149
1150#[cfg(test)]
1151pub(crate) fn write_time_table(
1152 output: &mut (impl Write + Seek),
1153 compression: Option<u8>,
1154 table: &[u64],
1155) -> WriteResult<()> {
1156 let num_entries = table.len();
1158 let table = delta_compress_time_table(table)?;
1159 let (uncompressed_len, compressed_len) = match compression {
1161 Some(comp) => {
1162 let compressed = miniz_oxide::deflate::compress_to_vec_zlib(table.as_slice(), comp);
1163 if compressed.len() < table.len() {
1165 output.write_all(compressed.as_slice())?;
1166 (table.len(), compressed.len())
1167 } else {
1168 output.write_all(table.as_slice())?;
1170 (table.len(), table.len())
1171 }
1172 }
1173 None => {
1174 output.write_all(table.as_slice())?;
1175 (table.len(), table.len())
1176 }
1177 };
1178 write_u64(output, uncompressed_len as u64)?;
1179 write_u64(output, compressed_len as u64)?;
1180 write_u64(output, num_entries as u64)?;
1181
1182 Ok(())
1183}
1184
1185#[cfg(test)]
1186#[inline]
1187fn delta_compress_time_table(table: &[u64]) -> WriteResult<Vec<u8>> {
1188 let mut output = vec![];
1189 let mut prev_time = 0u64;
1190 for time in table {
1191 let delta = *time - prev_time;
1192 prev_time = *time;
1193 write_variant_u64(&mut output, delta)?;
1194 }
1195 Ok(output)
1196}
1197#[allow(clippy::too_many_arguments)]
1198#[inline]
1199pub(crate) fn read_frame(
1200 input: &mut (impl Read + Seek),
1201 section_start: u64,
1202 section_length: u64,
1203 signals: &[SignalInfo],
1204 signal_filter: &BitMask,
1205 float_endian: FloatingPointEndian,
1206 start_time: u64,
1207 callback: &mut impl FnMut(u64, FstSignalHandle, FstSignalValue),
1208) -> ReadResult<()> {
1209 input.seek(SeekFrom::Start(section_start + 4 * 8))?;
1211 let (uncompressed_length, _) = read_variant_u64(input)?;
1212 let (compressed_length, _) = read_variant_u64(input)?;
1213 let (max_handle, _) = read_variant_u64(input)?;
1214 assert!(compressed_length <= section_length);
1215 let bytes_vec =
1216 read_zlib_compressed_bytes(input, uncompressed_length, compressed_length, true)?;
1217 let mut bytes = std::io::Cursor::new(bytes_vec);
1218
1219 assert_eq!(signals.len(), max_handle as usize);
1220 for (idx, signal) in signals.iter().enumerate() {
1221 let signal_length = signal.len();
1222 if signal_filter.is_set(idx) {
1223 let handle = FstSignalHandle::from_index(idx);
1224 match signal_length {
1225 0 => {} len => {
1227 if !signal.is_real() {
1228 let value = read_bytes(&mut bytes, len as usize)?;
1229 callback(start_time, handle, FstSignalValue::String(&value));
1230 } else {
1231 let value = read_f64(&mut bytes, float_endian)?;
1232 callback(start_time, handle, FstSignalValue::Real(value));
1233 }
1234 }
1235 }
1236 } else {
1237 bytes.seek(SeekFrom::Current(signal_length as i64))?;
1239 }
1240 }
1241 Ok(())
1242}
1243
1244#[inline]
1245pub(crate) fn skip_frame(input: &mut (impl Read + Seek), section_start: u64) -> ReadResult<()> {
1246 input.seek(SeekFrom::Start(section_start + 4 * 8))?;
1248 let (_uncompressed_length, _) = read_variant_u64(input)?;
1249 let (compressed_length, _) = read_variant_u64(input)?;
1250 let (_max_handle, _) = read_variant_u64(input)?;
1251 input.seek(SeekFrom::Current(compressed_length as i64))?;
1252 Ok(())
1253}
1254
1255#[derive(Debug)]
1257pub(crate) struct OffsetTable(Vec<SignalDataLoc>);
1258
1259impl From<Vec<SignalDataLoc>> for OffsetTable {
1260 fn from(value: Vec<SignalDataLoc>) -> Self {
1261 Self(value)
1262 }
1263}
1264
1265impl OffsetTable {
1266 pub(crate) fn iter(&self) -> OffsetTableIter {
1267 OffsetTableIter {
1268 table: self,
1269 signal_idx: 0,
1270 }
1271 }
1272
1273 #[allow(dead_code)]
1274 pub(crate) fn len(&self) -> usize {
1275 self.0.len()
1276 }
1277
1278 fn get_entry(&self, signal_idx: usize) -> Option<OffsetEntry> {
1279 match &self.0[signal_idx] {
1280 SignalDataLoc::None => None,
1281 SignalDataLoc::Alias(alias_idx) => match &self.0[*alias_idx as usize] {
1284 SignalDataLoc::Offset(offset, len) => Some(OffsetEntry {
1285 signal_idx,
1286 offset: offset.get() as u64,
1287 len: len.get(),
1288 }),
1289 _ => unreachable!("aliases should always directly point to an offset"),
1290 },
1291 SignalDataLoc::Offset(offset, len) => Some(OffsetEntry {
1292 signal_idx,
1293 offset: offset.get() as u64,
1294 len: len.get(),
1295 }),
1296 }
1297 }
1298}
1299
1300pub(crate) struct OffsetTableIter<'a> {
1301 table: &'a OffsetTable,
1302 signal_idx: usize,
1303}
1304
1305#[derive(Debug)]
1306pub(crate) struct OffsetEntry {
1307 pub(crate) signal_idx: usize,
1308 pub(crate) offset: u64,
1309 pub(crate) len: u32,
1310}
1311impl Iterator for OffsetTableIter<'_> {
1312 type Item = OffsetEntry;
1313
1314 fn next(&mut self) -> Option<Self::Item> {
1315 while self.signal_idx < self.table.0.len()
1317 && matches!(self.table.0[self.signal_idx], SignalDataLoc::None)
1318 {
1319 self.signal_idx += 1
1320 }
1321
1322 if self.signal_idx >= self.table.0.len() {
1324 return None;
1325 }
1326
1327 self.signal_idx += 1;
1329
1330 let res = self.table.get_entry(self.signal_idx - 1);
1332 debug_assert!(res.is_some());
1333 res
1334 }
1335}
1336
1337fn read_value_change_alias2(
1338 mut chain_bytes: &[u8],
1339 max_handle: u64,
1340 last_table_entry: u32,
1341) -> ReadResult<OffsetTable> {
1342 let mut table = Vec::with_capacity(max_handle as usize);
1343 let mut offset: Option<NonZeroU32> = None;
1344 let mut prev_alias = 0u32;
1345 let mut prev_offset_idx = 0usize;
1346 while !chain_bytes.is_empty() {
1347 let idx = table.len();
1348 let kind = chain_bytes[0];
1349 if (kind & 1) == 1 {
1350 let shval = read_variant_i64(&mut chain_bytes)? >> 1;
1351 match shval.cmp(&0) {
1352 Ordering::Greater => {
1353 let new_offset = NonZeroU32::new(
1355 (offset.map(|o| o.get()).unwrap_or_default() as i64 + shval) as u32,
1356 )
1357 .unwrap();
1358 if let Some(prev_offset) = offset {
1360 let len = NonZeroU32::new(new_offset.get() - prev_offset.get()).unwrap();
1361 table[prev_offset_idx] = SignalDataLoc::Offset(prev_offset, len);
1362 }
1363 offset = Some(new_offset);
1364 prev_offset_idx = idx;
1365 table.push(SignalDataLoc::None);
1367 }
1368 Ordering::Less => {
1369 prev_alias = (-shval - 1) as u32;
1371 table.push(SignalDataLoc::Alias(prev_alias));
1372 }
1373 Ordering::Equal => {
1374 table.push(SignalDataLoc::Alias(prev_alias));
1376 }
1377 }
1378 } else {
1379 let (value, _) = read_variant_u32(&mut chain_bytes)?;
1381 let zeros = value >> 1;
1382 for _ in 0..zeros {
1383 table.push(SignalDataLoc::None);
1384 }
1385 }
1386 }
1387
1388 if let Some(prev_offset) = offset {
1390 let len = NonZeroU32::new(last_table_entry - prev_offset.get()).unwrap();
1391 table[prev_offset_idx] = SignalDataLoc::Offset(prev_offset, len);
1392 }
1393
1394 Ok(table.into())
1395}
1396
1397fn read_value_change_alias(
1398 mut chain_bytes: &[u8],
1399 max_handle: u64,
1400 last_table_entry: u32,
1401) -> ReadResult<OffsetTable> {
1402 let mut table = Vec::with_capacity(max_handle as usize);
1403 let mut prev_offset_idx = 0usize;
1404 let mut offset: Option<NonZeroU32> = None;
1405 while !chain_bytes.is_empty() {
1406 let (raw_val, _) = read_variant_u32(&mut chain_bytes)?;
1407 let idx = table.len();
1408 if raw_val == 0 {
1409 let (raw_alias, _) = read_variant_u32(&mut chain_bytes)?;
1410 let alias = ((raw_alias as i64) - 1) as u32;
1411 table.push(SignalDataLoc::Alias(alias));
1412 } else if (raw_val & 1) == 1 {
1413 let new_offset =
1415 NonZeroU32::new(offset.map(|o| o.get()).unwrap_or_default() + (raw_val >> 1))
1416 .unwrap();
1417 if let Some(prev_offset) = offset {
1419 let len = NonZeroU32::new(new_offset.get() - prev_offset.get()).unwrap();
1420 table[prev_offset_idx] = SignalDataLoc::Offset(prev_offset, len);
1421 }
1422 offset = Some(new_offset);
1423 prev_offset_idx = idx;
1424 table.push(SignalDataLoc::None);
1426 } else {
1427 let zeros = raw_val >> 1;
1429 for _ in 0..zeros {
1430 table.push(SignalDataLoc::None);
1431 }
1432 }
1433 }
1434
1435 if let Some(prev_offset) = offset {
1437 let len = NonZeroU32::new(last_table_entry - prev_offset.get()).unwrap();
1438 table[prev_offset_idx] = SignalDataLoc::Offset(prev_offset, len);
1439 }
1440
1441 Ok(table.into())
1442}
1443
1444#[derive(Debug, Copy, Clone)]
1446enum SignalDataLoc {
1447 None,
1449 Alias(u32),
1451 Offset(NonZeroU32, NonZeroU32),
1453}
1454
1455pub(crate) fn read_signal_locs(
1456 input: &mut (impl Read + Seek),
1457 chain_len_offset: u64,
1458 section_kind: DataSectionKind,
1459 max_handle: u64,
1460 start: u64,
1461) -> ReadResult<OffsetTable> {
1462 input.seek(SeekFrom::Start(chain_len_offset))?;
1463 let chain_compressed_length = read_u64(input)?;
1464
1465 let chain_start = chain_len_offset - chain_compressed_length;
1467 input.seek(SeekFrom::Start(chain_start))?;
1468 let chain_bytes = read_bytes(input, chain_compressed_length as usize)?;
1469
1470 let last_table_entry = (chain_start - start) as u32; if section_kind == DataSectionKind::DynamicAlias2 {
1472 read_value_change_alias2(&chain_bytes, max_handle, last_table_entry)
1473 } else {
1474 read_value_change_alias(&chain_bytes, max_handle, last_table_entry)
1475 }
1476}
1477
1478#[cfg(test)]
1479mod tests {
1480 use super::*;
1481 use proptest::prelude::*;
1482
1483 #[test]
1484 fn data_struct_sizes() {
1485 assert_eq!(
1486 std::mem::size_of::<SignalDataLoc>(),
1487 std::mem::size_of::<u64>() + std::mem::size_of::<u32>()
1488 );
1489 }
1490
1491 #[test]
1492 fn test_read_variant_i64() {
1493 let in1 = [0x13];
1495 assert_eq!(read_variant_i64(&mut in1.as_slice()).unwrap(), 19);
1496 let in0 = [0x7b];
1498 assert_eq!(read_variant_i64(&mut in0.as_slice()).unwrap(), -5);
1499 }
1500
1501 #[test]
1502 fn regression_test_read_write_variant_i64() {
1503 do_test_read_write_variant_i64(-36028797018963969);
1504 do_test_read_write_variant_i64(-4611686018427387905);
1505 }
1506
1507 fn do_test_read_write_variant_i64(value: i64) {
1508 let mut buf = std::io::Cursor::new(vec![0u8; 24]);
1509 write_variant_i64(&mut buf, value).unwrap();
1510 buf.seek(SeekFrom::Start(0)).unwrap();
1511 let read_value = read_variant_i64(&mut buf).unwrap();
1512 assert_eq!(read_value, value);
1513 }
1514
1515 proptest! {
1516 #[test]
1517 fn test_read_write_variant_u64(value: u64) {
1518 let mut buf = std::io::Cursor::new(vec![0u8; 24]);
1519 write_variant_u64(&mut buf, value).unwrap();
1520 buf.seek(SeekFrom::Start(0)).unwrap();
1521 let (read_value, _) = read_variant_u64(&mut buf).unwrap();
1522 assert_eq!(read_value, value);
1523 }
1524
1525 #[test]
1526 fn test_read_write_variant_i64(value: i64) {
1527 do_test_read_write_variant_i64(value);
1528 }
1529 }
1530
1531 #[test]
1532 fn test_read_c_str_fixed_length() {
1533 let input = [b'h', b'i', 0u8, b'x'];
1534 assert_eq!(
1535 read_c_str_fixed_length(&mut input.as_slice(), 4).unwrap(),
1536 "hi"
1537 );
1538 let input2 = [b'h', b'i', b'i', 0u8, b'x'];
1539 assert_eq!(
1540 read_c_str_fixed_length(&mut input2.as_slice(), 5).unwrap(),
1541 "hii"
1542 );
1543 }
1544
1545 fn is_valid_c_str(value: &str, max_len: usize) -> bool {
1547 let string_bytes: &[u8] = value.as_bytes();
1548 let len_constraint = string_bytes.len() < max_len;
1549 let non_zero_constraint = !string_bytes.contains(&0u8);
1550 len_constraint && non_zero_constraint
1551 }
1552
1553 fn is_valid_alphanumeric_c_str(value: &str, max_len: usize) -> bool {
1554 let alphanumeric_constraint = value.chars().all(|c| c.is_alphanumeric());
1555 is_valid_c_str(value, max_len) && alphanumeric_constraint
1556 }
1557
1558 proptest! {
1559 #[test]
1560 fn test_write_c_str_fixed_length(string: String, max_len in 1 .. 400usize) {
1561 prop_assume!(is_valid_c_str(&string, max_len));
1562 let mut buf = std::io::Cursor::new(vec![0u8; max_len]);
1563 write_c_str_fixed_length(&mut buf, &string, max_len).unwrap();
1564 buf.seek(SeekFrom::Start(0)).unwrap();
1565 assert_eq!(
1566 read_c_str_fixed_length(&mut buf, max_len).unwrap(),
1567 string
1568 );
1569 }
1570 }
1571
1572 proptest! {
1573 #[test]
1574 fn test_write_c_str(string: String, max_len in 1 .. 400usize) {
1575 prop_assume!(is_valid_c_str(&string, max_len));
1576 let mut buf = std::io::Cursor::new(vec![0u8; max_len]);
1577 write_c_str(&mut buf, &string).unwrap();
1578 buf.seek(SeekFrom::Start(0)).unwrap();
1579 assert_eq!(
1580 read_c_str(&mut buf, max_len).unwrap(),
1581 string
1582 );
1583 }
1584 }
1585
1586 proptest! {
1587 #[test]
1588 fn test_read_write_header(header: Header) {
1589 prop_assume!(header.version.len() <= HEADER_VERSION_MAX_LEN);
1591 prop_assume!(header.date.len() <= HEADER_DATE_MAX_LEN );
1592
1593 let mut buf = [0u8; 512];
1594 write_header(&mut buf.as_mut(), &header).unwrap();
1595 let (actual_header, endian) = read_header(&mut buf.as_slice()).unwrap();
1596 assert_eq!(endian, FloatingPointEndian::Little);
1597 assert_eq!(actual_header, header);
1598 }
1599 }
1600
1601 proptest! {
1602 #[test]
1603 fn test_compress_bytes(bytes: Vec<u8>, allow_uncompressed: bool) {
1604 let mut buf = std::io::Cursor::new(vec![0u8; bytes.len() * 2]);
1605 let compressed_len = write_compressed_bytes(&mut buf, &bytes, 3, allow_uncompressed).unwrap();
1606 if allow_uncompressed {
1607 assert!(compressed_len <= bytes.len());
1608 }
1609 buf.seek(SeekFrom::Start(0)).unwrap();
1610 let uncompressed = read_zlib_compressed_bytes(&mut buf, bytes.len() as u64, compressed_len as u64, allow_uncompressed).unwrap();
1611 assert_eq!(uncompressed, bytes);
1612 }
1613 }
1614
1615 proptest! {
1616 #[test]
1617 fn test_read_write_blackout(mut blackouts: Vec<BlackoutData>) {
1618 blackouts.sort_by(|a, b| a.time.cmp(&b.time));
1620
1621 let max_len = blackouts.len() * 5 + 3 * 8;
1623 let mut buf = std::io::Cursor::new(vec![0u8; max_len]);
1624 write_blackout(&mut buf, &blackouts).unwrap();
1625 buf.seek(SeekFrom::Start(0)).unwrap();
1626 let actual = read_blackout(&mut buf).unwrap();
1627 assert_eq!(actual.len(), blackouts.len());
1628 assert_eq!(actual, blackouts);
1629 }
1630 }
1631
1632 proptest! {
1633 #[test]
1634 fn test_read_write_geometry(signals: Vec<SignalInfo>) {
1635 let max_len = signals.len() * 4 + 3 * 8;
1636 let mut buf = std::io::Cursor::new(vec![0u8; max_len]);
1637 write_geometry(&mut buf, &signals, 3).unwrap();
1638 buf.seek(SeekFrom::Start(0)).unwrap();
1639 let actual = read_geometry(&mut buf).unwrap();
1640 assert_eq!(actual.len(), signals.len());
1641 assert_eq!(actual, signals);
1642 }
1643 }
1644
1645 fn hierarchy_entry_with_valid_c_strings(entry: &FstHierarchyEntry) -> bool {
1647 match entry {
1648 FstHierarchyEntry::Scope {
1649 name, component, ..
1650 } => {
1651 is_valid_c_str(name, HIERARCHY_NAME_MAX_SIZE)
1652 && is_valid_c_str(component, HIERARCHY_NAME_MAX_SIZE)
1653 }
1654 FstHierarchyEntry::UpScope => true,
1655 FstHierarchyEntry::Var { name, .. } => is_valid_c_str(name, HIERARCHY_NAME_MAX_SIZE),
1656 FstHierarchyEntry::PathName { name, .. } => {
1657 is_valid_c_str(name, HIERARCHY_ATTRIBUTE_MAX_SIZE)
1658 }
1659 FstHierarchyEntry::SourceStem { .. } => true,
1660 FstHierarchyEntry::Comment { string } => {
1661 is_valid_c_str(string, HIERARCHY_ATTRIBUTE_MAX_SIZE)
1662 }
1663 FstHierarchyEntry::EnumTable { name, mapping, .. } => {
1664 is_valid_alphanumeric_c_str(name, HIERARCHY_ATTRIBUTE_MAX_SIZE)
1665 && mapping.iter().all(|(k, v)| {
1666 is_valid_alphanumeric_c_str(k, HIERARCHY_ATTRIBUTE_MAX_SIZE)
1667 && is_valid_alphanumeric_c_str(v, HIERARCHY_ATTRIBUTE_MAX_SIZE)
1668 })
1669 }
1670 FstHierarchyEntry::EnumTableRef { .. } => true,
1671 FstHierarchyEntry::VhdlVarInfo { type_name, .. } => {
1672 is_valid_c_str(type_name, HIERARCHY_NAME_MAX_SIZE)
1673 }
1674 FstHierarchyEntry::AttributeEnd => true,
1675 }
1676 }
1677
1678 fn hierarchy_entry_with_valid_mapping(entry: &FstHierarchyEntry) -> bool {
1680 match entry {
1681 FstHierarchyEntry::EnumTable { mapping, .. } => mapping
1682 .iter()
1683 .all(|(k, v)| is_valid_mapping_str(k) && is_valid_mapping_str(v)),
1684 _ => true,
1685 }
1686 }
1687 fn is_valid_mapping_str(value: &str) -> bool {
1688 !value.is_empty() && !value.contains(' ')
1689 }
1690
1691 fn hierarchy_entry_with_valid_port_width(entry: &FstHierarchyEntry) -> bool {
1693 if let FstHierarchyEntry::Var {
1694 tpe: FstVarType::Port,
1695 length,
1696 ..
1697 } = entry
1698 {
1699 *length < (u32::MAX / 3) - 2
1700 } else {
1701 true
1702 }
1703 }
1704
1705 fn read_write_hierarchy_entry(entry: FstHierarchyEntry) {
1706 let base_handle_count: u32 = match &entry {
1708 FstHierarchyEntry::Var {
1709 handle, is_alias, ..
1710 } => {
1711 if *is_alias {
1712 0
1713 } else {
1714 handle.get_index() as u32
1715 }
1716 }
1717 _ => 0,
1718 };
1719
1720 let max_len = 1024 * 64;
1721 let mut buf = std::io::Cursor::new(vec![0u8; max_len]);
1722 let mut handle_count = base_handle_count;
1723 write_hierarchy_entry(&mut buf, &mut handle_count, &entry).unwrap();
1724 if base_handle_count > 0 {
1725 assert_eq!(handle_count, base_handle_count + 1);
1726 }
1727 buf.seek(SeekFrom::Start(0)).unwrap();
1728 handle_count = base_handle_count;
1729 let actual = read_hierarchy_entry(&mut buf, &mut handle_count)
1730 .unwrap()
1731 .unwrap();
1732 assert_eq!(actual, entry);
1733 }
1734
1735 #[test]
1736 fn test_read_write_hierarchy_path_name_entry() {
1737 let entry = FstHierarchyEntry::PathName {
1738 id: 1,
1739 name: "".to_string(),
1740 };
1741 read_write_hierarchy_entry(entry);
1742 }
1743
1744 proptest! {
1745 #[test]
1746 fn test_prop_read_write_hierarchy_entry(entry: FstHierarchyEntry) {
1747 prop_assume!(hierarchy_entry_with_valid_c_strings(&entry));
1748 prop_assume!(hierarchy_entry_with_valid_mapping(&entry));
1749 prop_assume!(hierarchy_entry_with_valid_port_width(&entry));
1750 read_write_hierarchy_entry(entry);
1751 }
1752 }
1753
1754 #[test]
1756 fn test_read_write_hierarchy_entry() {
1757 let entry = FstHierarchyEntry::Comment {
1759 string: "TEST ".repeat((8000 + 4) / 5),
1760 };
1761 read_write_hierarchy_entry(entry);
1762 }
1763
1764 fn do_test_read_write_hierarchy_bytes(tpe: HierarchyCompression, bytes: Vec<u8>) {
1765 let max_len = std::cmp::max(64, bytes.len() + 3 * 8);
1766 let mut buf = std::io::Cursor::new(vec![0u8; max_len]);
1767 write_hierarchy_bytes(&mut buf, tpe, &bytes).unwrap();
1768 buf.seek(SeekFrom::Start(0)).unwrap();
1769 let actual = read_hierarchy_bytes(&mut buf, tpe).unwrap();
1770 assert_eq!(actual, bytes);
1771 }
1772
1773 #[test]
1774 fn test_read_write_hierarchy_bytes_regression() {
1775 do_test_read_write_hierarchy_bytes(HierarchyCompression::Lz4, vec![]);
1776 do_test_read_write_hierarchy_bytes(HierarchyCompression::ZLib, vec![]);
1777 }
1778
1779 proptest! {
1780 #[test]
1781 fn test_prop_read_write_hierarchy_bytes(tpe: HierarchyCompression, bytes: Vec<u8>) {
1782 do_test_read_write_hierarchy_bytes(tpe, bytes);
1783 }
1784 }
1785
1786 fn read_write_time_table(mut table: Vec<u64>, compressed: bool) {
1787 table.sort();
1789 let max_len = std::cmp::max(64, table.len() * 8 + 3 * 8);
1790 let mut buf = std::io::Cursor::new(vec![0u8; max_len]);
1791 let comp = if compressed { Some(3) } else { None };
1792 write_time_table(&mut buf, comp, &table).unwrap();
1793 let section_start = 0u64;
1794 let section_length = buf.stream_position().unwrap();
1795 buf.seek(SeekFrom::Start(0)).unwrap();
1796 let (actual_len, actual_table) =
1797 read_time_table(&mut buf, section_start, section_length).unwrap();
1798 assert_eq!(actual_len, section_length);
1799 assert_eq!(actual_table, table);
1800 }
1801
1802 #[test]
1803 fn test_read_write_time_table_uncompressed() {
1804 let table = vec![1, 0];
1805 read_write_time_table(table, false);
1806 }
1807
1808 #[test]
1809 fn test_read_write_time_table_compressed() {
1810 let table = (0..10000).collect();
1811 read_write_time_table(table, true);
1812 }
1813
1814 proptest! {
1815 #[test]
1816 fn test_prop_read_write_time_table(table: Vec<u64>, compressed: bool) {
1817 read_write_time_table(table, compressed);
1818 }
1819 }
1820}