diff --git a/src/capture.rs b/src/capture.rs index a3bf48af..c2641e98 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 { @@ -225,6 +231,7 @@ pub enum TrafficItem { #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] pub enum TrafficViewMode { Hierarchical, + Interleaved, Transactions, Packets, } @@ -236,6 +243,7 @@ impl TrafficViewMode { use TrafficViewMode::*; match self { Hierarchical => "Hierarchical", + Interleaved => "Interleaved", Transactions => "Transactions", Packets => "Packets", } @@ -246,6 +254,7 @@ impl TrafficViewMode { use TrafficViewMode::*; match self { Hierarchical => "traffic-hierarchical", + Interleaved => "traffic-interleaved", Transactions => "traffic-transactions", Packets => "traffic-packets", } @@ -256,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}'") @@ -1363,6 +1373,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 { @@ -1394,19 +1443,34 @@ 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, } } } +pub enum SearchResult { + TopLevelItem(u64, Item), + NextLevelItem(u64, u64, u64, Item), +} + pub trait ItemSource { fn item(&mut self, parent: Option<&Item>, @@ -1421,6 +1485,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) @@ -1432,6 +1512,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>, @@ -1443,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) @@ -1495,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(), }) @@ -1507,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( @@ -1526,6 +1623,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 { @@ -2187,6 +2535,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 { diff --git a/src/decoder.rs b/src/decoder.rs index 6ea658e6..b55739d8 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,11 +1025,29 @@ 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)?; 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) 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) } 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