From f23cb9c9ac3813896b36f9ee3519533f6e03081e Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Sat, 5 Nov 2022 17:48:17 +0000 Subject: [PATCH 1/6] Keep track of first item ID on each endpoint. --- src/decoder.rs | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/decoder.rs b/src/decoder.rs index 6ea658e6..ffc3b2e3 100644 --- a/src/decoder.rs +++ b/src/decoder.rs @@ -12,6 +12,7 @@ struct EndpointData { device_id: DeviceId, address: EndpointAddr, writer: EndpointWriter, + start_item: Option, early_start: Option, active: Option, ended: Option, @@ -59,6 +60,7 @@ impl EndpointData { address, device_id, writer, + start_item: None, early_start: None, active: None, ended: None, @@ -934,7 +936,7 @@ impl Decoder { let group_end_id = self.add_group_entry(endpoint_id, ep_group_id, false)?; if self.last_item_endpoint != Some(endpoint_id) { - self.add_item(endpoint_id, group_end_id)?; + self.add_item(endpoint_id, group_end_id, false)?; } } Ok(()) @@ -965,7 +967,7 @@ impl Decoder { ep_data.writer.group_index.push(ep_transaction_id)?; let group_start_id = self.add_group_entry(endpoint_id, ep_group_id, true)?; - self.add_item(endpoint_id, group_start_id)?; + self.add_item(endpoint_id, group_start_id, true)?; Ok(ep_group_id) } @@ -1009,11 +1011,12 @@ impl Decoder { Ok(state_id) } - fn add_item(&mut self, - item_endpoint_id: EndpointId, - group_id: GroupId) - -> Result - { + fn add_item( + &mut self, + item_endpoint_id: EndpointId, + group_id: GroupId, + start: bool, + ) -> Result { let item_id = self.capture.item_index.push(group_id)?; self.last_item_endpoint = Some(item_endpoint_id); @@ -1022,6 +1025,13 @@ impl Decoder { for i in 0..endpoint_count { let endpoint_id = EndpointId::from(i); let ep_data = &mut self.endpoint_data[endpoint_id]; + if start && endpoint_id == item_endpoint_id && + ep_data.start_item.replace(item_id).is_none() + { + // This is the first item since this endpoint appeared. + let first_item_id = Some(Arc::new(item_id)); + ep_data.writer.shared.first_item_id.swap(first_item_id); + } if let Some(ep_group_id) = ep_data.ended.take() { // This group has ended and is not yet linked to an item. let end_id = ep_data.writer.end_index.push(item_id)?; From 14a5194c14655999d124c1159c8a85388184f732 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Sun, 6 Nov 2022 11:16:07 +0000 Subject: [PATCH 2/6] Index endpoint transaction counts at every item. --- src/capture.rs | 6 ++++++ src/decoder.rs | 11 +++++++++++ 2 files changed, 17 insertions(+) diff --git a/src/capture.rs b/src/capture.rs index a3bf48af..7c5fddbd 100644 --- a/src/capture.rs +++ b/src/capture.rs @@ -146,6 +146,7 @@ pub struct EndpointWriter { pub group_index: CompactWriter, pub data_transactions: CompactWriter, pub data_byte_counts: CompactWriter, + pub progress_index: CompactWriter, pub end_index: CompactWriter, } @@ -157,6 +158,7 @@ pub struct EndpointReader { pub group_index: CompactReader, pub data_transactions: CompactReader, pub data_byte_counts: CompactReader, + pub progress_index: CompactReader, pub end_index: CompactReader, } @@ -169,6 +171,7 @@ pub fn create_endpoint() let (groups_writer, groups_reader) = compact_index()?; let (data_transaction_writer, data_transaction_reader) = compact_index()?; let (data_byte_count_writer, data_byte_count_reader) = compact_index()?; + let (progress_writer, progress_reader) = compact_index()?; let (end_writer, end_reader) = compact_index()?; // Create the shared state. @@ -184,6 +187,7 @@ pub fn create_endpoint() group_index: groups_writer, data_transactions: data_transaction_writer, data_byte_counts: data_byte_count_writer, + progress_index: progress_writer, end_index: end_writer, }; @@ -194,6 +198,7 @@ pub fn create_endpoint() group_index: groups_reader, data_transactions: data_transaction_reader, data_byte_counts: data_byte_count_reader, + progress_index: progress_reader, end_index: end_reader, }; @@ -214,6 +219,7 @@ pub type EndpointId = Id; pub type EndpointDataEvent = u64; pub type EndpointByteCount = u64; pub type DeviceVersion = u32; +pub type TrafficItemIdOffset = u64; #[derive(Clone, Debug)] pub enum TrafficItem { diff --git a/src/decoder.rs b/src/decoder.rs index ffc3b2e3..b55739d8 100644 --- a/src/decoder.rs +++ b/src/decoder.rs @@ -1037,6 +1037,17 @@ impl Decoder { let end_id = ep_data.writer.end_index.push(item_id)?; assert!(end_id == ep_group_id); } + if ep_data.writer.shared.first_item_id.load().is_some() { + // Record the total transactions on this endpoint. + let mut transaction_count = + ep_data.writer.transaction_ids.len(); + if start && endpoint_id == item_endpoint_id { + // We just added a transaction, that shouldn't be included. + transaction_count -= 1; + } + ep_data.writer.progress_index.push( + EndpointTransactionId::from(transaction_count))?; + } } Ok(item_id) From 84789ba5c9289647ac33df8650defbcfb3c41dc0 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Tue, 8 Nov 2022 17:59:36 +0000 Subject: [PATCH 3/6] Implement new API calls on Capture needed for interleaving. --- src/capture.rs | 348 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 348 insertions(+) diff --git a/src/capture.rs b/src/capture.rs index 7c5fddbd..90168f22 100644 --- a/src/capture.rs +++ b/src/capture.rs @@ -1369,6 +1369,45 @@ impl CaptureReader { true => Complete, } } + + fn transfer(&mut self, item_index: u64, item: &TrafficItem) + -> Result + { + if let TrafficItem::TransactionGroup(group_id) = item { + let start_item_id = TrafficItemId::from(item_index); + let entry = self.group_index.get(*group_id)?; + let endpoint_id = entry.endpoint_id(); + let ep_traf = self.endpoint_traffic(endpoint_id)?; + let ep_first_item_id = *ep_traf.shared.first_item_id + .load() + .as_ref() + .context(format!("Endpoint ID {endpoint_id} has no first item"))? + .as_ref(); + let transaction_range = + ep_traf.group_index.target_range( + entry.group_id(), + ep_traf.transaction_ids.len())?; + Ok(Transfer { + ep_first_item_id, + start_item_id, + group_id: *group_id, + endpoint_id, + first_ep_transaction_id: transaction_range.start, + transaction_range, + }) + } else { + bail!("Item {item:?} is not a group") + } + } + + fn transfers(&mut self, + expanded: &mut dyn Iterator) + -> Result, Error> + { + expanded + .map(|(index, item)| self.transfer(index, &item)) + .collect() + } } impl EndpointReader { @@ -1413,6 +1452,11 @@ impl CompletionStatus { } } +pub enum SearchResult { + TopLevelItem(u64, Item), + NextLevelItem(u64, u64, u64, Item), +} + pub trait ItemSource { fn item(&mut self, parent: Option<&Item>, @@ -1427,6 +1471,22 @@ pub trait ItemSource { parent: Option<&Item>, view_mode: ViewMode) -> Result<(CompletionStatus, u64), Error>; + fn count_within(&mut self, + item_index: u64, + item: &Item, + region: &Range) + -> Result; + fn count_before(&mut self, + item_index: u64, + item: &Item, + span_index: u64, + child: &Item) + -> Result; + fn find_child(&mut self, + expanded: &mut dyn Iterator, + region: &Range, + index: u64) + -> Result, Error>; fn description(&mut self, item: &Item, detail: bool) @@ -1438,6 +1498,15 @@ pub trait ItemSource { fn timestamp(&mut self, item: &Item) -> Result; } +struct Transfer { + ep_first_item_id: TrafficItemId, + start_item_id: TrafficItemId, + group_id: GroupId, + endpoint_id: EndpointId, + first_ep_transaction_id: EndpointTransactionId, + transaction_range: Range, +} + impl ItemSource for CaptureReader { fn item(&mut self, parent: Option<&TrafficItem>, @@ -1532,6 +1601,257 @@ impl ItemSource for CaptureReader { }) } + fn count_within(&mut self, + item_index: u64, + item: &TrafficItem, + region: &Range) + -> Result + { + // Count the transactions of this transfer item within a region. + let transfer = self.transfer(item_index, item)?; + let ep_traf = self.endpoint_traffic(transfer.endpoint_id)?; + let start_item_id = TrafficItemId::from(region.start); + let end_item_id = TrafficItemId::from(region.end); + let start_offset = start_item_id - transfer.ep_first_item_id; + let end_offset = end_item_id - transfer.ep_first_item_id; + let start_count = ep_traf.progress_index.get(start_offset)?.value; + let end_count = + if end_offset >= ep_traf.progress_index.len() { + ep_traf.transaction_ids.len() + } else { + ep_traf.progress_index.get(end_offset)?.value + }; + Ok(end_count - start_count) + } + + fn count_before(&mut self, + item_index: u64, + item: &TrafficItem, + span_index: u64, + child: &TrafficItem) + -> Result + { + // Count the transactions of this transfer item within a span, + // up to the specified child transaction item. + let transfer = self.transfer(item_index, item)?; + let ep_traf = self.endpoint_traffic(transfer.endpoint_id)?; + let span_item_id = TrafficItemId::from(span_index); + let span_offset = span_item_id - transfer.ep_first_item_id; + let transaction_range = ep_traf.progress_index.target_range( + span_offset, ep_traf.transaction_ids.len())?; + let transaction_count = transaction_range.len(); + if let TrafficItem::Transaction(_, transaction_id) = child { + let expected = transaction_id.value; + for index in 0..transaction_count { + let ep_transaction_id = transaction_range.start + index; + let id = ep_traf.transaction_ids.get(ep_transaction_id)?; + if id.value >= expected { + return Ok(index) + } + } + Ok(transaction_count) + } else { + bail!("Child {child:?} is not a transaction") + } + } + + fn find_child(&mut self, + expanded: &mut dyn Iterator, + region: &Range, + mut index: u64) + -> Result, Error> + { + use SearchResult::*; + use TrafficItem::*; + + // Collect data on the expanded transfers. + let mut transfers = self.transfers(expanded)?; + assert!(!transfers.is_empty()); + + // First, find the right span: the space between two contiguous items + // in which this transaction is to be found. + let mut total_transactions = 0; + let mut span_index = region.start; + for i in 0..region.len() { + span_index = region.start + i; + let span_item_id = TrafficItemId::from(span_index); + // Count the transactions within this span. + for transfer in transfers.iter_mut() { + let ep_traf = self.endpoint_traffic(transfer.endpoint_id)?; + // Find the transaction counts for this transfer at the + // beginning and end of this span. + let item_offset = span_item_id - transfer.ep_first_item_id; + transfer.transaction_range = + ep_traf.progress_index.target_range( + item_offset, ep_traf.transaction_ids.len())?; + // Add to the total count for this span. + total_transactions += transfer.transaction_range.len(); + } + // If the index is within this span, proceed to the next stage. + if index < total_transactions { + break; + // Otherwise, advance to the end of this span. + } else { + index -= total_transactions; + total_transactions = 0; + } + // We are now at the end of a span. If the index is now zero, + // return the transfer item after this span. + if index == 0 { + let item_id = span_item_id + 1; + let group_id = self.item_index.get(item_id)?; + let item = TransactionGroup(group_id); + return Ok(TopLevelItem(item_id.value, item)) + // Otherwise, skip over the transfer item. + } else { + index -= 1; + } + } + + // Check the index is within the span found by the loop above. This + // will fail if the index was past the end of this region's rows. + if index >= total_transactions { + bail!("Index {index} is beyond the \ + {total_transactions} transactions in this span"); + } + + // Now we have identified the correct span. Find the transaction with + // the remaining index from among the active transfers. + loop { + // Exclude transfers with no remaining transactions. + transfers.retain(|transfer| + !transfer.transaction_range.is_empty()); + + // If only one remains, look up directly. + if transfers.len() == 1 { + let transfer = &transfers[0]; + let ep_traf = self.endpoint_traffic(transfer.endpoint_id)?; + // Get the next transaction ID for this transfer. + let ep_transaction_id = + transfer.transaction_range.start + index; + let transaction_id = + ep_traf.transaction_ids.get(ep_transaction_id)?; + let parent_index = transfer.start_item_id.value; + let child_index = + ep_transaction_id - transfer.first_ep_transaction_id; + let item = Transaction( + Some(transfer.group_id), transaction_id); + return Ok(NextLevelItem( + span_index, parent_index, child_index, item)) + } + + // Exclude transactions that cannot possibly match the index. + for transfer in transfers.iter_mut() { + let range = &transfer.transaction_range; + if range.len() > index + 1 { + transfer.transaction_range.end = + range.start + index + 1; + } + } + + // Choose the transfer with the most transactions. + let (longest, longest_length) = transfers + .iter() + .enumerate() + .map(|(i, transfer)| (i, transfer.transaction_range.len())) + .max_by_key(|(_, length)| *length) + .context("No transfers remaining")?; + + // If there are no transfers with more than 1 transaction, + // proceed to selecting from the remaining candidates. + if longest_length < 2 { + break + } + + // Identify the midpoint of the longest transfer. + let midpoint_offset = longest_length / 2; + + // Get the transaction ID at the midpoint, as a pivot. + let ep_traf = + self.endpoint_traffic(transfers[longest].endpoint_id)?; + let ep_transaction_id = + transfers[longest].transaction_range.start + midpoint_offset; + let pivot_transaction_id = + ep_traf.transaction_ids.get(ep_transaction_id)?; + + // Find the offset of the pivot within each transfer. + let mut offsets = Vec::with_capacity(transfers.len()); + for transfer in transfers.iter() { + offsets.push( + if std::ptr::eq(transfer, &transfers[longest]) { + midpoint_offset + } else { + let ep_traf = + self.endpoint_traffic(transfer.endpoint_id)?; + let position = + ep_traf.transaction_ids.bisect_range_left( + &transfer.transaction_range, + &pivot_transaction_id)?; + position - transfer.transaction_range.start + } + ); + } + + // Count the total transactions before the pivot. + let count = offsets.iter().sum::(); + + use std::cmp::Ordering::*; + match index.cmp(&count) { + Equal => { + // If the index equals the count, return the pivot. + let parent_index = + transfers[longest].start_item_id.value; + let child_index = ep_transaction_id - + transfers[longest].first_ep_transaction_id; + let item = Transaction( + Some(transfers[longest].group_id), + pivot_transaction_id); + return Ok(NextLevelItem( + span_index, parent_index, child_index, item)); + }, + Less => { + // If the index is less than the count, split the ranges + // and discard the upper ends. + for (transfer, offset) in transfers.iter_mut().zip(offsets) { + transfer.transaction_range.end = + transfer.transaction_range.start + offset; + } + } + Greater => { + // If the index is greater than the count, split the ranges + // and discard the lower ends. + for (transfer, offset) in transfers.iter_mut().zip(offsets) { + transfer.transaction_range.start += offset; + } + // Reduce the index by the count of excluded transactions. + index -= count; + } + } + } + + // There is now at most one transaction in each transfer. Retrieve each + // and find the one with the lowest transaction ID. + let mut results = Vec::with_capacity(transfers.len()); + for transfer in transfers.iter() { + if !transfer.transaction_range.is_empty() { + let ep_traf = self.endpoint_traffic(transfer.endpoint_id)?; + let transaction_id = + ep_traf.transaction_ids.get( + transfer.transaction_range.start)?; + results.push((transfer, transaction_id)); + } + } + results.sort_by_key(|(_, id)| id.value); + let (transfer, transaction_id) = results + .get(index as usize) + .context("Index not found")?; + let parent_index = transfer.start_item_id.value; + let child_index = transfer.transaction_range.start - + transfer.first_ep_transaction_id; + let item = Transaction(Some(transfer.group_id), *transaction_id); + Ok(NextLevelItem(span_index, parent_index, child_index, item)) + } + fn description(&mut self, item: &TrafficItem, detail: bool) -> Result { @@ -2193,6 +2513,34 @@ impl ItemSource for CaptureReader { Ok((completion, children as u64)) } + fn count_within(&mut self, + _item_index: u64, + _item: &DeviceItem, + _region: &Range) + -> Result + { + unimplemented!() + } + + fn count_before(&mut self, + _item_index: u64, + _item: &DeviceItem, + _span_index: u64, + _child: &DeviceItem) + -> Result + { + unimplemented!() + } + + fn find_child(&mut self, + _expanded: &mut dyn Iterator, + _region: &Range, + _index: u64) + -> Result, Error> + { + unimplemented!() + } + fn description(&mut self, item: &DeviceItem, _detail: bool) -> Result { From 8f479020c5debd26f97dea26ecaedd38be798a96 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Wed, 18 Jan 2023 17:42:11 +0000 Subject: [PATCH 4/6] Add interleaving support to tree model. This commit adds support for interleaved regions and nodes to the tree model, but does not use it yet. This commit can be tested to verify that the changes do not impact the non-interleaved case. --- src/capture.rs | 16 +- src/tree_list_model.rs | 1462 +++++++++++++++++++++++++++++++++++++--- 2 files changed, 1380 insertions(+), 98 deletions(-) diff --git a/src/capture.rs b/src/capture.rs index 90168f22..1db1a286 100644 --- a/src/capture.rs +++ b/src/capture.rs @@ -1439,15 +1439,25 @@ impl EndpointReader { #[derive(Copy, Clone)] pub enum CompletionStatus { Complete, - Ongoing + Ongoing, + InterleavedComplete(u64), + InterleavedOngoing, } impl CompletionStatus { pub fn is_complete(&self) -> bool { use CompletionStatus::*; match self { - Complete => true, - Ongoing => false, + Complete | InterleavedComplete(_) => true, + Ongoing | InterleavedOngoing => false, + } + } + + pub fn is_interleaved(&self) -> bool { + use CompletionStatus::*; + match self { + InterleavedComplete(_) | InterleavedOngoing => true, + Complete | Ongoing => false, } } } diff --git a/src/tree_list_model.rs b/src/tree_list_model.rs index 63b8d79f..123bebcb 100644 --- a/src/tree_list_model.rs +++ b/src/tree_list_model.rs @@ -5,6 +5,7 @@ use std::collections::btree_map::Entry; use std::fmt::Debug; use std::marker::PhantomData; use std::rc::{Rc, Weak}; +use std::ops::Range; use anyhow::{Context, Error, bail}; @@ -15,7 +16,12 @@ use gtk::gio::prelude::ListModelExt; use derive_more::AddAssign; use itertools::Itertools; -use crate::capture::{CaptureReader, ItemSource}; +use crate::capture::{ + CaptureReader, + CompletionStatus, + ItemSource, + SearchResult +}; use crate::model::GenericModel; use crate::row_data::GenericRowData; use crate::item_widget::ItemWidget; @@ -41,8 +47,8 @@ trait Node { /// Whether the children of this node are displayed. fn expanded(&self) -> bool; - /// Mark this node as completed. - fn set_completed(&mut self); + /// Update this node's completion status. + fn set_completion(&mut self, completion: CompletionStatus); } struct Children { @@ -88,6 +94,9 @@ pub struct ItemNode { /// Index of this node below the parent Item. item_index: u64, + /// Completion status of this node. + completion: CompletionStatus, + /// Children of this item. children: Children, @@ -101,6 +110,11 @@ impl Children { self.expanded.contains_key(&index) } + /// Get the expanded child with the given index. + fn get_expanded(&self, index: u64) -> Option> { + self.expanded.get(&index).map(Rc::clone) + } + /// Set whether this child of the owning node is expanded. fn set_expanded(&mut self, child_rc: &ItemNodeRc, expanded: bool) { let child = child_rc.borrow(); @@ -152,8 +166,8 @@ impl Node for RootNode { true } - fn set_completed(&mut self) { - self.complete = true; + fn set_completion(&mut self, completion: CompletionStatus) { + self.complete = completion.is_complete(); } } @@ -188,14 +202,17 @@ impl Node for ItemNode where Item: Clone { } } - fn set_completed(&mut self) { - if let Some(parent_rc) = self.parent.upgrade() { - parent_rc - .borrow_mut() - .children_mut() - .incomplete - .remove(&self.item_index); + fn set_completion(&mut self, completion: CompletionStatus) { + if completion.is_complete() { + if let Some(parent_rc) = self.parent.upgrade() { + parent_rc + .borrow_mut() + .children_mut() + .incomplete + .remove(&self.item_index); + } } + self.completion = completion; } } @@ -274,10 +291,87 @@ impl ItemNode where Item: Clone { } } +#[derive(Clone)] +struct ItemNodeSet { + items: BTreeMap>, +} + +impl ItemNodeSet where Item: Clone { + fn new(node_rc: &ItemNodeRc) -> Self { + let mut new = Self { + items: BTreeMap::new() + }; + new.add(node_rc); + new + } + + fn add(&mut self, node_rc: &ItemNodeRc) { + let index = node_rc.borrow().item_index; + self.items.insert(index, node_rc.clone()); + } + + fn remove(&mut self, node_rc: &ItemNodeRc) { + let index = node_rc.borrow().item_index; + self.items.remove(&index); + } + + fn with(&self, node_rc: &ItemNodeRc) -> Self { + let mut new = self.clone(); + new.add(node_rc); + new + } + + fn without(&self, node_rc: &ItemNodeRc) -> Self { + let mut new = self.clone(); + new.remove(node_rc); + new + } + + fn any_incomplete(&self) -> bool { + self.items + .values() + .any(|node_rc| !node_rc.borrow().completion.is_complete()) + } + + fn is_empty(&self) -> bool { + self.items.is_empty() + } + + fn iter_items(&self) -> impl Iterator + '_ { + self.items + .iter() + .map(|(index, node_rc)| (*index, node_rc.borrow().item.clone())) + } +} + +impl PartialEq for ItemNodeSet { + fn eq(&self, other: &Self) -> bool { + self.items.len() == other.items.len() && + self.items.values() + .zip(other.items.values()) + .all(|(a, b)| Rc::ptr_eq(a, b)) + } +} + +impl Debug for ItemNodeSet +where Item: Clone + Debug +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) + -> Result<(), std::fmt::Error> + { + write!(f, "{:?}", + self.items + .values() + .map(|rc| rc.borrow().item.clone()) + .collect::>()) + } +} + #[derive(Clone)] enum Source { TopLevelItems(), ChildrenOf(ItemNodeRc), + InterleavedSearch(ItemNodeSet, Range), } use Source::*; @@ -301,6 +395,8 @@ where Item: Clone + Debug write!(f, "Top level items"), ChildrenOf(rc) => write!(f, "Children of {:?}", rc.borrow().item), + InterleavedSearch(expanded, range) => + write!(f, "Interleaved search in {range:?} from {expanded:?}"), }?; write!(f, ", offset {}, length {}", self.offset, self.length) } @@ -312,6 +408,16 @@ impl Region where Item: Clone { region_b: &Region ) -> Option> { match (®ion_a.source, ®ion_b.source) { + (InterleavedSearch(exp_a, range_a), + InterleavedSearch(exp_b, range_b)) if exp_a == exp_b => Some( + Region { + source: InterleavedSearch( + exp_a.clone(), + range_a.start..range_b.end), + offset: region_a.offset, + length: region_a.length + region_b.length, + } + ), (ChildrenOf(a_ref), ChildrenOf(b_ref)) if Rc::ptr_eq(a_ref, b_ref) => Some( Region { @@ -346,6 +452,17 @@ impl std::fmt::Display for ModelUpdate { } } +enum InterleavedUpdate { + AddedOngoing(ItemNodeRc, u64), + AddedComplete(ItemNodeRc, u64, u64), +} + +enum UpdateState { + Root(Vec>), + Interleaved(Option>), + Contiguous(), +} + pub struct TreeListModel { _marker: PhantomData<(Model, RowData)>, capture: RefCell, @@ -389,6 +506,10 @@ where Item: 'static + Clone + Debug, self.root.borrow().children().total_count } + fn item_count(&self) -> u64 { + self.root.borrow().children().direct_count + } + fn check(&self) -> Result<(), Error> { // Check that we have the expected number of rows in the region map. let expected_count = self.row_count(); @@ -399,8 +520,8 @@ where Item: 'static + Clone + Debug, .map(|(start, region)| start + region.length) .unwrap_or(0); if expected_count != actual_count { - bail!("Region map total row count is {}, expected {}", - actual_count, expected_count) + bail!("Region map total row count is {actual_count}, \ + expected {expected_count}") } else { Ok(()) } @@ -425,6 +546,7 @@ where Item: 'static + Clone + Debug, let rows_affected = node.children.direct_count; let expanded_children = node.children.expanded.clone(); + let interleaved = node.completion.is_interleaved(); // There cannot be any visible incomplete children at this point. node.children.incomplete.clear(); @@ -442,13 +564,39 @@ where Item: 'static + Clone + Debug, } else { #[cfg(feature="debug-region-map")] println!("\nCollapsing node at {}", position); - // If collapsing, first recursively collapse children of this node. - for (index, child_ref) in expanded_children { - let child_position = children_position + index; - #[cfg(feature="debug-region-map")] - println!("\nRecursively collapsing child at {}", - child_position); - self.set_expanded(model, &child_ref, child_position, false)?; + if interleaved { + // Collapse expanded children of this node, from last to first. + let mut end_position = self.row_count(); + for child_ref in expanded_children.into_values().rev() { + // Since this node's children are interleaved, we find each + // expanded child's position by searching the region map + // in reverse order, for the first region containing the + // expanded child's own children. The search window extends + // from the parent node to either the end of the region map, + // or the previous collapsed child, shrinking each time. + let child_position = + self.find_expanded(position..end_position, &child_ref)?; + // Update end position for next search. + end_position = child_position; + // Collapse this child. + #[cfg(feature="debug-region-map")] + println!("\nRecursively collapsing child at {}", + child_position); + self.set_expanded( + model, &child_ref, child_position, false)?; + } + } else { + // If this node's children are not interleaved, each child's + // position is at a simple offset, provided we collapse them + // from first to last. + for (index, child_ref) in expanded_children { + let child_position = children_position + index; + #[cfg(feature="debug-region-map")] + println!("\nRecursively collapsing child at {}", + child_position); + self.set_expanded( + model, &child_ref, child_position, false)?; + } } // Update the region map for the removed children. self.collapse(children_position, node_ref)? @@ -484,9 +632,67 @@ where Item: 'static + Clone + Debug, Ok(()) } + fn find_expanded(&self, range: Range, node_rc: &ItemNodeRc) + -> Result + { + self.regions + .borrow() + .range(range) + .rev() + .find_map(|(start, region)| { + match (region.offset, ®ion.source) { + (0, ChildrenOf(rc)) + if Rc::ptr_eq(rc, node_rc) + // The node appears on the row before + // its first children. + => Some(*start - 1), + _ => None + } + }) + .context("No region found for expanded child node") + } + + fn find_top_level_item(&self, node_rc: &ItemNodeRc) + -> Result + { + let index = node_rc.borrow().item_index; + for (start, region) in self.regions.borrow().iter() { + match ®ion.source { + TopLevelItems() if + region.offset <= index && + region.length > index - region.offset => + { + return Ok(start + index - region.offset); + }, + InterleavedSearch(expanded, range) if + range.start < index && + range.end > index => + { + let count_range = range.start..index; + let position = + self.count_items(&count_range)? + + self.count_within(expanded, &count_range)?; + if position >= region.offset && + position < region.offset + region.length + { + return Ok(start + position - region.offset) + } + } + _ => {} + } + } + bail!("Item not found") + } + fn expand(&self, position: u64, node_ref: &ItemNodeRc) -> Result { + // Extract some details of the node being expanded. + let node = node_ref.borrow(); + let node_start = node.item_index; + let next_item = node_start + 1; + let interleaved = node.completion.is_interleaved(); + // Find the start of the parent region. let (&parent_start, _) = self.regions .borrow() @@ -506,29 +712,179 @@ where Item: 'static + Clone + Debug, "Parent not found at position {parent_start}"))?; // Remove all following regions, to iterate over later. - let following_regions = self.regions + let mut following_regions = self.regions .borrow_mut() - .split_off(&parent_start) - .into_iter(); + .split_off(&parent_start); + + let more_after = !following_regions.is_empty(); // Split the parent region and construct a new region between. - let update = self.split_parent(parent_start, &parent, node_ref, - vec![Region { - source: parent.source.clone(), - offset: parent.offset, - length: relative_position, - }], - Region { - source: ChildrenOf(node_ref.clone()), - offset: 0, - length: node_ref.borrow().children.direct_count, + // + // Where the new region is an interleaved one, its overlap with the + // remainder of the parent is handled in the split_parent call. + let mut update = match (interleaved, &parent.source) { + // Self-contained region expanded. + (false, _) => { + self.split_parent(parent_start, &parent, node_ref, more_after, + vec![Region { + source: parent.source.clone(), + offset: parent.offset, + length: relative_position, + }], + Region { + source: ChildrenOf(node_ref.clone()), + offset: 0, + length: node.children.direct_count, + }, + vec![Region { + source: parent.source.clone(), + offset: parent.offset + relative_position, + length: parent.length - relative_position, + }] + )? }, - vec![Region { - source: parent.source.clone(), - offset: parent.offset + relative_position, - length: parent.length - relative_position, - }] - )?; + // Interleaved region expanded from within a root region. + (true, TopLevelItems()) => { + let expanded = ItemNodeSet::new(node_ref); + let range = node_start..next_item; + let added = self.count_within(&expanded, &range)?; + if parent.length == 1 && + parent_start + parent.length != self.row_count() + { + // There's nothing to split, and there must already be an + // interleaved region after this, so keep the parent as is. + let mut update = ModelUpdate::default(); + self.preserve_region( + &mut update, parent_start, &parent, false)?; + update + } else { + self.split_parent( + parent_start, &parent, node_ref, more_after, + vec![Region { + source: TopLevelItems(), + offset: parent.offset, + length: relative_position, + }], + Region { + source: InterleavedSearch(expanded, range), + offset: 0, + length: added, + }, + vec![Region { + source: TopLevelItems(), + offset: parent.offset + relative_position, + length: parent.length - relative_position, + }] + )? + } + }, + // New interleaved region expanded from within an existing one. + (true, InterleavedSearch(parent_expanded, parent_range)) => { + let range_1 = parent_range.start..node_start; + let range_2 = node_start..next_item; + let range_3 = next_item..parent_range.end; + let expanded_1 = parent_expanded.clone(); + let expanded_2 = parent_expanded.with(node_ref); + let expanded_3 = parent_expanded.clone(); + let total_changed = self.count_within(parent_expanded, &range_2)?; + let rows_present = parent.length - relative_position; + let (changed, added) = if rows_present >= total_changed { + let changed = total_changed; + let added = self.count_in_range(&range_2, node_ref)?; + (changed, added) + } else { + let changed = rows_present; + let added = self.count_to_item( + parent_expanded, &range_2, rows_present - 1, node_ref)?; + (changed, added) + }; + if parent.offset != 0 { + // Update the range end of previous parts of this parent. + for region in self.regions.borrow_mut().values_mut().rev() { + match &mut region.source { + TopLevelItems() => break, + ChildrenOf(..) => continue, + InterleavedSearch(_, range) + if range.end > node_start => + { + range.end = node_start; + }, + InterleavedSearch(..) => break + } + } + } + if total_changed > rows_present { + // Update the range start of following parts of this parent. + for region in following_regions.values_mut() { + match &mut region.source { + TopLevelItems() => break, + ChildrenOf(..) => continue, + InterleavedSearch(_, range) + if range.start < node_start => + { + range.start = node_start; + region.offset -= relative_position; + }, + InterleavedSearch(..) => break + } + } + } + self.split_parent(parent_start, &parent, node_ref, more_after, + vec![ + Region { + source: InterleavedSearch(expanded_1, range_1), + offset: parent.offset, + length: relative_position - 1, + }, + Region { + source: TopLevelItems(), + offset: node_start, + length: 1, + } + ], + Region { + source: InterleavedSearch(expanded_2, range_2), + offset: 0, + length: changed + added, + }, + if rows_present > changed { + vec![ + Region { + source: TopLevelItems(), + offset: next_item, + length: 1, + }, + Region { + source: InterleavedSearch(expanded_3, range_3), + offset: 0, + length: rows_present - changed - 1, + } + ] + } else { + vec![] + } + )? + }, + // Other combinations are not supported. + (..) => bail!("Unable to expand from {parent:?}") + }; + + drop(node); + + // For an interleaved source, update all regions that it overlaps. + let mut following_regions = following_regions.into_iter(); + if interleaved { + while let Some((start, region)) = following_regions.next() { + let more_after = following_regions.len() > 0; + // Do whatever is necessary to overlap this region. + if !self.overlap_region( + &mut update, start, ®ion, node_ref, more_after)? + { + // No further overlapping regions. + break; + } + } + } // Shift all remaining regions down by the added rows. for (start, region) in following_regions { @@ -571,6 +927,20 @@ where Item: 'static + Clone + Debug, rows_removed: node_ref.borrow().children.direct_count, rows_changed: 0, } + }, + // For an interleaved source, update all overlapped regions. + InterleavedSearch(..) => { + let mut update = ModelUpdate::default(); + for (start, region) in following_regions.by_ref() { + // Do whatever is necessary to unoverlap this region. + if !self.unoverlap_region( + &mut update, start, ®ion, node_ref)? + { + // No further overlapping regions. + break; + } + } + update } }; @@ -582,6 +952,295 @@ where Item: 'static + Clone + Debug, Ok(update) } + fn overlap_region(&self, + update: &mut ModelUpdate, + start: u64, + region: &Region, + node_ref: &ItemNodeRc, + more_after: bool) + -> Result + { + use Source::*; + + let node_range = self.node_range(node_ref); + + Ok(match ®ion.source { + TopLevelItems() if region.offset >= node_range.end => { + // This region is not overlapped. + self.preserve_region(update, start, region, false)? + }, + InterleavedSearch(_, range) if range.start >= node_range.end => { + // This region is not overlapped. + self.preserve_region(update, start, region, false)? + }, + ChildrenOf(_) => { + // This region is overlapped but self-contained. + self.preserve_region(update, start, region, true)? + }, + TopLevelItems() if region.length == 1 => { + // This region includes only a single root item. Check + // whether it is overlapped by the new node. + let overlapped = region.offset < node_range.end; + // Either way, the region itself is unchanged. + self.preserve_region(update, start, region, overlapped)?; + // We may need to add a new interleaved region after this item + // if it is overlapped, is the last item, and there is not + // already an interleaved region to follow it. + let item_count = self.item_count(); + let last_item = region.offset + 1 == item_count; + if overlapped && last_item && !more_after { + let range = region.offset..item_count; + let added = self.count_in_range(&range, node_ref)?; + let expanded = ItemNodeSet::new(node_ref); + let trailing_region = Region { + source: InterleavedSearch(expanded, range), + offset: 0, + length: added, + }; + #[cfg(feature="debug-region-map")] { + println!(); + println!("Inserting: {:?}", trailing_region); + } + self.insert_region( + start + region.length + update.rows_added, + trailing_region + )?; + update.rows_added += added; + } + overlapped + }, + TopLevelItems() + if region.offset + region.length <= node_range.end => + { + // This region is fully overlapped by the new node. + // Replace with a new interleaved region. + let end = region.offset + region.length; + let contains_last_item = end == self.item_count(); + let expanded = ItemNodeSet::new(node_ref); + if contains_last_item && !more_after { + // This region contains the last item, and there is not + // currently a following interleaved region. Create one + // that runs to the end of the model. + let range = region.offset..end; + let added = self.count_in_range(&range, node_ref)?; + self.replace_region(update, start, region, + vec![ + Region { + source: TopLevelItems(), + offset: region.offset, + length: 1, + }, + Region { + source: InterleavedSearch(expanded, range), + offset: 0, + length: region.length - 1 + added, + } + ], + )?; + false + } else { + // There are further regions after this one, so just + // overlap up to the end of these top level items. + let range = region.offset..(end - 1); + let added = self.count_in_range(&range, node_ref)?; + self.replace_region(update, start, region, + vec![ + Region { + source: TopLevelItems(), + offset: region.offset, + length: 1, + }, + Region { + source: InterleavedSearch(expanded, range), + offset: 0, + length: region.length - 2 + added, + }, + Region { + source: TopLevelItems(), + offset: region.offset + region.length - 1, + length: 1 + } + ] + )?; + true + } + }, + TopLevelItems() => { + // This region is partially overlapped by the new node. + // Split it into overlapped and unoverlapped parts. + let expanded = ItemNodeSet::new(node_ref); + let range = region.offset..node_range.end; + let changed = self.count_items(&range)?; + let added = self.count_in_range(&range, node_ref)?; + self.partial_overlap(update, start, region, + vec![ + Region { + source: TopLevelItems(), + offset: region.offset, + length: 1, + }, + Region { + source: InterleavedSearch(expanded, range), + offset: 0, + length: changed + added + } + ], + vec![ + Region { + source: TopLevelItems(), + offset: region.offset + changed + 1, + length: region.length - changed - 1, + } + ] + )?; + // No longer overlapping. + false + }, + InterleavedSearch(expanded, range) + if range.end <= node_range.end => + { + // This region is fully overlapped by the new node. + // Replace with a new interleaved region. + let (added_before_offset, added_after_offset) = + self.count_around_offset( + expanded, range, node_ref, + region.offset, + region.offset + region.length)?; + let range = range.clone(); + let more_expanded = expanded.with(node_ref); + self.replace_region(update, start, region, + vec![ + Region { + source: InterleavedSearch(more_expanded, range), + offset: region.offset + added_before_offset, + length: region.length + added_after_offset, + } + ] + )?; + true + }, + InterleavedSearch(expanded, range) => { + // This region may be partially overlapped by the new node, + // depending on its starting offset and length. + let range_1 = range.start..node_range.end; + let range_2 = node_range.end..range.end; + // Work out the offset at which this source would be split. + let split_offset = self.count_items(&range_1)? + + self.count_within(expanded, &range_1)?; + if region.offset >= split_offset { + // This region begins after the split, so isn't overlapped. + self.preserve_region(update, start, region, false)? + } else if region.offset + region.length <= split_offset { + // This region ends before the split, so is fully overlapped. + let (added_before_offset, added_after_offset) = + self.count_around_offset( + expanded, range, node_ref, + region.offset, + region.offset + region.length)?; + let range = range.clone(); + let more_expanded = expanded.with(node_ref); + self.replace_region(update, start, region, + vec![ + Region { + source: InterleavedSearch(more_expanded, range), + offset: region.offset + added_before_offset, + length: region.length + added_after_offset, + } + ] + )?; + true + } else { + // This region straddles the split. Break it into overlapped + // and unoverlapped parts. + let changed = split_offset - region.offset; + let (added_before_offset, added_after_offset) = + self.count_around_offset( + expanded, range, node_ref, + region.offset, split_offset)?; + let expanded_1 = expanded.with(node_ref); + let expanded_2 = expanded.clone(); + self.partial_overlap(update, start, region, + vec![ + Region { + source: InterleavedSearch(expanded_1, range_1), + offset: region.offset + added_before_offset, + length: changed + added_after_offset, + } + ], + vec![ + Region { + source: TopLevelItems(), + offset: node_range.end, + length: 1, + }, + Region { + source: InterleavedSearch(expanded_2, range_2), + offset: 0, + length: region.length - changed - 1, + } + ] + )?; + // No longer overlapping. + false + } + } + }) + } + + fn unoverlap_region(&self, + update: &mut ModelUpdate, + start: u64, + region: &Region, + node_ref: &ItemNodeRc) + -> Result + { + use Source::*; + + let node_range = self.node_range(node_ref); + + Ok(match ®ion.source { + TopLevelItems() if region.offset >= node_range.end => { + // This region is not overlapped. + self.preserve_region(update, start, region, false)? + }, + InterleavedSearch(_, range) if range.start >= node_range.end => { + // This region is not overlapped. + self.preserve_region(update, start, region, false)? + }, + ChildrenOf(_) | TopLevelItems() => { + // This region is overlapped but self-contained. + self.preserve_region(update, start, region, true)? + }, + InterleavedSearch(expanded, range) => { + // This region is overlapped. Replace with a new one. + let less_expanded = expanded.without(node_ref); + let new_region = if less_expanded.is_empty() { + // This node was the last expanded one in this region. + Region { + source: TopLevelItems(), + offset: range.start + 1, + length: self.count_items(range)?, + } + } else { + // There are other nodes expanded in this region. + let (removed_before_offset, removed_after_offset) = + self.count_around_offset( + expanded, range, node_ref, + region.offset, + region.offset + region.length)?; + let range = range.clone(); + Region { + source: InterleavedSearch(less_expanded, range), + offset: region.offset - removed_before_offset, + length: region.length - removed_after_offset, + } + }; + self.replace_region(update, start, region, vec![new_region])?; + true + } + }) + } + fn insert_region(&self, position: u64, region: Region) -> Result<(), Error> { @@ -602,13 +1261,158 @@ where Item: 'static + Clone + Debug, } } + fn preserve_region(&self, + update: &mut ModelUpdate, + start: u64, + region: &Region, + include_as_changed: bool) + -> Result + { + let new_position = start + + update.rows_added + - update.rows_removed; + + self.insert_region(new_position, region.clone())?; + + if include_as_changed { + update.rows_changed += region.length; + } + + Ok(include_as_changed) + } + + fn replace_region(&self, + update: &mut ModelUpdate, + start: u64, + region: &Region, + new_regions: Vec>) + -> Result<(), Error> + { + use std::cmp::Ordering::*; + + let new_length: u64 = new_regions + .iter() + .map(|region| region.length) + .sum(); + + let effect = match new_length.cmp(®ion.length) { + Greater => ModelUpdate { + rows_added: new_length - region.length, + rows_removed: 0, + rows_changed: region.length + }, + Less => ModelUpdate { + rows_added: 0, + rows_removed: region.length - new_length, + rows_changed: new_length + }, + Equal => ModelUpdate { + rows_added: 0, + rows_removed: 0, + rows_changed: region.length + }, + }; + + #[cfg(feature="debug-region-map")] + { + println!(); + println!("Replacing: {:?}", region); + for (i, new_region) in new_regions.iter().enumerate() { + if i == 0 { + println!(" with: {:?}", new_region); + } else { + println!(" {:?}", new_region); + } + } + println!(" {}", effect); + } + + let mut position = start + + update.rows_added + - update.rows_removed; + + for region in new_regions { + let length = region.length; + self.insert_region(position, region)?; + position += length; + } + + *update += effect; + + Ok(()) + } + + fn partial_overlap(&self, + update: &mut ModelUpdate, + start: u64, + region: &Region, + changed_regions: Vec>, + unchanged_regions: Vec>) + -> Result<(), Error> + { + let changed_length: u64 = changed_regions + .iter() + .map(|region| region.length) + .sum(); + + let unchanged_length: u64 = unchanged_regions + .iter() + .map(|region| region.length) + .sum(); + + let total_length = changed_length + unchanged_length; + + let effect = ModelUpdate { + rows_added: total_length - region.length, + rows_removed: 0, + rows_changed: region.length - unchanged_length, + }; + + #[cfg(feature="debug-region-map")] + { + println!(); + println!("Splitting: {:?}", region); + for (i, changed_region) in changed_regions.iter().enumerate() { + if i == 0 { + println!(" changed: {:?}", changed_region); + } else { + println!(" : {:?}", changed_region); + } + } + for (i, unchanged_region) in unchanged_regions.iter().enumerate() { + if i == 0 { + println!("unchanged: {:?}", unchanged_region); + } else { + println!(" : {:?}", unchanged_region); + } + } + println!(" {}", effect); + } + + let mut position = start + update.rows_added - update.rows_removed; + for region in changed_regions + .into_iter() + .chain(unchanged_regions) + { + let length = region.length; + self.insert_region(position, region)?; + position += length; + } + + *update += effect; + + Ok(()) + } + + #[allow(clippy::too_many_arguments)] fn split_parent(&self, parent_start: u64, parent: &Region, - _node_ref: &ItemNodeRc, + node_ref: &ItemNodeRc, + more_after: bool, parts_before: Vec>, new_region: Region, - parts_after: Vec>) + mut parts_after: Vec>) -> Result { let length_before: u64 = parts_before @@ -626,7 +1430,7 @@ where Item: 'static + Clone + Debug, let rows_added = total_length - parent.length; let rows_changed = parent.length - length_before - length_after; - let update = ModelUpdate { + let mut update = ModelUpdate { rows_added, rows_removed: 0, rows_changed, @@ -657,6 +1461,7 @@ where Item: 'static + Clone + Debug, println!(" {}", &update); } + let interleaved = matches!(&new_region.source, InterleavedSearch(..)); let new_position = parent_start + length_before; let position_after = new_position + new_region.length; @@ -670,19 +1475,32 @@ where Item: 'static + Clone + Debug, self.insert_region(new_position, new_region)?; position = position_after; - for region in parts_after - .into_iter() - .filter(|region| region.length > 0) - { + + parts_after.retain(|region| region.length > 0); + let mut add_after = parts_after.into_iter(); + let mut overlap = interleaved; + while let Some(region) = add_after.next() { let length = region.length; - self.insert_region(position, region)?; + if overlap { + let more_after = more_after || add_after.len() > 0; + overlap = self.overlap_region(&mut update, + position - rows_added, + ®ion, + node_ref, + more_after)?; + } else { + self.insert_region(position, region)?; + } position += length; } Ok(update) } - fn merge_regions(&self) { + fn pre_merge(&self, printed: &mut bool) { + if *printed { + return + } #[cfg(feature="debug-region-map")] { println!(); println!("Before merge:"); @@ -690,14 +1508,18 @@ where Item: 'static + Clone + Debug, println!("{}: {:?}", start, region); } } + *printed = true; + } + fn merge_pairs(&self, printed: &mut bool) { let new_regions = self.regions - .borrow_mut() - .split_off(&0) + .borrow() + .clone() .into_iter() .coalesce(|(start_a, region_a), (start_b, region_b)| match Region::merge(®ion_a, ®ion_b) { Some(region_c) => { + self.pre_merge(printed); #[cfg(feature="debug-region-map")] { println!(); println!("Merging: {:?}", region_a); @@ -713,6 +1535,145 @@ where Item: 'static + Clone + Debug, self.regions.replace(new_regions); } + fn merge_regions(&self) { + let mut printed = false; + + // Merge adjacent regions with the same source. + self.merge_pairs(&mut printed); + + // Find starts and lengths of superfluous root regions. + let superfluous_regions: Vec<(u64, u64)> = self.regions + .borrow() + .iter() + .tuple_windows() + .filter_map(|((_, a), (b_start, b), (_, c))| + match (&a.source, &b.source, &c.source) { + (InterleavedSearch(exp_a, _), + TopLevelItems(), + InterleavedSearch(exp_c, _)) if exp_a == exp_c => { + self.pre_merge(&mut printed); + #[cfg(feature="debug-region-map")] + { + println!(); + println!("Dropping: {:?}", b); + } + Some((*b_start, b.length)) + }, + _ => None + }) + .collect(); + + // Remove superfluous regions. + for (start, length) in superfluous_regions { + let mut regions = self.regions.borrow_mut(); + regions.remove(&start); + let (_, prev_region) = regions + .range_mut(..start) + .next_back() + .unwrap(); + prev_region.length += length; + } + + // Once again merge adjacent regions with the same source. + self.merge_pairs(&mut printed); + } + + fn node_range(&self, node_ref: &ItemNodeRc) -> Range { + use CompletionStatus::*; + let node = node_ref.borrow(); + let start = node.item_index; + let end = match node.completion { + InterleavedComplete(index) => index, + InterleavedOngoing => self.item_count(), + _ => start, + }; + start..end + } + + fn count_items(&self, range: &Range) -> Result { + let length = range.end - range.start; + if length == 0 { + bail!("Range is empty") + } else { + Ok(length - 1) + } + } + + fn count_to_item(&self, + expanded: &ItemNodeSet, + range: &Range, + to_index: u64, + node_ref: &ItemNodeRc) + -> Result + { + use SearchResult::*; + let node = node_ref.borrow(); + let item_index = node.item_index; + let item = &node.item; + let mut expanded = expanded.iter_items(); + let mut cap = self.capture.borrow_mut(); + let search_result = cap.find_child(&mut expanded, range, to_index)?; + Ok(match search_result { + TopLevelItem(found_index, _) => { + let range_to_item = range.start..found_index; + cap.count_within(item_index, item, &range_to_item)? + }, + NextLevelItem(span_index, .., child) => { + let range_to_span = range.start..span_index; + cap.count_within(item_index, item, &range_to_span)? + + cap.count_before(item_index, item, span_index, &child)? + }, + }) + } + + fn count_in_range(&self, + range: &Range, + node_ref: &ItemNodeRc) + -> Result + { + let mut cap = self.capture.borrow_mut(); + let node = node_ref.borrow(); + cap.count_within(node.item_index, &node.item, range) + } + + fn count_around_offset(&self, + expanded: &ItemNodeSet, + range: &Range, + node_ref: &ItemNodeRc, + offset: u64, + end: u64) + -> Result<(u64, u64), Error> + { + let length = + self.count_items(range)? + self.count_within(expanded, range)?; + let rows_before_offset = if offset == 0 { + 0 + } else { + self.count_to_item(expanded, range, offset - 1, node_ref)? + }; + let rows_before_end = if end == length { + self.count_in_range(range, node_ref)? + } else { + self.count_to_item(expanded, range, end, node_ref)? + }; + let rows_after_offset = rows_before_end - rows_before_offset; + Ok((rows_before_offset, rows_after_offset)) + } + + fn count_within(&self, + expanded: &ItemNodeSet, + range: &Range) + -> Result + { + let mut cap = self.capture.borrow_mut(); + Ok(expanded + .iter_items() + .map(|(index, item)| cap.count_within(index, &item, range)) + .collect::, Error>>()? + .iter() + .sum()) + } + pub fn update(&self, model: &Model) -> Result { #[cfg(feature="debug-region-map")] let rows_before = self.row_count(); @@ -738,10 +1699,13 @@ where Item: 'static + Clone + Debug, node_rc: &Rc>, mut position: u64, model: &Model) - -> Result + -> Result<(u64, Option>), Error> where T: Node + 'static, Rc>: NodeRcOps, { + use InterleavedUpdate::*; + use UpdateState::*; + // Extract details about the current node. let node = node_rc.borrow(); let expanded = node.expanded(); @@ -756,11 +1720,10 @@ where Item: 'static + Clone + Debug, let mut cap = self.capture.borrow_mut(); let (completion, new_direct_count) = cap.item_children(node.item(), self.view_mode)?; - let completed = completion.is_complete(); let children_added = new_direct_count - old_direct_count; drop(node); - if let Some(item_node_rc) = node_rc.item_node_rc() { + let mut state = if let Some(item_node_rc) = node_rc.item_node_rc() { // This is an item node. let mut item_node = item_node_rc.borrow_mut(); @@ -797,29 +1760,81 @@ where Item: 'static + Clone + Debug, // Advance past this node's own row. position += 1; - } + + // Construct node state for remaining steps. + use CompletionStatus::*; + let children_to_add = if expanded && children_added > 0 { + Some(children_added) + } else { + None + }; + match (item_node.completion, children_to_add) { + (InterleavedComplete(end), Some(count)) => { + drop(item_node); + Interleaved(Some(AddedComplete(item_node_rc, count, end))) + }, + (InterleavedOngoing, Some(count)) => { + drop(item_node); + Interleaved(Some(AddedOngoing(item_node_rc, count))) + }, + (InterleavedComplete(_) | InterleavedOngoing, None) => + Interleaved(None), + (..) => + Contiguous() + } + } else { + Root(Vec::new()) + }; drop(cap); - // If completed, remove from incomplete node list. - if completed { - node_rc.borrow_mut().set_completed(); - } + // Update node's completion status. + node_rc.borrow_mut().set_completion(completion); if expanded { // Deal with incomplete children of this node. let mut last_index = 0; for (index, child_weak) in incomplete_children { if let Some(child_rc) = child_weak.upgrade() { - // Advance position up to this child. - position += node_rc - .borrow() - .children() - .rows_between(last_index, index); - // Recursively update this child. - position = self.update_node::>( - &child_rc, position, model)?; - last_index = index + 1; + // Advance position to this child. + position = match state { + Root(..) => + // Find position of top level item from region map. + self.find_top_level_item(&child_rc)?, + Interleaved(..) => { + // There can be only one incomplete child of an + // item whose children are interleaved. If it is + // expanded, we can find it from the region map. + // Otherwise, it must always be at the last row. + let end = self.row_count(); + if child_rc.borrow().expanded() { + self.find_expanded(position..end, &child_rc)? + } else { + end - 1 + } + }, + Contiguous(..) => { + // If the children of this node are contiguous, we + // can advance to another child by summing the + // total rows of the intermediate children. + let delta = node_rc + .borrow() + .children() + .rows_between(last_index, index); + last_index = index + 1; + position + delta + }, + }; + // Recursively update the child. + let (new_position, update) = + self.update_node::>( + &child_rc, position, model)?; + // If the child has a pending interleaved update, store it. + if let (Root(todo), Some(update)) = (&mut state, update) { + todo.push(update); + } + // Advance position to after this child and its children. + position = new_position; } else { // Child no longer referenced, remove it. node_rc @@ -830,26 +1845,244 @@ where Item: 'static + Clone + Debug, } } - // Advance to the end of this node's existing children. - position += node_rc - .borrow_mut() - .children_mut() - .rows_between(last_index, old_direct_count); + if matches!(state, Contiguous()) { + // Advance to the end of this node's existing children. + position += node_rc + .borrow_mut() + .children_mut() + .rows_between(last_index, old_direct_count); + } } // Now deal with any new children of this node. - if children_added > 0 { - // Update this node's child counts. + if let Root(interleaved_updates) = &state { + // Updating the root node. New rows are added after all others. + let initial_position = self.row_count(); + position = initial_position; + + // Running totals of rows pending and added. + let mut top_level_added = 0; + let mut top_level_pending = children_added; + let mut second_level_added = 0; + let mut second_level_pending = 0; + + // Collect completed items, and count pending second level items. + let mut completed = BTreeMap::new(); + for update in interleaved_updates { + match update { + AddedComplete(child_rc, children_added, end) => { + completed.insert(*end, child_rc); + second_level_pending += children_added; + }, + AddedOngoing(_child_rc, children_added) => { + second_level_pending += children_added; + } + } + } + + // Handle completed items. + let mut completed = completed.into_iter(); + if let Some((child_end, child_rc)) = completed.next() { + + // Find the last interleaved region. + let (mut expanded, range, old_length) = self.regions + .borrow_mut() + .values_mut() + .rev() + .find_map(|region| + if let InterleavedSearch(expanded, range) = + &mut region.source + { + // Copy all the fields. + let fields = ( + expanded.clone(), + range.clone(), + region.offset + region.length, + ); + // Extend the search range to the new end. + range.end = child_end; + // Return the copied fields. + Some(fields) + } else { + None + } + ) + .context("No interleaved region found")?; + + // Add a new region with the additional rows. + let full_range = range.start..child_end; + let old_top = self.count_items(&range)?; + let full_top = self.count_items(&full_range)?; + let full_second = self.count_within(&expanded, &full_range)?; + let full_length = full_top + full_second; + let new_length = full_length - old_length; + let new_top = full_top - old_top; + let new_second = new_length - new_top; + let new_region = Region { + source: InterleavedSearch(expanded.clone(), full_range), + offset: old_length, + length: new_length, + }; + self.insert_region(position, new_region)?; + position += new_length; + top_level_added += new_top; + top_level_pending -= new_top; + second_level_added += new_second; + second_level_pending -= new_second; + + // Update for next child completion. + let mut last_end = child_end; + expanded = expanded.without(child_rc); + + // Now handle any further completed children. + for (child_end, child_rc) in completed { + // Add a region for the in-between top level item. + self.insert_region(position, Region { + source: TopLevelItems(), + offset: last_end, + length: 1, + })?; + position += 1; + top_level_added += 1; + top_level_pending -= 1; + + // Add a new interleaved region. + let range = last_end..child_end; + let new_top = self.count_items(&range)?; + let new_second = self.count_within(&expanded, &range)?; + let new_length = new_top + new_second; + self.insert_region(position, Region { + source: InterleavedSearch( + expanded.clone(), range.clone()), + offset: 0, + length: new_length, + })?; + position += new_length; + top_level_added += new_top; + top_level_pending -= new_top; + second_level_added += new_second; + second_level_pending -= new_second; + + // Update for next completion. + last_end = child_end; + expanded = expanded.without(child_rc); + } + } + + // Add any further pending items at the end. + if top_level_pending > 0 || second_level_pending > 0 { + // Find the source and offset needed to extend the last region + // that contains top level items, or default to a new top level + // source if the map is empty. + let (source, offset, old_end, old_length) = self.regions + .borrow_mut() + .values_mut() + .rev() + .find_map(|region| { + let old_length = region.offset + region.length; + Some(match &mut region.source { + TopLevelItems() if second_level_pending == 0 => { + let source = region.source.clone(); + let old_end = region.offset + region.length; + let offset = old_end; + (source, offset, old_end, old_length) + }, + InterleavedSearch(expanded, range) => { + let old_end = range.end; + if expanded.any_incomplete() { + // Still ongoing. Extend its range + // and continue it with a new region. + range.end += top_level_pending; + let source = InterleavedSearch( + expanded.clone(), range.clone()); + let offset = region.offset + region.length; + (source, offset, old_end, old_length) + } else { + // Region has ended, start a new top + // level region after it. + let source = TopLevelItems(); + let offset = old_end; + (source, offset, old_end, old_length) + } + }, + _ => return None + }) + }) + .unwrap_or((TopLevelItems(), 0, 0, 0)); + + // Add a region with the new rows. + let (new_length, new_top, new_second) = match &source { + InterleavedSearch(expanded, full_range) => { + let old_range = full_range.start..old_end; + let old_top = + self.count_items(&old_range).unwrap_or(0); + let full_top = + self.count_items(full_range)?; + let full_second = + self.count_within(expanded, full_range)?; + let full_length = full_top + full_second; + let new_length = full_length - old_length; + let new_top = full_top - old_top; + let new_second = new_length - new_top; + (new_length, new_top, new_second) + }, + TopLevelItems() => + (top_level_pending, top_level_pending, 0), + _ => + unreachable!() + }; + let region = Region {source, offset, length: new_length}; + self.insert_region(position, region)?; + position += new_length; + top_level_added += new_top; + top_level_pending -= new_top; + second_level_added += new_second; + second_level_pending -= new_second; + + #[cfg(feature="debug-region-map")] { + println!(); + println!("Region map after root node update:"); + for (start, region) in self.regions.borrow().iter() { + println!("{}: {:?}", start, region); + } + } + + // We should now have added all pending rows. + assert!(top_level_pending == 0); + assert!(second_level_pending == 0); + + self.merge_regions(); + + // Update child counts. + let mut root = self.root.borrow_mut(); + root.children.direct_count += top_level_added; + root.children.total_count += + top_level_added + second_level_added; + drop(root); + + // Apply a single update to cover all the regions added. + self.apply_update(model, initial_position, ModelUpdate { + rows_added: top_level_added + second_level_added, + rows_removed: 0, + rows_changed: 0 + }); + } + } else if children_added > 0 { + // This is an item node. Update child counts. let mut node = node_rc.borrow_mut(); let children = node.children_mut(); children.direct_count += children_added; children.total_count += children_added; drop(node); - if expanded { - #[cfg(feature="debug-region-map")] - println!("\nAdding {} new children at {}", - children_added, position); + let interleaved = matches!(state, Interleaved(..)); + + if expanded && !interleaved { + #[cfg(feature="debug-region-map")] { + println!(); + println!("Adding {} new children at {}", + children_added, position); + } // Move the following regions down to make space. let following_regions = self.regions @@ -885,8 +2118,12 @@ where Item: 'static + Clone + Debug, } } - // Return the position after all of this node's rows. - Ok(position) + // Return the position after all of this node's rows, and any pending + // interleaved update to do for this node. + Ok(match state { + Interleaved(update) => (position, update), + _ => (position, None) + }) } fn fetch(&self, position: u64) -> Result, Error> { @@ -899,13 +2136,50 @@ where Item: 'static + Clone + Debug, .with_context(|| format!( "No region before position {position}"))?; - // Get the index of this row relative to the start of that region. - let relative_position = region.offset + (position - start); + // Get the index of this row relative to the start of the region source. + let row_index = region.offset + (position - start); // Get the parent for this row, according to the type of region. - let parent_ref: AnyNodeRc = match region.source { - TopLevelItems() => self.root.clone(), - ChildrenOf(node_ref) => node_ref, + let mut cap = self.capture.borrow_mut(); + let (parent_ref, item_index, item): + (AnyNodeRc, u64, Item) = + match region.source + { + TopLevelItems() => ( + self.root.clone(), + row_index, + cap.item(None, self.view_mode, row_index)?), + ChildrenOf(node_ref) => ( + node_ref.clone(), + row_index, + cap.item( + Some(&node_ref.borrow().item), + self.view_mode, + row_index)?), + InterleavedSearch(expanded, range) => { + // Run the interleaved search. + let mut expanded_items = expanded.iter_items(); + let search_result = cap.find_child( + &mut expanded_items, &range, row_index)?; + // Return a node corresponding to the search result. + use SearchResult::*; + match search_result { + // Search found a top level item. + TopLevelItem(index, item) => { + (self.root.clone(), index, item) + }, + // Search found a child of an expanded top level item. + NextLevelItem(_, parent_index, child_index, item) => { + // There must already be a node for its parent. + let parent_ref = self.root + .borrow() + .children() + .get_expanded(parent_index) + .context("Parent dropped")?; + (parent_ref, child_index, item) + } + } + } }; // Check if we already have a node for this item in the parent's @@ -914,7 +2188,7 @@ where Item: 'static + Clone + Debug, .borrow() .children() .expanded - .get(&relative_position) + .get(&item_index) { return Ok(node_rc.clone()) } @@ -923,30 +2197,28 @@ where Item: 'static + Clone + Debug, if let Some(node_rc) = parent_ref .borrow() .children() - .fetch_incomplete(relative_position) + .fetch_incomplete(item_index) { return Ok(node_rc) } // Otherwise, fetch it from the database. - let mut cap = self.capture.borrow_mut(); - let mut parent = parent_ref.borrow_mut(); - let item = - cap.item(parent.item(), self.view_mode, relative_position)?; let (completion, child_count) = cap.item_children(Some(&item), self.view_mode)?; let node = ItemNode { item, parent: Rc::downgrade(&parent_ref), - item_index: relative_position, + item_index, + completion, children: Children::new(child_count), widgets: RefCell::new(HashSet::new()), }; let node_rc = Rc::new(RefCell::new(node)); if !completion.is_complete() { - parent + parent_ref + .borrow_mut() .children_mut() - .add_incomplete(relative_position, &node_rc); + .add_incomplete(item_index, &node_rc); } Ok(node_rc) } From 34e3bc645d3666db71905868614c3f4a958247ce Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Fri, 26 Jul 2024 11:29:25 +0100 Subject: [PATCH 5/6] Add support for interleaved view mode. --- src/capture.rs | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/capture.rs b/src/capture.rs index 1db1a286..c2641e98 100644 --- a/src/capture.rs +++ b/src/capture.rs @@ -231,6 +231,7 @@ pub enum TrafficItem { #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] pub enum TrafficViewMode { Hierarchical, + Interleaved, Transactions, Packets, } @@ -242,6 +243,7 @@ impl TrafficViewMode { use TrafficViewMode::*; match self { Hierarchical => "Hierarchical", + Interleaved => "Interleaved", Transactions => "Transactions", Packets => "Packets", } @@ -252,6 +254,7 @@ impl TrafficViewMode { use TrafficViewMode::*; match self { Hierarchical => "traffic-hierarchical", + Interleaved => "traffic-interleaved", Transactions => "traffic-transactions", Packets => "traffic-packets", } @@ -262,6 +265,7 @@ impl TrafficViewMode { use TrafficViewMode::*; match log_name { "traffic-hierarchical" => Hierarchical, + "traffic-interleaved" => Interleaved, "traffic-transactions" => Transactions, "traffic-packets" => Packets, _ => panic!("Unrecognised log name '{log_name}'") @@ -1528,7 +1532,7 @@ impl ItemSource for CaptureReader { use TrafficViewMode::*; match parent { None => Ok(match view_mode { - Hierarchical => { + Hierarchical | Interleaved => { let item_id = TrafficItemId::from(index); let group_id = self.item_index.get(item_id)?; TransactionGroup(group_id) @@ -1580,7 +1584,7 @@ impl ItemSource for CaptureReader { Ok(match parent { None => { (self.completion(), match view_mode { - Hierarchical => self.item_index.len(), + Hierarchical | Interleaved => self.item_index.len(), Transactions => self.transaction_index.len(), Packets => self.packet_index.len(), }) @@ -1592,11 +1596,19 @@ impl ItemSource for CaptureReader { } let transaction_count = self.group_range(&entry)?.len(); let ep_traf = self.endpoint_traffic(entry.endpoint_id())?; - if entry.group_id().value >= ep_traf.end_index.len() { - (Ongoing, transaction_count) - } else { - (Complete, transaction_count) - } + let ep_group_id = entry.group_id(); + let ongoing = ep_group_id.value >= ep_traf.end_index.len(); + let status = match (view_mode, ongoing) { + (Hierarchical, true) => Ongoing, + (Hierarchical, false) => Complete, + (Interleaved, true) => InterleavedOngoing, + (Interleaved, false) => { + let end = ep_traf.end_index.get(ep_group_id)?; + InterleavedComplete(end.value) + } + (Transactions | Packets, _) => unreachable!(), + }; + (status, transaction_count) }, Some(Transaction(_, transaction_id)) => { let packet_count = self.transaction_index.target_range( From f06061af1aed8cb5619a8d067b8bb30ea2ce6f2a Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Sun, 28 Jul 2024 19:17:14 +0100 Subject: [PATCH 6/6] Enable interleaved view. --- src/ui.rs | 4 +- tests/ui/emf2022-badge/reference.txt | 39 +++++ tests/ui/mouse-step/reference.txt | 190 +++++++++++++++++++++++++ tests/ui/split-poll-step/reference.txt | 40 ++++++ 4 files changed, 271 insertions(+), 2 deletions(-) diff --git a/src/ui.rs b/src/ui.rs index 2f5c11b3..327232de 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -105,8 +105,8 @@ use { crate::record_ui::Recording, }; -const TRAFFIC_MODES: [TrafficViewMode; 3] = - [Hierarchical, Transactions, Packets]; +const TRAFFIC_MODES: [TrafficViewMode; 4] = + [Hierarchical, Interleaved, Transactions, Packets]; static TOTAL: AtomicU64 = AtomicU64::new(0); static CURRENT: AtomicU64 = AtomicU64::new(0); diff --git a/tests/ui/emf2022-badge/reference.txt b/tests/ui/emf2022-badge/reference.txt index 231ca667..c3d8fb77 100644 --- a/tests/ui/emf2022-badge/reference.txt +++ b/tests/ui/emf2022-badge/reference.txt @@ -33,6 +33,39 @@ At traffic-hierarchical row 0: + Getting HID report descriptor #0 for interface 2.2, reading 144 bytes + Class request #9, index 0, value 513 for interface 2.2, writing 2 bytes + Polling 194 times for interrupt transfer on endpoint 2.3 IN +At traffic-interleaved row 0: ++ 209 SOF groups ++ Getting device descriptor #0 for device 0, reading 18 of 64 requested bytes ++ Setting address to 1 for device 0 ++ Getting device descriptor #0 for device 1, reading 18 bytes ++ 3 times: Getting device qualifier descriptor #0 for device 1, reading 0 of 10 requested bytes, stalled ++ Getting configuration descriptor #0 for device 1, reading 9 bytes ++ Getting configuration descriptor #0 for device 1, reading 98 bytes ++ Getting string descriptor #0 for device 1, reading 4 of 255 requested bytes ++ Getting string descriptor #2, language 0x0409 (English/US) for device 1, reading 56 of 255 requested bytes: 'USB JTAG/serial debug unit\u{0}' ++ Getting string descriptor #1, language 0x0409 (English/US) for device 1, reading 22 of 255 requested bytes: 'Espressif\u{0}' ++ Getting string descriptor #3, language 0x0409 (English/US) for device 1, reading 36 of 255 requested bytes: 'F4:12:FA:4D:F1:7C' ++ Setting configuration 1 for device 1 ++ Class request #32, index 0, value 0 for interface 1.0, writing 7 bytes ++ Getting device descriptor #0 for device 0, reading 18 of 64 requested bytes ++ Setting address to 2 for device 0 ++ Getting device descriptor #0 for device 2, reading 18 bytes ++ 3 times: Getting device qualifier descriptor #0 for device 2, reading 0 of 10 requested bytes, stalled ++ Getting configuration descriptor #0 for device 2, reading 9 bytes ++ Getting configuration descriptor #0 for device 2, reading 100 bytes ++ Getting string descriptor #0 for device 2, reading 4 of 255 requested bytes ++ Getting string descriptor #2, language 0x0409 (English/US) for device 2, reading 12 of 255 requested bytes: 'TiDAL' ++ Getting string descriptor #1, language 0x0409 (English/US) for device 2, reading 44 of 255 requested bytes: 'Electromagnetic Field' ++ Getting string descriptor #3, language 0x0409 (English/US) for device 2, reading 14 of 255 requested bytes: '123456' ++ Setting configuration 1 for device 2 ++ Getting string descriptor #4, language 0x0409 (English/US) for device 2, reading 42 of 255 requested bytes: 'Espressif CDC Device' ++ Class request #32, index 0, value 0 for interface 2.0, writing 7 bytes ++ Getting string descriptor #5, language 0x0409 (English/US) for device 2, reading 24 of 255 requested bytes: 'TiDAL badge' ++ Getting string descriptor #3, language 0x0409 (English/US) for device 2, reading 14 of 255 requested bytes: '123456' ++ Class request #10, index 0, value 0 for interface 2.2 ++ Getting HID report descriptor #0 for interface 2.2, reading 144 bytes ++ Class request #9, index 0, value 513 for interface 2.2, writing 2 bytes ++ Polling 194 times for interrupt transfer on endpoint 2.3 IN At traffic-transactions row 0: + 6 SOF packets + SETUP transaction on 0.0 with 8 data bytes, ACK: [80, 06, 00, 01, 00, 00, 40, 00] @@ -4287,6 +4320,12 @@ At traffic-hierarchical row 0: At traffic-hierarchical row 35: - Polling 194 times for interrupt transfer on endpoint 2.3 IN + Polling 263 times for interrupt transfer on endpoint 2.3 IN +At traffic-interleaved row 0: +- 209 SOF groups ++ 278 SOF groups +At traffic-interleaved row 35: +- Polling 194 times for interrupt transfer on endpoint 2.3 IN ++ Polling 263 times for interrupt transfer on endpoint 2.3 IN At traffic-transactions row 542: + IN transaction on 2.3, NAK + 8 SOF packets diff --git a/tests/ui/mouse-step/reference.txt b/tests/ui/mouse-step/reference.txt index 14b6e9b4..46a3be15 100644 --- a/tests/ui/mouse-step/reference.txt +++ b/tests/ui/mouse-step/reference.txt @@ -2,6 +2,8 @@ Opening file tests/mouse/capture.pcap Updating after 1 packets decoded At traffic-hierarchical row 0: + 1 invalid groups +At traffic-interleaved row 0: ++ 1 invalid groups At traffic-transactions row 0: + 1 malformed packet At traffic-packets row 0: @@ -22,6 +24,8 @@ At traffic-hierarchical row 0: + 1 invalid groups + 1 malformed packet + Malformed packet (invalid PID) of 1 byte: [FF] +At traffic-interleaved row 1: ++ Incomplete control transfer on device 0 At traffic-transactions row 1: + SETUP transaction on 0.0 At traffic-packets row 1: @@ -60,6 +64,9 @@ At traffic-hierarchical row 3: + Getting device descriptor #0 for device 0, reading 0 of 64 requested bytes, incomplete At traffic-hierarchical row 8: + IN transaction on 0.0 +At traffic-interleaved row 1: +- Incomplete control transfer on device 0 ++ Getting device descriptor #0 for device 0, reading 0 of 64 requested bytes, incomplete At traffic-transactions row 2: + IN transaction on 0.0 At traffic-packets row 4: @@ -84,6 +91,9 @@ At traffic-hierarchical row 3: + Getting device descriptor #0 for device 0, reading 0 of 64 requested bytes, incomplete At traffic-hierarchical row 11: + IN transaction on 0.0 +At traffic-interleaved row 1: +- Incomplete control transfer on device 0 ++ Getting device descriptor #0 for device 0, reading 0 of 64 requested bytes, incomplete At traffic-transactions row 3: + IN transaction on 0.0 At traffic-packets row 6: @@ -108,6 +118,9 @@ At traffic-hierarchical row 3: + Getting device descriptor #0 for device 0, reading 0 of 64 requested bytes, incomplete At traffic-hierarchical row 14: + IN transaction on 0.0 +At traffic-interleaved row 1: +- Incomplete control transfer on device 0 ++ Getting device descriptor #0 for device 0, reading 0 of 64 requested bytes, incomplete At traffic-transactions row 4: + IN transaction on 0.0 At traffic-packets row 8: @@ -143,6 +156,9 @@ At traffic-hierarchical row 3: + Getting device descriptor #0 for device 0, reading 8 of 64 requested bytes, incomplete At traffic-hierarchical row 18: + IN transaction on 0.0 +At traffic-interleaved row 1: +- Incomplete control transfer on device 0 ++ Getting device descriptor #0 for device 0, reading 8 of 64 requested bytes, incomplete At traffic-transactions row 5: + IN transaction on 0.0 At traffic-packets row 11: @@ -162,6 +178,9 @@ At traffic-hierarchical row 3: + Getting device descriptor #0 for device 0, reading 8 of 64 requested bytes, incomplete At traffic-hierarchical row 19: + IN transaction on 0.0 +At traffic-interleaved row 1: +- Incomplete control transfer on device 0 ++ Getting device descriptor #0 for device 0, reading 8 of 64 requested bytes, incomplete At traffic-transactions row 6: + IN transaction on 0.0 At traffic-packets row 13: @@ -181,6 +200,9 @@ At traffic-hierarchical row 3: + Getting device descriptor #0 for device 0, reading 8 of 64 requested bytes, incomplete At traffic-hierarchical row 20: + IN transaction on 0.0 +At traffic-interleaved row 1: +- Incomplete control transfer on device 0 ++ Getting device descriptor #0 for device 0, reading 8 of 64 requested bytes, incomplete At traffic-transactions row 7: + IN transaction on 0.0 At traffic-packets row 15: @@ -209,6 +231,9 @@ At traffic-hierarchical row 3: + Getting device descriptor #0 for device 0, reading 16 of 64 requested bytes, incomplete At traffic-hierarchical row 21: + IN transaction on 0.0 +At traffic-interleaved row 1: +- Incomplete control transfer on device 0 ++ Getting device descriptor #0 for device 0, reading 16 of 64 requested bytes, incomplete At traffic-transactions row 8: + IN transaction on 0.0 At traffic-packets row 18: @@ -228,6 +253,9 @@ At traffic-hierarchical row 3: + Getting device descriptor #0 for device 0, reading 16 of 64 requested bytes, incomplete At traffic-hierarchical row 22: + IN transaction on 0.0 +At traffic-interleaved row 1: +- Incomplete control transfer on device 0 ++ Getting device descriptor #0 for device 0, reading 16 of 64 requested bytes, incomplete At traffic-transactions row 9: + IN transaction on 0.0 At traffic-packets row 20: @@ -256,6 +284,9 @@ At traffic-hierarchical row 3: + Getting device descriptor #0 for device 0, reading 18 of 64 requested bytes, incomplete At traffic-hierarchical row 23: + OUT transaction on 0.0 +At traffic-interleaved row 1: +- Incomplete control transfer on device 0 ++ Getting device descriptor #0 for device 0, reading 18 of 64 requested bytes, incomplete At traffic-transactions row 10: + OUT transaction on 0.0 At traffic-packets row 23: @@ -281,6 +312,8 @@ At traffic-packets row 25: Updating after 27 packets decoded At traffic-hierarchical row 24: + Incomplete control transfer on device 0 +At traffic-interleaved row 2: ++ Incomplete control transfer on device 0 At traffic-transactions row 11: + SETUP transaction on 0.0 At traffic-packets row 26: @@ -301,6 +334,9 @@ Updating after 30 packets decoded At traffic-hierarchical row 24: - Incomplete control transfer on device 0 + Setting address to 4 for device 0, incomplete +At traffic-interleaved row 2: +- Incomplete control transfer on device 0 ++ Setting address to 4 for device 0, incomplete At traffic-transactions row 12: + IN transaction on 0.0 At traffic-packets row 29: @@ -315,6 +351,9 @@ Updating after 32 packets decoded At traffic-hierarchical row 24: - Incomplete control transfer on device 0 + Setting address to 4 for device 0, incomplete +At traffic-interleaved row 2: +- Incomplete control transfer on device 0 ++ Setting address to 4 for device 0, incomplete At traffic-transactions row 13: + IN transaction on 0.0 At traffic-packets row 31: @@ -380,6 +419,8 @@ At traffic-hierarchical row 0: + IN transaction on 0.0 with 2 data bytes, ACK: [00, 01] + OUT transaction on 0.0 with no data, ACK + Setting address to 4 for device 0 +At traffic-interleaved row 3: ++ Incomplete control transfer on device 4 At traffic-transactions row 14: + SETUP transaction on 4.0 At traffic-packets row 34: @@ -402,6 +443,9 @@ Updating after 38 packets decoded At traffic-hierarchical row 25: - Incomplete control transfer on device 4 + Getting device descriptor #0 for device 4, reading 0 of 18 requested bytes, incomplete +At traffic-interleaved row 3: +- Incomplete control transfer on device 4 ++ Getting device descriptor #0 for device 4, reading 0 of 18 requested bytes, incomplete At traffic-transactions row 15: + IN transaction on 4.0 At traffic-packets row 37: @@ -416,6 +460,9 @@ Updating after 40 packets decoded At traffic-hierarchical row 25: - Incomplete control transfer on device 4 + Getting device descriptor #0 for device 4, reading 0 of 18 requested bytes, incomplete +At traffic-interleaved row 3: +- Incomplete control transfer on device 4 ++ Getting device descriptor #0 for device 4, reading 0 of 18 requested bytes, incomplete At traffic-transactions row 16: + IN transaction on 4.0 At traffic-packets row 39: @@ -430,6 +477,9 @@ Updating after 42 packets decoded At traffic-hierarchical row 25: - Incomplete control transfer on device 4 + Getting device descriptor #0 for device 4, reading 0 of 18 requested bytes, incomplete +At traffic-interleaved row 3: +- Incomplete control transfer on device 4 ++ Getting device descriptor #0 for device 4, reading 0 of 18 requested bytes, incomplete At traffic-transactions row 17: + IN transaction on 4.0 At traffic-packets row 41: @@ -450,6 +500,9 @@ Updating after 45 packets decoded At traffic-hierarchical row 25: - Incomplete control transfer on device 4 + Getting device descriptor #0 for device 4, reading 8 of 18 requested bytes, incomplete +At traffic-interleaved row 3: +- Incomplete control transfer on device 4 ++ Getting device descriptor #0 for device 4, reading 8 of 18 requested bytes, incomplete At traffic-transactions row 18: + IN transaction on 4.0 At traffic-packets row 44: @@ -464,6 +517,9 @@ Updating after 47 packets decoded At traffic-hierarchical row 25: - Incomplete control transfer on device 4 + Getting device descriptor #0 for device 4, reading 8 of 18 requested bytes, incomplete +At traffic-interleaved row 3: +- Incomplete control transfer on device 4 ++ Getting device descriptor #0 for device 4, reading 8 of 18 requested bytes, incomplete At traffic-transactions row 19: + IN transaction on 4.0 At traffic-packets row 46: @@ -478,6 +534,9 @@ Updating after 49 packets decoded At traffic-hierarchical row 25: - Incomplete control transfer on device 4 + Getting device descriptor #0 for device 4, reading 8 of 18 requested bytes, incomplete +At traffic-interleaved row 3: +- Incomplete control transfer on device 4 ++ Getting device descriptor #0 for device 4, reading 8 of 18 requested bytes, incomplete At traffic-transactions row 20: + IN transaction on 4.0 At traffic-packets row 48: @@ -498,6 +557,9 @@ Updating after 52 packets decoded At traffic-hierarchical row 25: - Incomplete control transfer on device 4 + Getting device descriptor #0 for device 4, reading 16 of 18 requested bytes, incomplete +At traffic-interleaved row 3: +- Incomplete control transfer on device 4 ++ Getting device descriptor #0 for device 4, reading 16 of 18 requested bytes, incomplete At traffic-transactions row 21: + IN transaction on 4.0 At traffic-packets row 51: @@ -512,6 +574,9 @@ Updating after 54 packets decoded At traffic-hierarchical row 25: - Incomplete control transfer on device 4 + Getting device descriptor #0 for device 4, reading 16 of 18 requested bytes, incomplete +At traffic-interleaved row 3: +- Incomplete control transfer on device 4 ++ Getting device descriptor #0 for device 4, reading 16 of 18 requested bytes, incomplete At traffic-transactions row 22: + IN transaction on 4.0 At traffic-packets row 53: @@ -532,6 +597,9 @@ Updating after 57 packets decoded At traffic-hierarchical row 25: - Incomplete control transfer on device 4 + Getting device descriptor #0 for device 4, reading 18 bytes, incomplete +At traffic-interleaved row 3: +- Incomplete control transfer on device 4 ++ Getting device descriptor #0 for device 4, reading 18 bytes, incomplete At traffic-transactions row 23: + OUT transaction on 4.0 At traffic-packets row 56: @@ -554,6 +622,8 @@ At devices row 0: Updating after 60 packets decoded At traffic-hierarchical row 26: + Incomplete control transfer on device 4 +At traffic-interleaved row 4: ++ Incomplete control transfer on device 4 At traffic-transactions row 24: + SETUP transaction on 4.0 At traffic-packets row 59: @@ -574,6 +644,9 @@ Updating after 63 packets decoded At traffic-hierarchical row 26: - Incomplete control transfer on device 4 + Getting configuration descriptor #0 for device 4, reading 0 of 9 requested bytes, incomplete +At traffic-interleaved row 4: +- Incomplete control transfer on device 4 ++ Getting configuration descriptor #0 for device 4, reading 0 of 9 requested bytes, incomplete At traffic-transactions row 25: + IN transaction on 4.0 At traffic-packets row 62: @@ -588,6 +661,9 @@ Updating after 65 packets decoded At traffic-hierarchical row 26: - Incomplete control transfer on device 4 + Getting configuration descriptor #0 for device 4, reading 0 of 9 requested bytes, incomplete +At traffic-interleaved row 4: +- Incomplete control transfer on device 4 ++ Getting configuration descriptor #0 for device 4, reading 0 of 9 requested bytes, incomplete At traffic-transactions row 26: + IN transaction on 4.0 At traffic-packets row 64: @@ -602,6 +678,9 @@ Updating after 67 packets decoded At traffic-hierarchical row 26: - Incomplete control transfer on device 4 + Getting configuration descriptor #0 for device 4, reading 0 of 9 requested bytes, incomplete +At traffic-interleaved row 4: +- Incomplete control transfer on device 4 ++ Getting configuration descriptor #0 for device 4, reading 0 of 9 requested bytes, incomplete At traffic-transactions row 27: + IN transaction on 4.0 At traffic-packets row 66: @@ -622,6 +701,9 @@ Updating after 70 packets decoded At traffic-hierarchical row 26: - Incomplete control transfer on device 4 + Getting configuration descriptor #0 for device 4, reading 8 of 9 requested bytes, incomplete +At traffic-interleaved row 4: +- Incomplete control transfer on device 4 ++ Getting configuration descriptor #0 for device 4, reading 8 of 9 requested bytes, incomplete At traffic-transactions row 28: + IN transaction on 4.0 At traffic-packets row 69: @@ -636,6 +718,9 @@ Updating after 72 packets decoded At traffic-hierarchical row 26: - Incomplete control transfer on device 4 + Getting configuration descriptor #0 for device 4, reading 8 of 9 requested bytes, incomplete +At traffic-interleaved row 4: +- Incomplete control transfer on device 4 ++ Getting configuration descriptor #0 for device 4, reading 8 of 9 requested bytes, incomplete At traffic-transactions row 29: + IN transaction on 4.0 At traffic-packets row 71: @@ -656,6 +741,9 @@ Updating after 75 packets decoded At traffic-hierarchical row 26: - Incomplete control transfer on device 4 + Getting configuration descriptor #0 for device 4, reading 9 bytes, incomplete +At traffic-interleaved row 4: +- Incomplete control transfer on device 4 ++ Getting configuration descriptor #0 for device 4, reading 9 bytes, incomplete At traffic-transactions row 30: + OUT transaction on 4.0 At traffic-packets row 74: @@ -678,6 +766,8 @@ At devices row 0: Updating after 78 packets decoded At traffic-hierarchical row 27: + Incomplete control transfer on device 4 +At traffic-interleaved row 5: ++ Incomplete control transfer on device 4 At traffic-transactions row 31: + SETUP transaction on 4.0 At traffic-packets row 77: @@ -701,6 +791,9 @@ Updating after 81 packets decoded At traffic-hierarchical row 27: - Incomplete control transfer on device 4 + Getting configuration descriptor #0 for device 4, reading 0 of 34 requested bytes, incomplete +At traffic-interleaved row 5: +- Incomplete control transfer on device 4 ++ Getting configuration descriptor #0 for device 4, reading 0 of 34 requested bytes, incomplete At traffic-transactions row 32: + IN transaction on 4.0 At traffic-packets row 80: @@ -715,6 +808,9 @@ Updating after 83 packets decoded At traffic-hierarchical row 27: - Incomplete control transfer on device 4 + Getting configuration descriptor #0 for device 4, reading 0 of 34 requested bytes, incomplete +At traffic-interleaved row 5: +- Incomplete control transfer on device 4 ++ Getting configuration descriptor #0 for device 4, reading 0 of 34 requested bytes, incomplete At traffic-transactions row 33: + IN transaction on 4.0 At traffic-packets row 82: @@ -729,6 +825,9 @@ Updating after 85 packets decoded At traffic-hierarchical row 27: - Incomplete control transfer on device 4 + Getting configuration descriptor #0 for device 4, reading 0 of 34 requested bytes, incomplete +At traffic-interleaved row 5: +- Incomplete control transfer on device 4 ++ Getting configuration descriptor #0 for device 4, reading 0 of 34 requested bytes, incomplete At traffic-transactions row 34: + IN transaction on 4.0 At traffic-packets row 84: @@ -749,6 +848,9 @@ Updating after 88 packets decoded At traffic-hierarchical row 27: - Incomplete control transfer on device 4 + Getting configuration descriptor #0 for device 4, reading 8 of 34 requested bytes, incomplete +At traffic-interleaved row 5: +- Incomplete control transfer on device 4 ++ Getting configuration descriptor #0 for device 4, reading 8 of 34 requested bytes, incomplete At traffic-transactions row 35: + IN transaction on 4.0 At traffic-packets row 87: @@ -763,6 +865,9 @@ Updating after 90 packets decoded At traffic-hierarchical row 27: - Incomplete control transfer on device 4 + Getting configuration descriptor #0 for device 4, reading 8 of 34 requested bytes, incomplete +At traffic-interleaved row 5: +- Incomplete control transfer on device 4 ++ Getting configuration descriptor #0 for device 4, reading 8 of 34 requested bytes, incomplete At traffic-transactions row 36: + IN transaction on 4.0 At traffic-packets row 89: @@ -777,6 +882,9 @@ Updating after 92 packets decoded At traffic-hierarchical row 27: - Incomplete control transfer on device 4 + Getting configuration descriptor #0 for device 4, reading 8 of 34 requested bytes, incomplete +At traffic-interleaved row 5: +- Incomplete control transfer on device 4 ++ Getting configuration descriptor #0 for device 4, reading 8 of 34 requested bytes, incomplete At traffic-transactions row 37: + IN transaction on 4.0 At traffic-packets row 91: @@ -797,6 +905,9 @@ Updating after 95 packets decoded At traffic-hierarchical row 27: - Incomplete control transfer on device 4 + Getting configuration descriptor #0 for device 4, reading 16 of 34 requested bytes, incomplete +At traffic-interleaved row 5: +- Incomplete control transfer on device 4 ++ Getting configuration descriptor #0 for device 4, reading 16 of 34 requested bytes, incomplete At traffic-transactions row 38: + IN transaction on 4.0 At traffic-packets row 94: @@ -811,6 +922,9 @@ Updating after 97 packets decoded At traffic-hierarchical row 27: - Incomplete control transfer on device 4 + Getting configuration descriptor #0 for device 4, reading 16 of 34 requested bytes, incomplete +At traffic-interleaved row 5: +- Incomplete control transfer on device 4 ++ Getting configuration descriptor #0 for device 4, reading 16 of 34 requested bytes, incomplete At traffic-transactions row 39: + IN transaction on 4.0 At traffic-packets row 96: @@ -825,6 +939,9 @@ Updating after 99 packets decoded At traffic-hierarchical row 27: - Incomplete control transfer on device 4 + Getting configuration descriptor #0 for device 4, reading 16 of 34 requested bytes, incomplete +At traffic-interleaved row 5: +- Incomplete control transfer on device 4 ++ Getting configuration descriptor #0 for device 4, reading 16 of 34 requested bytes, incomplete At traffic-transactions row 40: + IN transaction on 4.0 At traffic-packets row 98: @@ -845,6 +962,9 @@ Updating after 102 packets decoded At traffic-hierarchical row 27: - Incomplete control transfer on device 4 + Getting configuration descriptor #0 for device 4, reading 24 of 34 requested bytes, incomplete +At traffic-interleaved row 5: +- Incomplete control transfer on device 4 ++ Getting configuration descriptor #0 for device 4, reading 24 of 34 requested bytes, incomplete At traffic-transactions row 41: + IN transaction on 4.0 At traffic-packets row 101: @@ -859,6 +979,9 @@ Updating after 104 packets decoded At traffic-hierarchical row 27: - Incomplete control transfer on device 4 + Getting configuration descriptor #0 for device 4, reading 24 of 34 requested bytes, incomplete +At traffic-interleaved row 5: +- Incomplete control transfer on device 4 ++ Getting configuration descriptor #0 for device 4, reading 24 of 34 requested bytes, incomplete At traffic-transactions row 42: + IN transaction on 4.0 At traffic-packets row 103: @@ -873,6 +996,9 @@ Updating after 106 packets decoded At traffic-hierarchical row 27: - Incomplete control transfer on device 4 + Getting configuration descriptor #0 for device 4, reading 24 of 34 requested bytes, incomplete +At traffic-interleaved row 5: +- Incomplete control transfer on device 4 ++ Getting configuration descriptor #0 for device 4, reading 24 of 34 requested bytes, incomplete At traffic-transactions row 43: + IN transaction on 4.0 At traffic-packets row 105: @@ -893,6 +1019,9 @@ Updating after 109 packets decoded At traffic-hierarchical row 27: - Incomplete control transfer on device 4 + Getting configuration descriptor #0 for device 4, reading 32 of 34 requested bytes, incomplete +At traffic-interleaved row 5: +- Incomplete control transfer on device 4 ++ Getting configuration descriptor #0 for device 4, reading 32 of 34 requested bytes, incomplete At traffic-transactions row 44: + IN transaction on 4.0 At traffic-packets row 108: @@ -907,6 +1036,9 @@ Updating after 111 packets decoded At traffic-hierarchical row 27: - Incomplete control transfer on device 4 + Getting configuration descriptor #0 for device 4, reading 32 of 34 requested bytes, incomplete +At traffic-interleaved row 5: +- Incomplete control transfer on device 4 ++ Getting configuration descriptor #0 for device 4, reading 32 of 34 requested bytes, incomplete At traffic-transactions row 45: + IN transaction on 4.0 At traffic-packets row 110: @@ -927,6 +1059,9 @@ Updating after 114 packets decoded At traffic-hierarchical row 27: - Incomplete control transfer on device 4 + Getting configuration descriptor #0 for device 4, reading 34 bytes, incomplete +At traffic-interleaved row 5: +- Incomplete control transfer on device 4 ++ Getting configuration descriptor #0 for device 4, reading 34 bytes, incomplete At traffic-transactions row 46: + OUT transaction on 4.0 At traffic-packets row 113: @@ -949,6 +1084,8 @@ At devices row 0: Updating after 117 packets decoded At traffic-hierarchical row 28: + Incomplete control transfer on device 4 +At traffic-interleaved row 6: ++ Incomplete control transfer on device 4 At traffic-transactions row 47: + SETUP transaction on 4.0 At traffic-packets row 116: @@ -969,6 +1106,9 @@ Updating after 120 packets decoded At traffic-hierarchical row 28: - Incomplete control transfer on device 4 + Getting string descriptor #0 for device 4, reading 0 of 255 requested bytes, incomplete +At traffic-interleaved row 6: +- Incomplete control transfer on device 4 ++ Getting string descriptor #0 for device 4, reading 0 of 255 requested bytes, incomplete At traffic-transactions row 48: + IN transaction on 4.0 At traffic-packets row 119: @@ -983,6 +1123,9 @@ Updating after 122 packets decoded At traffic-hierarchical row 28: - Incomplete control transfer on device 4 + Getting string descriptor #0 for device 4, reading 0 of 255 requested bytes, incomplete +At traffic-interleaved row 6: +- Incomplete control transfer on device 4 ++ Getting string descriptor #0 for device 4, reading 0 of 255 requested bytes, incomplete At traffic-transactions row 49: + IN transaction on 4.0 At traffic-packets row 121: @@ -997,6 +1140,9 @@ Updating after 124 packets decoded At traffic-hierarchical row 28: - Incomplete control transfer on device 4 + Getting string descriptor #0 for device 4, reading 0 of 255 requested bytes, incomplete +At traffic-interleaved row 6: +- Incomplete control transfer on device 4 ++ Getting string descriptor #0 for device 4, reading 0 of 255 requested bytes, incomplete At traffic-transactions row 50: + IN transaction on 4.0 At traffic-packets row 123: @@ -1017,6 +1163,9 @@ Updating after 127 packets decoded At traffic-hierarchical row 28: - Incomplete control transfer on device 4 + Getting string descriptor #0 for device 4, reading 4 of 255 requested bytes, incomplete +At traffic-interleaved row 6: +- Incomplete control transfer on device 4 ++ Getting string descriptor #0 for device 4, reading 4 of 255 requested bytes, incomplete At traffic-transactions row 51: + OUT transaction on 4.0 At traffic-packets row 126: @@ -1039,6 +1188,8 @@ At devices row 0: Updating after 130 packets decoded At traffic-hierarchical row 29: + Incomplete control transfer on device 4 +At traffic-interleaved row 7: ++ Incomplete control transfer on device 4 At traffic-transactions row 52: + SETUP transaction on 4.0 At traffic-packets row 129: @@ -1059,6 +1210,9 @@ Updating after 133 packets decoded At traffic-hierarchical row 29: - Incomplete control transfer on device 4 + Getting string descriptor #2, language 0x0409 (English/US) for device 4, reading 0 of 255 requested bytes, incomplete +At traffic-interleaved row 7: +- Incomplete control transfer on device 4 ++ Getting string descriptor #2, language 0x0409 (English/US) for device 4, reading 0 of 255 requested bytes, incomplete At traffic-transactions row 53: + IN transaction on 4.0 At traffic-packets row 132: @@ -1073,6 +1227,9 @@ Updating after 135 packets decoded At traffic-hierarchical row 29: - Incomplete control transfer on device 4 + Getting string descriptor #2, language 0x0409 (English/US) for device 4, reading 0 of 255 requested bytes, incomplete +At traffic-interleaved row 7: +- Incomplete control transfer on device 4 ++ Getting string descriptor #2, language 0x0409 (English/US) for device 4, reading 0 of 255 requested bytes, incomplete At traffic-transactions row 54: + IN transaction on 4.0 At traffic-packets row 134: @@ -1087,6 +1244,9 @@ Updating after 137 packets decoded At traffic-hierarchical row 29: - Incomplete control transfer on device 4 + Getting string descriptor #2, language 0x0409 (English/US) for device 4, reading 0 of 255 requested bytes, incomplete +At traffic-interleaved row 7: +- Incomplete control transfer on device 4 ++ Getting string descriptor #2, language 0x0409 (English/US) for device 4, reading 0 of 255 requested bytes, incomplete At traffic-transactions row 55: + IN transaction on 4.0 At traffic-packets row 136: @@ -1107,6 +1267,9 @@ Updating after 140 packets decoded At traffic-hierarchical row 29: - Incomplete control transfer on device 4 + Getting string descriptor #2, language 0x0409 (English/US) for device 4, reading 8 of 255 requested bytes: 'USB', incomplete +At traffic-interleaved row 7: +- Incomplete control transfer on device 4 ++ Getting string descriptor #2, language 0x0409 (English/US) for device 4, reading 8 of 255 requested bytes: 'USB', incomplete At traffic-transactions row 56: + IN transaction on 4.0 At traffic-packets row 139: @@ -1127,6 +1290,9 @@ Updating after 143 packets decoded At traffic-hierarchical row 29: - Incomplete control transfer on device 4 + Getting string descriptor #2, language 0x0409 (English/US) for device 4, reading 16 of 255 requested bytes: 'USB Opt', incomplete +At traffic-interleaved row 7: +- Incomplete control transfer on device 4 ++ Getting string descriptor #2, language 0x0409 (English/US) for device 4, reading 16 of 255 requested bytes: 'USB Opt', incomplete At traffic-transactions row 57: + IN transaction on 4.0 At traffic-packets row 142: @@ -1141,6 +1307,9 @@ Updating after 145 packets decoded At traffic-hierarchical row 29: - Incomplete control transfer on device 4 + Getting string descriptor #2, language 0x0409 (English/US) for device 4, reading 16 of 255 requested bytes: 'USB Opt', incomplete +At traffic-interleaved row 7: +- Incomplete control transfer on device 4 ++ Getting string descriptor #2, language 0x0409 (English/US) for device 4, reading 16 of 255 requested bytes: 'USB Opt', incomplete At traffic-transactions row 58: + IN transaction on 4.0 At traffic-packets row 144: @@ -1155,6 +1324,9 @@ Updating after 147 packets decoded At traffic-hierarchical row 29: - Incomplete control transfer on device 4 + Getting string descriptor #2, language 0x0409 (English/US) for device 4, reading 16 of 255 requested bytes: 'USB Opt', incomplete +At traffic-interleaved row 7: +- Incomplete control transfer on device 4 ++ Getting string descriptor #2, language 0x0409 (English/US) for device 4, reading 16 of 255 requested bytes: 'USB Opt', incomplete At traffic-transactions row 59: + IN transaction on 4.0 At traffic-packets row 146: @@ -1175,6 +1347,9 @@ Updating after 150 packets decoded At traffic-hierarchical row 29: - Incomplete control transfer on device 4 + Getting string descriptor #2, language 0x0409 (English/US) for device 4, reading 24 of 255 requested bytes: 'USB Optical', incomplete +At traffic-interleaved row 7: +- Incomplete control transfer on device 4 ++ Getting string descriptor #2, language 0x0409 (English/US) for device 4, reading 24 of 255 requested bytes: 'USB Optical', incomplete At traffic-transactions row 60: + IN transaction on 4.0 At traffic-packets row 149: @@ -1189,6 +1364,9 @@ Updating after 152 packets decoded At traffic-hierarchical row 29: - Incomplete control transfer on device 4 + Getting string descriptor #2, language 0x0409 (English/US) for device 4, reading 24 of 255 requested bytes: 'USB Optical', incomplete +At traffic-interleaved row 7: +- Incomplete control transfer on device 4 ++ Getting string descriptor #2, language 0x0409 (English/US) for device 4, reading 24 of 255 requested bytes: 'USB Optical', incomplete At traffic-transactions row 61: + IN transaction on 4.0 At traffic-packets row 151: @@ -1203,6 +1381,9 @@ Updating after 154 packets decoded At traffic-hierarchical row 29: - Incomplete control transfer on device 4 + Getting string descriptor #2, language 0x0409 (English/US) for device 4, reading 24 of 255 requested bytes: 'USB Optical', incomplete +At traffic-interleaved row 7: +- Incomplete control transfer on device 4 ++ Getting string descriptor #2, language 0x0409 (English/US) for device 4, reading 24 of 255 requested bytes: 'USB Optical', incomplete At traffic-transactions row 62: + IN transaction on 4.0 At traffic-packets row 153: @@ -1223,6 +1404,9 @@ Updating after 157 packets decoded At traffic-hierarchical row 29: - Incomplete control transfer on device 4 + Getting string descriptor #2, language 0x0409 (English/US) for device 4, reading 32 of 255 requested bytes: 'USB Optical Mou', incomplete +At traffic-interleaved row 7: +- Incomplete control transfer on device 4 ++ Getting string descriptor #2, language 0x0409 (English/US) for device 4, reading 32 of 255 requested bytes: 'USB Optical Mou', incomplete At traffic-transactions row 63: + IN transaction on 4.0 At traffic-packets row 156: @@ -1237,6 +1421,9 @@ Updating after 159 packets decoded At traffic-hierarchical row 29: - Incomplete control transfer on device 4 + Getting string descriptor #2, language 0x0409 (English/US) for device 4, reading 32 of 255 requested bytes: 'USB Optical Mou', incomplete +At traffic-interleaved row 7: +- Incomplete control transfer on device 4 ++ Getting string descriptor #2, language 0x0409 (English/US) for device 4, reading 32 of 255 requested bytes: 'USB Optical Mou', incomplete At traffic-transactions row 64: + IN transaction on 4.0 At traffic-packets row 158: @@ -1257,6 +1444,9 @@ Updating after 162 packets decoded At traffic-hierarchical row 29: - Incomplete control transfer on device 4 + Getting string descriptor #2, language 0x0409 (English/US) for device 4, reading 36 of 255 requested bytes: 'USB Optical Mouse', incomplete +At traffic-interleaved row 7: +- Incomplete control transfer on device 4 ++ Getting string descriptor #2, language 0x0409 (English/US) for device 4, reading 36 of 255 requested bytes: 'USB Optical Mouse', incomplete At traffic-transactions row 65: + OUT transaction on 4.0 At traffic-packets row 161: diff --git a/tests/ui/split-poll-step/reference.txt b/tests/ui/split-poll-step/reference.txt index 85f63afa..0f1faa30 100644 --- a/tests/ui/split-poll-step/reference.txt +++ b/tests/ui/split-poll-step/reference.txt @@ -7,6 +7,8 @@ At traffic-packets row 0: Updating after 2 packets decoded At traffic-hierarchical row 0: + Polling 1 times for interrupt transfer on endpoint 14.1 IN +At traffic-interleaved row 0: ++ Polling 1 times for interrupt transfer on endpoint 14.1 IN At traffic-transactions row 0: - Starting low speed interrupt transaction on hub 12 port 2 + Starting IN transaction on 0.16 @@ -33,6 +35,8 @@ At traffic-hierarchical row 0: - Starting IN transaction on 14.1 + Polling 1 times for interrupt transfer on endpoint 14.1 IN + Starting IN transaction on 14.1 +At traffic-interleaved row 1: ++ Polling 1 times for interrupt transfer on endpoint 14.2 IN At traffic-transactions row 1: - Starting low speed interrupt transaction on hub 12 port 2 + Starting IN transaction on 0.16 @@ -90,6 +94,9 @@ At traffic-hierarchical row 0: + Polling 2 times for interrupt transfer on endpoint 14.1 IN At traffic-hierarchical row 3: + Starting IN transaction on 14.1 +At traffic-interleaved row 0: +- Polling 1 times for interrupt transfer on endpoint 14.1 IN ++ Polling 2 times for interrupt transfer on endpoint 14.1 IN At traffic-transactions row 4: - Starting low speed interrupt transaction on hub 12 port 2 + Starting IN transaction on 0.16 @@ -106,6 +113,9 @@ At traffic-hierarchical row 4: + Polling 2 times for interrupt transfer on endpoint 14.2 IN At traffic-hierarchical row 7: + Starting IN transaction on 14.2 +At traffic-interleaved row 1: +- Polling 1 times for interrupt transfer on endpoint 14.2 IN ++ Polling 2 times for interrupt transfer on endpoint 14.2 IN At traffic-transactions row 5: - Starting low speed interrupt transaction on hub 12 port 2 + Starting IN transaction on 0.16 @@ -128,6 +138,9 @@ At traffic-hierarchical row 0: + Polling 2 times for interrupt transfer on endpoint 14.1 IN At traffic-hierarchical row 4: + Completing IN transaction on 14.1, NAK +At traffic-interleaved row 0: +- Polling 1 times for interrupt transfer on endpoint 14.1 IN ++ Polling 2 times for interrupt transfer on endpoint 14.1 IN At traffic-transactions row 6: - Completing low speed interrupt transaction on hub 12 port 2 + Completing IN transaction on 0.16, NAK @@ -150,6 +163,9 @@ At traffic-hierarchical row 5: + Polling 2 times for interrupt transfer on endpoint 14.2 IN At traffic-hierarchical row 9: + Completing IN transaction on 14.2, NAK +At traffic-interleaved row 1: +- Polling 1 times for interrupt transfer on endpoint 14.2 IN ++ Polling 2 times for interrupt transfer on endpoint 14.2 IN At traffic-transactions row 7: - Completing low speed interrupt transaction on hub 12 port 2 + Completing IN transaction on 0.16, NAK @@ -166,6 +182,9 @@ At traffic-hierarchical row 0: + Polling 3 times for interrupt transfer on endpoint 14.1 IN At traffic-hierarchical row 5: + Starting IN transaction on 14.1 +At traffic-interleaved row 0: +- Polling 1 times for interrupt transfer on endpoint 14.1 IN ++ Polling 3 times for interrupt transfer on endpoint 14.1 IN At traffic-transactions row 8: - Starting low speed interrupt transaction on hub 12 port 2 + Starting IN transaction on 0.16 @@ -182,6 +201,9 @@ At traffic-hierarchical row 6: + Polling 3 times for interrupt transfer on endpoint 14.2 IN At traffic-hierarchical row 11: + Starting IN transaction on 14.2 +At traffic-interleaved row 1: +- Polling 1 times for interrupt transfer on endpoint 14.2 IN ++ Polling 3 times for interrupt transfer on endpoint 14.2 IN At traffic-transactions row 9: - Starting low speed interrupt transaction on hub 12 port 2 + Starting IN transaction on 0.16 @@ -204,6 +226,9 @@ At traffic-hierarchical row 0: + Polling 3 times for interrupt transfer on endpoint 14.1 IN At traffic-hierarchical row 6: + Completing IN transaction on 14.1, NAK +At traffic-interleaved row 0: +- Polling 1 times for interrupt transfer on endpoint 14.1 IN ++ Polling 3 times for interrupt transfer on endpoint 14.1 IN At traffic-transactions row 10: - Completing low speed interrupt transaction on hub 12 port 2 + Completing IN transaction on 0.16, NAK @@ -226,6 +251,9 @@ At traffic-hierarchical row 7: + Polling 3 times for interrupt transfer on endpoint 14.2 IN At traffic-hierarchical row 13: + Completing IN transaction on 14.2, NAK +At traffic-interleaved row 1: +- Polling 1 times for interrupt transfer on endpoint 14.2 IN ++ Polling 3 times for interrupt transfer on endpoint 14.2 IN At traffic-transactions row 11: - Completing low speed interrupt transaction on hub 12 port 2 + Completing IN transaction on 0.16, NAK @@ -242,6 +270,9 @@ At traffic-hierarchical row 0: + Polling 4 times for interrupt transfer on endpoint 14.1 IN At traffic-hierarchical row 7: + Starting IN transaction on 14.1 +At traffic-interleaved row 0: +- Polling 1 times for interrupt transfer on endpoint 14.1 IN ++ Polling 4 times for interrupt transfer on endpoint 14.1 IN At traffic-transactions row 12: - Starting low speed interrupt transaction on hub 12 port 2 + Starting IN transaction on 0.16 @@ -258,6 +289,9 @@ At traffic-hierarchical row 8: + Polling 4 times for interrupt transfer on endpoint 14.2 IN At traffic-hierarchical row 15: + Starting IN transaction on 14.2 +At traffic-interleaved row 1: +- Polling 1 times for interrupt transfer on endpoint 14.2 IN ++ Polling 4 times for interrupt transfer on endpoint 14.2 IN At traffic-transactions row 13: - Starting low speed interrupt transaction on hub 12 port 2 + Starting IN transaction on 0.16 @@ -280,6 +314,9 @@ At traffic-hierarchical row 0: + Polling 4 times for interrupt transfer on endpoint 14.1 IN At traffic-hierarchical row 8: + Completing IN transaction on 14.1, NAK +At traffic-interleaved row 0: +- Polling 1 times for interrupt transfer on endpoint 14.1 IN ++ Polling 4 times for interrupt transfer on endpoint 14.1 IN At traffic-transactions row 14: - Completing low speed interrupt transaction on hub 12 port 2 + Completing IN transaction on 0.16, NAK @@ -302,6 +339,9 @@ At traffic-hierarchical row 9: + Polling 4 times for interrupt transfer on endpoint 14.2 IN At traffic-hierarchical row 17: + Completing IN transaction on 14.2, NAK +At traffic-interleaved row 1: +- Polling 1 times for interrupt transfer on endpoint 14.2 IN ++ Polling 4 times for interrupt transfer on endpoint 14.2 IN At traffic-transactions row 15: - Completing low speed interrupt transaction on hub 12 port 2 + Completing IN transaction on 0.16, NAK