Skip to main content

timely/dataflow/operators/
capability.rs

1//! Capabilities to send data from operators
2//!
3//! Timely dataflow operators are only able to send data if they possess a "capability",
4//! a system-created object which warns the runtime that the operator may still produce
5//! output records.
6//!
7//! The timely dataflow runtime creates a capability and provides it to an operator whenever
8//! the operator receives input data. The capabilities allow the operator to respond to the
9//! received data, immediately or in the future, for as long as the capability is held.
10//!
11//! Timely dataflow's progress tracking infrastructure communicates the number of outstanding
12//! capabilities across all workers.
13//! Each operator may hold on to its capabilities, and may clone, advance, and drop them.
14//! Each of these actions informs the timely dataflow runtime of changes to the number of outstanding
15//! capabilities, so that the runtime can notice when the count for some capability reaches zero.
16//! While an operator can hold capabilities indefinitely, and create as many new copies of them
17//! as it would like, the progress tracking infrastructure will not move forward until the
18//! operators eventually release their capabilities.
19//!
20//! Note that these capabilities currently lack the property of "transferability":
21//! An operator should not hand its capabilities to some other operator. In the future, we should
22//! probably bind capabilities more strongly to a specific operator and output.
23
24use std::{borrow, error::Error, fmt::Display, ops::Deref};
25use std::rc::Rc;
26use std::cell::{OnceCell, RefCell};
27use std::fmt::{self, Debug};
28
29use crate::order::{PartialOrder, TotalOrder};
30use crate::progress::Timestamp;
31use crate::progress::{ChangeBatch, Stamp};
32use crate::progress::operate::PortConnectivity;
33use crate::scheduling::Activations;
34use crate::dataflow::channels::pullers::counter::ConsumedGuard;
35
36/// An internal trait expressing the capability to send messages with given timestamps.
37pub trait CapabilityTrait<T: Timestamp> {
38    /// The stamp of timestamps to attach to messages sent with this capability.
39    ///
40    /// Messages may only result in downstream work at times greater or equal to
41    /// some element of the stamp. An empty stamp makes no progress claims, and
42    /// such messages may be delivered after downstream frontiers have advanced
43    /// past all times in their contents.
44    fn stamp(&self) -> Stamp<T>;
45    /// Validates that the capability is valid for a specific internal buffer and output port.
46    fn valid_for_output(&self, query_buffer: &Rc<RefCell<ChangeBatch<T>>>, port: usize) -> bool;
47}
48
49impl<T: Timestamp, C: CapabilityTrait<T>> CapabilityTrait<T> for &C {
50    fn stamp(&self) -> Stamp<T> { (**self).stamp() }
51    fn valid_for_output(&self, query_buffer: &Rc<RefCell<ChangeBatch<T>>>, port: usize) -> bool {
52        (**self).valid_for_output(query_buffer, port)
53    }
54}
55impl<T: Timestamp, C: CapabilityTrait<T>> CapabilityTrait<T> for &mut C {
56    fn stamp(&self) -> Stamp<T> { (**self).stamp() }
57    fn valid_for_output(&self, query_buffer: &Rc<RefCell<ChangeBatch<T>>>, port: usize) -> bool {
58        (**self).valid_for_output(query_buffer, port)
59    }
60}
61
62/// The capability to send data with a certain timestamp on a dataflow edge.
63///
64/// Capabilities are used by timely dataflow's progress tracking machinery to restrict and track
65/// when user code retains the ability to send messages on dataflow edges. All capabilities are
66/// constructed by the system, and should eventually be dropped by the user. Failure to drop
67/// a capability (for whatever reason) will cause timely dataflow's progress tracking to stall.
68pub struct Capability<T: Timestamp> {
69    time: T,
70    internal: Rc<RefCell<ChangeBatch<T>>>,
71}
72
73impl<T: Timestamp> CapabilityTrait<T> for Capability<T> {
74    fn stamp(&self) -> Stamp<T> { Stamp::from_elem(self.time.clone()) }
75    fn valid_for_output(&self, query_buffer: &Rc<RefCell<ChangeBatch<T>>>, _port: usize) -> bool {
76        Rc::ptr_eq(&self.internal, query_buffer)
77    }
78}
79
80impl<T: Timestamp> Capability<T> {
81    /// Creates a new capability at `time` while incrementing (and keeping a reference to) the provided
82    /// [`ChangeBatch`].
83    pub(crate) fn new(time: T, internal: Rc<RefCell<ChangeBatch<T>>>) -> Self {
84        internal.borrow_mut().update(time.clone(), 1);
85
86        Self {
87            time,
88            internal,
89        }
90    }
91
92    /// The timestamp associated with this capability.
93    pub fn time(&self) -> &T {
94        &self.time
95    }
96
97    /// Makes a new capability for a timestamp `new_time` greater or equal to the timestamp of
98    /// the source capability (`self`).
99    ///
100    /// This method panics if `self.time` is not less or equal to `new_time`.
101    pub fn delayed(&self, new_time: &T) -> Capability<T> {
102        /// Makes the panic branch cold & outlined to decrease code bloat & give
103        /// the inner function the best chance possible of being inlined with
104        /// minimal code bloat
105        #[cold]
106        #[inline(never)]
107        fn delayed_panic(capability: &dyn Debug, invalid_time: &dyn Debug) -> ! {
108            // Formatting & panic machinery is relatively expensive in terms of code bloat, so
109            // we outline it
110            panic!(
111                "Attempted to delay {:?} to {:?}, which is not beyond the capability's time.",
112                capability,
113                invalid_time,
114            )
115        }
116
117        self.try_delayed(new_time)
118            .unwrap_or_else(|| delayed_panic(self, new_time))
119    }
120
121    /// Attempts to make a new capability for a timestamp `new_time` that is
122    /// greater or equal to the timestamp of the source capability (`self`).
123    ///
124    /// Returns [`None`] `self.time` is not less or equal to `new_time`.
125    pub fn try_delayed(&self, new_time: &T) -> Option<Capability<T>> {
126        if self.time.less_equal(new_time) {
127            Some(Self::new(new_time.clone(), Rc::clone(&self.internal)))
128        } else {
129            None
130        }
131    }
132
133    /// Downgrades the capability to one corresponding to `new_time`.
134    ///
135    /// This method panics if `self.time` is not less or equal to `new_time`.
136    pub fn downgrade(&mut self, new_time: &T) {
137        /// Makes the panic branch cold & outlined to decrease code bloat & give
138        /// the inner function the best chance possible of being inlined with
139        /// minimal code bloat
140        #[cold]
141        #[inline(never)]
142        fn downgrade_panic(capability: &dyn Debug, invalid_time: &dyn Debug) -> ! {
143            // Formatting & panic machinery is relatively expensive in terms of code bloat, so
144            // we outline it
145            panic!(
146                "Attempted to downgrade {:?} to {:?}, which is not beyond the capability's time.",
147                capability,
148                invalid_time,
149            )
150        }
151
152        self.try_downgrade(new_time)
153            .unwrap_or_else(|_| downgrade_panic(self, new_time))
154    }
155
156    /// Attempts to downgrade the capability to one corresponding to `new_time`.
157    ///
158    /// Returns a [DowngradeError] if `self.time` is not less or equal to `new_time`.
159    pub fn try_downgrade(&mut self, new_time: &T) -> Result<(), DowngradeError> {
160        if let Some(new_capability) = self.try_delayed(new_time) {
161            *self = new_capability;
162            Ok(())
163        } else {
164            Err(DowngradeError(()))
165        }
166    }
167}
168
169// Necessary for correctness. When a capability is dropped, the "internal" `ChangeBatch` needs to be
170// updated accordingly to inform the rest of the system that the operator has released its permit
171// to send data and request notification at the associated timestamp.
172impl<T: Timestamp> Drop for Capability<T> {
173    fn drop(&mut self) {
174        let time = ::std::mem::replace(&mut self.time, T::minimum());
175        self.internal.borrow_mut().update(time, -1);
176    }
177}
178
179impl<T: Timestamp> Clone for Capability<T> {
180    fn clone(&self) -> Capability<T> {
181        Self::new(self.time.clone(), Rc::clone(&self.internal))
182    }
183}
184
185impl<T: Timestamp> Deref for Capability<T> {
186    type Target = T;
187
188    fn deref(&self) -> &T {
189        &self.time
190    }
191}
192
193impl<T: Timestamp> Debug for Capability<T> {
194    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
195        f.debug_struct("Capability")
196            .field("time", &self.time)
197            .field("internal", &"...")
198            .finish()
199    }
200}
201
202impl<T: Timestamp> PartialEq for Capability<T> {
203    fn eq(&self, other: &Self) -> bool {
204        self.time() == other.time() && Rc::ptr_eq(&self.internal, &other.internal)
205    }
206}
207impl<T: Timestamp> Eq for Capability<T> { }
208
209impl<T: Timestamp> PartialOrder for Capability<T> {
210    fn less_equal(&self, other: &Self) -> bool {
211        self.time().less_equal(other.time()) && Rc::ptr_eq(&self.internal, &other.internal)
212    }
213}
214
215impl<T: Timestamp+::std::hash::Hash> ::std::hash::Hash for Capability<T> {
216    fn hash<H: ::std::hash::Hasher>(&self, state: &mut H) {
217        self.time.hash(state);
218    }
219}
220
221/// An error produced when trying to downgrade a capability with a time
222/// that's not less than or equal to the current capability
223#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
224pub struct DowngradeError(());
225
226impl Display for DowngradeError {
227    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
228        f.write_str("could not downgrade the given capability")
229    }
230}
231
232impl Error for DowngradeError {}
233
234/// A shared list of shared output capability buffers.
235type CapabilityUpdates<T> = Rc<RefCell<Vec<Rc<RefCell<ChangeBatch<T>>>>>>;
236
237/// An capability of an input port.
238///
239/// Holding onto this capability will implicitly hold on to a capability for all the outputs
240/// ports this input is connected to, after the connection summaries have been applied.
241///
242/// This input capability supplies a `retain_for_output(self)` method which consumes the input
243/// capability and turns it into a [Capability] for a specific output port.
244pub struct InputCapability<T: Timestamp> {
245    /// Output capability buffers, for use in minting capabilities.
246    internal: CapabilityUpdates<T>,
247    /// Timestamp summaries for each output.
248    summaries: Rc<OnceCell<PortConnectivity<T::Summary>>>,
249    /// A drop guard that updates the consumed capability this InputCapability refers to on drop
250    consumed_guard: ConsumedGuard<T>,
251}
252
253impl<T: Timestamp> CapabilityTrait<T> for InputCapability<T> {
254    fn stamp(&self) -> Stamp<T> { self.stamp().clone() }
255    fn valid_for_output(&self, query_buffer: &Rc<RefCell<ChangeBatch<T>>>, port: usize) -> bool {
256        let summaries_borrow = self.summaries.get().expect("connectivity frozen at operator build");
257        let internal_borrow = self.internal.borrow();
258        // To be valid, the output buffer must match and the timestamp summary needs to be the default.
259        Rc::ptr_eq(&internal_borrow[port], query_buffer) &&
260        summaries_borrow.get(port).map_or(false, |path| path.elements() == [Default::default()])
261    }
262}
263
264impl<T: Timestamp> InputCapability<T> {
265    /// Creates a new capability reference at `time` while incrementing (and keeping a reference to)
266    /// the provided [`ChangeBatch`].
267    pub(crate) fn new(internal: CapabilityUpdates<T>, summaries: Rc<OnceCell<PortConnectivity<T::Summary>>>, guard: ConsumedGuard<T>) -> Self {
268        InputCapability {
269            internal,
270            summaries,
271            consumed_guard: guard,
272        }
273    }
274
275    /// The stamp of timestamps associated with the received message.
276    #[inline]
277    pub fn stamp(&self) -> &Stamp<T> {
278        self.consumed_guard.stamp()
279    }
280
281    /// The least timestamp of the message's stamp, if the stamp is non-empty.
282    ///
283    /// This method exists only for totally ordered timestamps, where every non-empty stamp
284    /// has a least element: the time at which the message may first result in downstream
285    /// work, and the time at which to hold a capability for it. Partially ordered timestamps
286    /// have no such element; operators over them must read the whole
287    /// [`InputCapability::stamp`] and forward each of its elements.
288    ///
289    /// Messages sent under no capabilities have an empty stamp and no least element. An
290    /// operator that needs a time should `if let Some(time) = cap.least()` and drop such
291    /// messages otherwise; they make no progress claims, and may arrive after the frontier
292    /// has passed every time in their contents.
293    #[inline]
294    pub fn least(&self) -> Option<&T> where T: TotalOrder {
295        self.stamp().least()
296    }
297
298    /// Delays capability for a specific output port.
299    ///
300    /// Makes a new capability for a timestamp `new_time` greater or equal to some element
301    /// of the stamp of the source capability (`self`).
302    ///
303    /// This method panics if no element of `self.stamp()` is less or equal to `new_time`.
304    pub fn delayed(&self, new_time: &T, output_port: usize) -> Capability<T> {
305        use crate::progress::timestamp::PathSummary;
306        if let Some(path) = self.summaries.get().expect("connectivity frozen at operator build").get(output_port) {
307            if self.stamp().iter().flat_map(|elem| path.iter().flat_map(move |summary| summary.results_in(elem))).any(|time| time.less_equal(new_time)) {
308                Capability::new(new_time.clone(), Rc::clone(&self.internal.borrow()[output_port]))
309            } else {
310                panic!("Attempted to delay to a time ({:?}) not greater or equal to the operators input-output summary ({:?}) applied to any element of the capability's stamp ({:?})", new_time, path, self.stamp());
311            }
312        }
313        else {
314            panic!("Attempted to delay a capability for a disconnected output");
315        }
316    }
317
318    /// Transforms to an owned capability for a specific output port.
319    ///
320    /// This method produces an owned capability which must be dropped to release the
321    /// capability. Users should take care that these capabilities are only stored for
322    /// as long as they are required, as failing to drop them may result in livelock.
323    ///
324    /// The capability is for the least element of the stamp, and so exists only for totally
325    /// ordered timestamps; partially ordered timestamps must be retained with
326    /// [`InputCapability::retain_stamp`], which holds a capability for each element. An
327    /// empty stamp yields no capability.
328    ///
329    /// This method panics if the timestamp summary to `output_port` strictly advances the time.
330    #[inline]
331    pub fn retain_least(&self, output_port: usize) -> Option<Capability<T>> where T: TotalOrder {
332        self.least().map(|time| self.delayed(time, output_port))
333    }
334
335    /// Transforms to an owned capability set for a specific output port, with one
336    /// capability for each element of the message's stamp.
337    ///
338    /// An empty stamp produces an empty capability set. Data sent through a session
339    /// keyed by an empty capability set makes no progress claims, and may be delivered
340    /// after downstream frontiers have advanced past all times in its contents.
341    ///
342    /// This method panics if the timestamp summary to `output_port` strictly advances
343    /// any element of the stamp.
344    pub fn retain_stamp(&self, output_port: usize) -> CapabilitySet<T> {
345        self.stamp().iter().map(|time| self.delayed(time, output_port)).collect()
346    }
347}
348
349impl<T: Timestamp> Debug for InputCapability<T> {
350    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
351        f.debug_struct("InputCapability")
352            .field("stamp", self.stamp())
353            .field("internal", &"...")
354            .finish()
355    }
356}
357
358/// Capability that activates on drop or downgrade.
359#[derive(Clone, Debug)]
360pub struct ActivateCapability<T: Timestamp> {
361    pub(crate) capability: Capability<T>,
362    pub(crate) address: Rc<[usize]>,
363    pub(crate) activations: Rc<RefCell<Activations>>,
364}
365
366impl<T: Timestamp> CapabilityTrait<T> for ActivateCapability<T> {
367    fn stamp(&self) -> Stamp<T> { self.capability.stamp() }
368    fn valid_for_output(&self, query_buffer: &Rc<RefCell<ChangeBatch<T>>>, port: usize) -> bool {
369        self.capability.valid_for_output(query_buffer, port)
370    }
371}
372
373impl<T: Timestamp> ActivateCapability<T> {
374    /// Creates a new activating capability.
375    pub fn new(capability: Capability<T>, address: Rc<[usize]>, activations: Rc<RefCell<Activations>>) -> Self {
376        Self {
377            capability,
378            address,
379            activations,
380        }
381    }
382
383    /// The timestamp associated with this capability.
384    pub fn time(&self) -> &T {
385        self.capability.time()
386    }
387
388    /// Creates a new delayed capability.
389    pub fn delayed(&self, time: &T) -> Self {
390        ActivateCapability {
391            capability: self.capability.delayed(time),
392            address: Rc::clone(&self.address),
393            activations: Rc::clone(&self.activations),
394        }
395    }
396
397    /// Downgrades this capability.
398    pub fn downgrade(&mut self, time: &T) {
399        self.capability.downgrade(time);
400        self.activations.borrow_mut().activate(&self.address);
401    }
402}
403
404impl<T: Timestamp> Drop for ActivateCapability<T> {
405    fn drop(&mut self) {
406        self.activations.borrow_mut().activate(&self.address);
407    }
408}
409
410/// A set of capabilities, for possibly incomparable times.
411#[derive(Clone, Debug)]
412pub struct CapabilitySet<T: Timestamp> {
413    elements: Vec<Capability<T>>,
414}
415
416impl<T: Timestamp> CapabilitySet<T> {
417
418    /// Allocates an empty capability set.
419    pub fn new() -> Self {
420        Self { elements: Vec::new() }
421    }
422
423    /// Allocates an empty capability set with space for `capacity` elements
424    pub fn with_capacity(capacity: usize) -> Self {
425        Self { elements: Vec::with_capacity(capacity) }
426    }
427
428    /// Allocates a capability set containing a single capability.
429    ///
430    /// # Examples
431    /// ```
432    /// use std::collections::HashMap;
433    /// use timely::dataflow::{
434    ///     operators::{ToStream, generic::Operator},
435    ///     channels::pact::Pipeline,
436    /// };
437    /// use timely::dataflow::operators::CapabilitySet;
438    ///
439    /// timely::example(|scope| {
440    ///     vec![()]
441    ///         .into_iter()
442    ///         .to_stream(scope)
443    ///         .container::<Vec<_>>()
444    ///         .unary_frontier(Pipeline, "example", |default_cap, _info| {
445    ///             let mut cap = CapabilitySet::from_elem(default_cap);
446    ///             move |(input, frontier), output| {
447    ///                 cap.downgrade(&frontier.frontier());
448    ///                 input.for_each_stamp(|cap, data| {});
449    ///                 let a_cap = cap.first();
450    ///                 if let Some(a_cap) = a_cap.as_ref() {
451    ///                     output.session(a_cap).give(());
452    ///                 }
453    ///             }
454    ///         })
455    ///         .container::<Vec<_>>();
456    /// });
457    /// ```
458    pub fn from_elem(cap: Capability<T>) -> Self {
459        Self { elements: vec![cap] }
460    }
461
462    /// Inserts `capability` into the set, discarding redundant capabilities.
463    pub fn insert(&mut self, capability: Capability<T>) {
464        if !self.elements.iter().any(|c| c.less_equal(&capability)) {
465            self.elements.retain(|c| !capability.less_equal(c));
466            self.elements.push(capability);
467        }
468    }
469
470    /// Creates a new capability to send data at `time`.
471    ///
472    /// This method panics if there does not exist a capability in `self.elements` less or equal to `time`.
473    pub fn delayed(&self, time: &T) -> Capability<T> {
474        /// Makes the panic branch cold & outlined to decrease code bloat & give
475        /// the inner function the best chance possible of being inlined with
476        /// minimal code bloat
477        #[cold]
478        #[inline(never)]
479        fn delayed_panic(invalid_time: &dyn Debug) -> ! {
480            // Formatting & panic machinery is relatively expensive in terms of code bloat, so
481            // we outline it
482            panic!(
483                "failed to create a delayed capability, the current set does not \
484                have an element less than or equal to {:?}",
485                invalid_time,
486            )
487        }
488
489        self.try_delayed(time)
490            .unwrap_or_else(|| delayed_panic(time))
491    }
492
493    /// Attempts to create a new capability to send data at `time`.
494    ///
495    /// Returns [`None`] if there does not exist a capability in `self.elements` less or equal to `time`.
496    pub fn try_delayed(&self, time: &T) -> Option<Capability<T>> {
497        self.elements
498            .iter()
499            .find(|capability| capability.time().less_equal(time))
500            .and_then(|capability| capability.try_delayed(time))
501    }
502
503    /// Downgrades the set of capabilities to correspond with the times in `frontier`.
504    ///
505    /// This method panics if any element of `frontier` is not greater or equal to some element of `self.elements`.
506    pub fn downgrade<B, F>(&mut self, frontier: F)
507    where
508        B: borrow::Borrow<T>,
509        F: IntoIterator<Item = B>,
510    {
511        /// Makes the panic branch cold & outlined to decrease code bloat & give
512        /// the inner function the best chance possible of being inlined with
513        /// minimal code bloat
514        #[cold]
515        #[inline(never)]
516        fn downgrade_panic() -> ! {
517            // Formatting & panic machinery is relatively expensive in terms of code bloat, so
518            // we outline it
519            panic!(
520                "Attempted to downgrade a CapabilitySet with a frontier containing an element \
521                that was not beyond an element within the set"
522            )
523        }
524
525        self.try_downgrade(frontier)
526            .unwrap_or_else(|_| downgrade_panic())
527    }
528
529    /// Attempts to downgrade the set of capabilities to correspond with the times in `frontier`.
530    ///
531    /// Returns [`None`] if any element of `frontier` is not greater or equal to some element of `self.elements`.
532    ///
533    /// **Warning**: If an error is returned the capability set may be in an inconsistent state and can easily
534    /// cause logic errors within the program if not properly handled.
535    ///
536    pub fn try_downgrade<B, F>(&mut self, frontier: F) -> Result<(), DowngradeError>
537    where
538        B: borrow::Borrow<T>,
539        F: IntoIterator<Item = B>,
540    {
541        let count = self.elements.len();
542        for time in frontier.into_iter() {
543            let capability = self.try_delayed(time.borrow()).ok_or(DowngradeError(()))?;
544            self.elements.push(capability);
545        }
546        self.elements.drain(..count);
547
548        Ok(())
549    }
550}
551
552impl<T: Timestamp> CapabilityTrait<T> for CapabilitySet<T> {
553    /// The stamp of the set's timestamps.
554    ///
555    /// An empty set stamps messages with an empty stamp: such messages make no
556    /// progress claims, are invisible to progress tracking, and may be delivered
557    /// after downstream frontiers have advanced past all times in their contents.
558    fn stamp(&self) -> Stamp<T> {
559        self.elements.iter().map(|capability| capability.time().clone()).collect()
560    }
561    /// Valid iff every member capability is valid for the output.
562    ///
563    /// An empty set is vacuously valid for any output: constructing an empty set
564    /// and opening a session with it is the deliberate mechanism for sending
565    /// messages that make no progress claims.
566    fn valid_for_output(&self, query_buffer: &Rc<RefCell<ChangeBatch<T>>>, port: usize) -> bool {
567        self.elements.iter().all(|capability| capability.valid_for_output(query_buffer, port))
568    }
569}
570
571impl<T: Timestamp> CapabilitySet<T> {
572    /// Creates a new capability set to send data at each element of `stamp`.
573    ///
574    /// This method panics if any element of `stamp` is without a capability in
575    /// `self.elements` less or equal to it.
576    pub fn delayed_stamp(&self, stamp: &Stamp<T>) -> CapabilitySet<T> {
577        stamp.iter().map(|time| self.delayed(time)).collect()
578    }
579}
580
581impl<T: Timestamp> FromIterator<Capability<T>> for CapabilitySet<T> {
582    fn from_iter<I: IntoIterator<Item = Capability<T>>>(iter: I) -> Self {
583        let mut result = Self::new();
584        for capability in iter {
585            result.insert(capability);
586        }
587        result
588    }
589}
590
591impl<T> From<Vec<Capability<T>>> for CapabilitySet<T>
592where
593    T: Timestamp,
594{
595    fn from(capabilities: Vec<Capability<T>>) -> Self {
596        let mut this = Self::with_capacity(capabilities.len());
597        for capability in capabilities {
598            this.insert(capability);
599        }
600
601        this
602    }
603}
604
605impl<T: Timestamp> Default for CapabilitySet<T> {
606    fn default() -> Self {
607        Self::new()
608    }
609}
610
611impl<T: Timestamp> Deref for CapabilitySet<T> {
612    type Target=[Capability<T>];
613
614    fn deref(&self) -> &[Capability<T>] {
615        &self.elements
616    }
617}