|
10 | 10 | //! you need specific behavior, it may be best to defensively copy, paste, and maintain the |
11 | 11 | //! specific behavior you require. |
12 | 12 |
|
13 | | -use std::cmp::Ordering; |
14 | 13 | use std::collections::VecDeque; |
15 | | -use timely::container::{ContainerBuilder, DrainContainer, PushInto}; |
| 14 | +use columnation::Columnation; |
| 15 | +use timely::container::{ContainerBuilder, PushInto}; |
16 | 16 | use crate::Data; |
17 | | -use crate::difference::{IsZero, Semigroup}; |
| 17 | +use crate::difference::Semigroup; |
18 | 18 |
|
19 | 19 | /// Sorts and consolidates `vec`. |
20 | 20 | /// |
@@ -232,115 +232,63 @@ where |
232 | 232 | } |
233 | 233 | } |
234 | 234 |
|
235 | | -/// Layout of containers and their read items to be consolidated. |
| 235 | +/// A container that can sort and consolidate its contents internally. |
236 | 236 | /// |
237 | | -/// This trait specifies behavior to extract keys and diffs from container's read |
238 | | -/// items. Consolidation accumulates the diffs per key. |
| 237 | +/// The container knows its own layout — how to sort its elements, how to |
| 238 | +/// compare adjacent entries, and how to merge diffs. The caller provides |
| 239 | +/// a `target` container to receive the consolidated output, allowing |
| 240 | +/// reuse of allocations across calls. |
239 | 241 | /// |
240 | | -/// The trait requires `Container` to have access to its `Item` GAT. |
241 | | -pub trait ConsolidateLayout: DrainContainer { |
242 | | - /// Key portion of data, essentially everything minus the diff |
243 | | - type Key<'a>: Eq where Self: 'a; |
244 | | - |
245 | | - /// GAT diff type. |
246 | | - type Diff<'a>; |
247 | | - |
248 | | - /// Owned diff type. |
249 | | - type DiffOwned: for<'a> Semigroup<Self::Diff<'a>>; |
250 | | - |
251 | | - /// Converts a reference diff into an owned diff. |
252 | | - fn owned_diff(diff: Self::Diff<'_>) -> Self::DiffOwned; |
253 | | - |
254 | | - /// Deconstruct an item into key and diff. Must be cheap. |
255 | | - fn into_parts(item: Self::Item<'_>) -> (Self::Key<'_>, Self::Diff<'_>); |
256 | | - |
257 | | - /// Push an element to a compatible container. |
258 | | - /// |
259 | | - /// This function is odd to have, so let's explain why it exists. Ideally, the container |
260 | | - /// would accept a `(key, diff)` pair and we wouldn't need this function. However, we |
261 | | - /// might never be in a position where this is true: Vectors can push any `T`, which would |
262 | | - /// collide with a specific implementation for pushing tuples of mixes GATs and owned types. |
263 | | - /// |
264 | | - /// For this reason, we expose a function here that takes a GAT key and an owned diff, and |
265 | | - /// leave it to the implementation to "patch" a suitable item that can be pushed into `self`. |
266 | | - fn push_with_diff(&mut self, key: Self::Key<'_>, diff: Self::DiffOwned); |
267 | | - |
268 | | - /// Compare two items by key to sort containers. |
269 | | - fn cmp(item1: &Self::Item<'_>, item2: &Self::Item<'_>) -> Ordering; |
270 | | - |
271 | | - /// Returns the number of items in the container. |
| 242 | +/// After the call, `target` contains the sorted, consolidated data and |
| 243 | +/// `self` may be empty or in an unspecified state (implementations should |
| 244 | +/// document this). |
| 245 | +pub trait Consolidate { |
| 246 | + /// The number of elements in the container. |
272 | 247 | fn len(&self) -> usize; |
273 | | - |
274 | | - /// Clear the container. Afterwards, `len()` should return 0. |
| 248 | + /// Clear the container. |
275 | 249 | fn clear(&mut self); |
| 250 | + /// Sort and consolidate `self` into `target`. |
| 251 | + fn consolidate_into(&mut self, target: &mut Self); |
| 252 | +} |
276 | 253 |
|
277 | | - /// Consolidate the supplied container. |
| 254 | +impl<D: Ord, T: Ord, R: Semigroup> Consolidate for Vec<(D, T, R)> { |
| 255 | + fn len(&self) -> usize { Vec::len(self) } |
| 256 | + fn clear(&mut self) { Vec::clear(self) } |
278 | 257 | fn consolidate_into(&mut self, target: &mut Self) { |
279 | | - // Sort input data |
280 | | - let mut permutation = Vec::with_capacity(self.len()); |
281 | | - permutation.extend(self.drain()); |
282 | | - permutation.sort_by(|a, b| Self::cmp(a, b)); |
283 | | - |
284 | | - // Iterate over the data, accumulating diffs for like keys. |
285 | | - let mut iter = permutation.drain(..); |
286 | | - if let Some(item) = iter.next() { |
287 | | - |
288 | | - let (k, d) = Self::into_parts(item); |
289 | | - let mut prev_key = k; |
290 | | - let mut prev_diff = Self::owned_diff(d); |
291 | | - |
292 | | - for item in iter { |
293 | | - let (next_key, next_diff) = Self::into_parts(item); |
294 | | - if next_key == prev_key { |
295 | | - prev_diff.plus_equals(&next_diff); |
296 | | - } |
297 | | - else { |
298 | | - if !prev_diff.is_zero() { |
299 | | - target.push_with_diff(prev_key, prev_diff); |
300 | | - } |
301 | | - prev_key = next_key; |
302 | | - prev_diff = Self::owned_diff(next_diff); |
303 | | - } |
304 | | - } |
305 | | - |
306 | | - if !prev_diff.is_zero() { |
307 | | - target.push_with_diff(prev_key, prev_diff); |
308 | | - } |
309 | | - } |
| 258 | + consolidate_updates(self); |
| 259 | + std::mem::swap(self, target); |
310 | 260 | } |
311 | 261 | } |
312 | 262 |
|
313 | | -impl<D, T, R> ConsolidateLayout for Vec<(D, T, R)> |
314 | | -where |
315 | | - D: Ord + Clone + 'static, |
316 | | - T: Ord + Clone + 'static, |
317 | | - R: Semigroup + Clone + 'static, |
318 | | -{ |
319 | | - type Key<'a> = (D, T) where Self: 'a; |
320 | | - type Diff<'a> = R where Self: 'a; |
321 | | - type DiffOwned = R; |
322 | | - |
323 | | - fn owned_diff(diff: Self::Diff<'_>) -> Self::DiffOwned { diff } |
324 | | - |
325 | | - fn into_parts((data, time, diff): Self::Item<'_>) -> (Self::Key<'_>, Self::Diff<'_>) { |
326 | | - ((data, time), diff) |
327 | | - } |
328 | | - |
329 | | - fn cmp<'a>(item1: &Self::Item<'_>, item2: &Self::Item<'_>) -> Ordering { |
330 | | - (&item1.0, &item1.1).cmp(&(&item2.0, &item2.1)) |
331 | | - } |
332 | | - |
333 | | - fn push_with_diff(&mut self, (data, time): Self::Key<'_>, diff: Self::DiffOwned) { |
334 | | - self.push((data, time, diff)); |
335 | | - } |
336 | | - |
337 | | - #[inline] fn len(&self) -> usize { Vec::len(self) } |
338 | | - #[inline] fn clear(&mut self) { Vec::clear(self) } |
339 | | - |
340 | | - /// Consolidate the supplied container. |
| 263 | +impl<D: Ord + Columnation, T: Ord + Columnation, R: Semigroup + Columnation> Consolidate for crate::containers::TimelyStack<(D, T, R)> { |
| 264 | + fn len(&self) -> usize { self[..].len() } |
| 265 | + fn clear(&mut self) { crate::containers::TimelyStack::clear(self) } |
341 | 266 | fn consolidate_into(&mut self, target: &mut Self) { |
342 | | - consolidate_updates(self); |
343 | | - std::mem::swap(self, target); |
| 267 | + let len = self[..].len(); |
| 268 | + let mut indices: Vec<usize> = (0..len).collect(); |
| 269 | + indices.sort_unstable_by(|&i, &j| { |
| 270 | + let (d1, t1, _) = &self[i]; |
| 271 | + let (d2, t2, _) = &self[j]; |
| 272 | + (d1, t1).cmp(&(d2, t2)) |
| 273 | + }); |
| 274 | + target.clear(); |
| 275 | + let mut idx = 0; |
| 276 | + while idx < indices.len() { |
| 277 | + let (d, t, r) = &self[indices[idx]]; |
| 278 | + let mut r_owned = r.clone(); |
| 279 | + idx += 1; |
| 280 | + while idx < indices.len() { |
| 281 | + let (d2, t2, r2) = &self[indices[idx]]; |
| 282 | + if d == d2 && t == t2 { |
| 283 | + r_owned.plus_equals(r2); |
| 284 | + idx += 1; |
| 285 | + } else { break; } |
| 286 | + } |
| 287 | + if !r_owned.is_zero() { |
| 288 | + target.copy_destructured(d, t, &r_owned); |
| 289 | + } |
| 290 | + } |
| 291 | + self.clear(); |
344 | 292 | } |
345 | 293 | } |
346 | 294 |
|
|
0 commit comments