Skip to main content

OutputSink

Trait OutputSink 

Source
pub unsafe trait OutputSink: 'static + Sized {
    type Params: 'static;
    type Rows<'a>: ViewLen + FillDefault
       where Self: 'a;
    type Row<'a>
       where Self: 'a;
    type WriteToken: 'static;

    // Required methods
    fn storage_dtype(params: &Self::Params) -> DType;
    fn with_capacity(rows: usize, params: &Self::Params) -> VortexResult<Self>;
    fn rows(&mut self) -> Self::Rows<'_>;
    unsafe fn row_unchecked<'a>(
        rows: &'a mut Self::Rows<'_>,
        index: usize,
    ) -> Self::Row<'a>;
    unsafe fn finish(self) -> VortexResult<ArrayRef>;

    // Provided method
    fn initialize_skipped_rows(rows: &mut Self::Rows<'_>) { ... }
}
Expand description

A column allocated once per batch that a row closure writes into, one row at a time.

A sink owns batch-wide state that an independent owned value cannot express, such as uninitialized storage or a row handle covering more than one element. The executor passes each row slot into an Fn closure.

A sink describes only how rows are physically written. An output dtype derived from the function options or argument dtypes is declared by RowVisitor::with_output_dtype, which labels the column this sink builds.

Rows arrive in increasing index order. Ordinary execution visits 0..row_count exactly once. Skip-invalid execution runs initialize_skipped_rows first and then visits only valid rows.

§Errors

Lifecycle methods report only incidental failures such as allocation. A semantic error that depends on input values must come from the row callback through a fallible SinkResult, or RowFn::INFALLIBLE cannot protect optimizations such as dictionary push-down.

§Safety

An implementation must uphold all of these requirements:

  • Every index below ViewLen::len for Rows must identify one distinct row owned by this sink.
  • A borrowed Rows view must retain its length and index-to-row mapping until it is dropped. Calls to row_unchecked and safe uses of a returned Row must preserve both properties.
  • initialize_skipped_rows is the only exception to this stability requirement. The executor checks the length again after the initializer. The initializer must initialize every row.
  • A row must either be initialized before the callback or require a WriteToken that safe code cannot produce without initializing that exact row. Evidence for an uninitialized row must not be safely forgeable, reusable, or substitutable.
  • Self and every borrowed Rows view must remain safe to drop if decoding, preparation, skipped-row initialization, or a row callback returns an error or unwinds. The executor can abandon a sink after any prefix of rows.
  • finish must be sound once every visited callback returned its required token and, for a skip-invalid traversal, initialize_skipped_rows ran successfully first.
  • Violating these requirements can cause undefined behavior.

Required Associated Types§

Source

type Params: 'static

Physical parameters required to construct this sink before the row loop.

This type describes only physical storage. A logical output dtype belongs on RowVisitor::with_output_dtype.

Source

type Rows<'a>: ViewLen + FillDefault where Self: 'a

A loop-local view of all output rows.

Borrowed once before execution so the sink’s buffer descriptor and shape become loop invariants rather than being re-read through &mut Self for every row. The FillDefault bound lets the default initialize_skipped_rows zero-initialize the rows; storage that is fully initialized at construction can wrap itself in Preinitialized to make that a no-op.

Source

type Row<'a> where Self: 'a

The place a row closure writes one row through, borrowed from the sink.

Source

type WriteToken: 'static

Proof that a successful row closure left its row handle initialized.

Use () for initialized row handles. A sink exposing uninitialized storage uses a token returned after initialization. If a sink uses the token to justify unsafe code, safe code must not be able to construct one without establishing the invariant.

Required Methods§

Source

fn storage_dtype(params: &Self::Params) -> DType

The dtype of the column this sink builds.

Must be non-nullable: batch execution derives nullability from the inputs, widens the result, and masks the null rows.

Source

fn with_capacity(rows: usize, params: &Self::Params) -> VortexResult<Self>

Allocate a sink for rows rows.

Source

fn rows(&mut self) -> Self::Rows<'_>

Borrow all output rows for the hot loop.

Source

unsafe fn row_unchecked<'a>( rows: &'a mut Self::Rows<'_>, index: usize, ) -> Self::Row<'a>

Hand out the place to write row index. Must be O(1): it is called in the row loop.

§Safety

index must be less than ViewLen::len for rows.

Source

unsafe fn finish(self) -> VortexResult<ArrayRef>

Finish into the built column, whose dtype must be this sink’s storage_dtype for the parameters passed to with_capacity. Called once per batch.

§Safety

The executor must have completed every row callback successfully, and each callback must have returned this sink’s WriteToken. When rows are skipped, initialize_skipped_rows must have run before traversal.

Provided Methods§

Source

fn initialize_skipped_rows(rows: &mut Self::Rows<'_>)

Initialize every output position before skip-invalid execution.

This must make every row safe to finish. The values are placeholders only: callbacks overwrite valid rows, and batch execution masks skipped rows before the output is observable, so any well-formed value works. Sink storage is non-nullable, so nulls do not exist at this level.

The default implementation zero-initializes the rows through FillDefault, which Rows provides: a plain slice of Default elements fills itself, a custom row view implements the filling for its own storage, and rows that are fully initialized at construction wrap themselves in Preinitialized to skip it. Override this method only when one batch-wide pass over the rows is the wrong operation.

Dense execution never calls this method because it visits every row.

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§

Source§

impl OutputSink for Utf8Sink

Source§

type Params = ()

Source§

type Rows<'a> = Utf8Rows<'a>

Source§

type Row<'a> = Utf8Writer<'a>

Source§

type WriteToken = ()

Source§

impl<T: OutputElement + Copy + Default> OutputSink for FixedSizeListSink<T>

Source§

type Params = usize

Source§

type Rows<'a> = FixedSizeRows<'a, T>

Source§

type Row<'a> = &'a mut [MaybeUninit<T>]

Source§

type WriteToken = InitializedRow

Source§

impl<T: OutputElement + Copy + Default> OutputSink for UninitElementSink<T>

Source§

type Params = ()

Source§

type Rows<'a> = UninitElementRows<'a, T>

Source§

type Row<'a> = &'a mut MaybeUninit<T>

Source§

type WriteToken = InitializedElement