vortex_array/array/plugin.rs
1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4use std::fmt;
5use std::fmt::Debug;
6use std::fmt::Formatter;
7use std::sync::Arc;
8
9use vortex_buffer::ByteBuffer;
10use vortex_error::VortexResult;
11use vortex_error::vortex_ensure;
12use vortex_session::VortexSession;
13
14use crate::ArrayRef;
15use crate::IntoArray;
16use crate::array::Array;
17use crate::array::ArrayId;
18use crate::array::VTable;
19use crate::buffer::BufferHandle;
20use crate::dtype::DType;
21use crate::serde::ArrayChildren;
22
23/// Reference-counted array plugin.
24pub type ArrayPluginRef = Arc<dyn ArrayPlugin>;
25
26/// The wire representation produced by an in-memory array's serializer.
27///
28/// A serializer may reuse the in-memory array's buffers and children with [`Self::from_array`],
29/// or return different parts when an older wire representation requires a lossless structural
30/// downgrade.
31#[derive(Clone, Debug)]
32pub struct ArraySerialization {
33 /// The concrete array ID to write on the wire.
34 pub serialized_id: ArrayId,
35 /// Encoding-specific metadata written into the array node.
36 pub metadata: Vec<u8>,
37 /// Top-level buffers written for this array node.
38 pub buffers: Vec<ByteBuffer>,
39 /// Child arrays to serialize recursively.
40 pub children: Vec<ArrayRef>,
41}
42
43impl ArraySerialization {
44 /// Create a wire representation from an ID, metadata, buffers, and children.
45 pub fn new(
46 serialized_id: ArrayId,
47 metadata: Vec<u8>,
48 buffers: Vec<ByteBuffer>,
49 children: Vec<ArrayRef>,
50 ) -> Self {
51 Self {
52 serialized_id,
53 metadata,
54 buffers,
55 children,
56 }
57 }
58
59 /// Reuse an in-memory array's buffers and children with the supplied serialized metadata.
60 pub fn from_array(serialized_id: ArrayId, array: &ArrayRef, metadata: Vec<u8>) -> Self {
61 Self::new(serialized_id, metadata, array.buffers(), array.children())
62 }
63}
64
65/// The borrowed wire components passed to an array deserializer.
66pub struct ArrayDeserialization<'a> {
67 /// The exact array ID found on the wire.
68 pub serialized_id: ArrayId,
69 /// The logical dtype supplied by the containing format.
70 pub dtype: &'a DType,
71 /// The logical array length supplied by the containing format.
72 pub len: usize,
73 /// Encoding-specific metadata from the array node.
74 pub metadata: &'a [u8],
75 /// Top-level buffers referenced by the array node.
76 pub buffers: &'a [BufferHandle],
77 /// Lazily decoded child arrays referenced by the array node.
78 pub children: &'a dyn ArrayChildren,
79}
80
81impl<'a> ArrayDeserialization<'a> {
82 /// Create borrowed deserialization input from a wire ID and its serialized components.
83 pub fn new(
84 serialized_id: ArrayId,
85 dtype: &'a DType,
86 len: usize,
87 metadata: &'a [u8],
88 buffers: &'a [BufferHandle],
89 children: &'a dyn ArrayChildren,
90 ) -> Self {
91 Self {
92 serialized_id,
93 dtype,
94 len,
95 metadata,
96 buffers,
97 children,
98 }
99 }
100}
101
102/// Registry trait for serializing and deserializing an in-memory array representation.
103///
104/// A plugin has one [`id`](Self::id) for the in-memory representation and one or more
105/// [`serialized_ids`](Self::serialized_ids) for wire representations. Its serializer chooses the
106/// wire representation, and the serialization context validates that the chosen ID is permitted
107/// before it is written.
108///
109/// Every serialized ID is also registered for deserialization. A current plugin may therefore
110/// deserialize several historical IDs into the same in-memory representation. A reader that
111/// predates a newer ID has no registration for it and reports it as unknown instead of silently
112/// interpreting an unsupported representation.
113pub trait ArrayPlugin: 'static + Send + Sync {
114 /// Returns the ID of the in-memory array representation handled by this plugin.
115 fn id(&self) -> ArrayId;
116
117 /// Returns the serialized array IDs understood by this plugin, ordered oldest to newest.
118 ///
119 /// The default uses the in-memory ID as the sole wire ID. Override this for an in-memory array
120 /// that has multiple serialized variants. IDs retained only for reading may also be included;
121 /// the single serializer need not select them.
122 fn serialized_ids(&self) -> Vec<ArrayId> {
123 vec![self.id()]
124 }
125
126 /// Serialize `array` to its wire representation.
127 ///
128 /// This function is called only for arrays whose in-memory encoding matches [`id`](Self::id).
129 /// The returned ID must be declared by [`serialized_ids`](Self::serialized_ids). Return
130 /// `Ok(None)` when the array cannot be serialized.
131 fn serialize(
132 &self,
133 array: &ArrayRef,
134 session: &VortexSession,
135 ) -> VortexResult<Option<ArraySerialization>>;
136
137 /// Deserialize one recognized wire representation into the current in-memory array.
138 ///
139 /// `serialized_id` identifies the exact representation encountered on disk. The returned
140 /// array does not necessarily have to use this plugin's in-memory ID; this supports legacy
141 /// representations that are normalized into another current in-memory array. Implementations
142 /// must validate the contract of that exact ID rather than accepting every form understood by
143 /// the current in-memory representation under an older ID.
144 fn deserialize(
145 &self,
146 parts: ArrayDeserialization<'_>,
147 session: &VortexSession,
148 ) -> VortexResult<ArrayRef>;
149
150 /// Can this plugin emit an array with the given encoding.
151 ///
152 /// By default, this is just the [ID][Self::id] of the plugin, but
153 /// can be overridden if this plugin instance supports reading/writing multiple arrays.
154 fn is_supported_encoding(&self, id: &ArrayId) -> bool {
155 self.id() == *id
156 }
157}
158
159impl Debug for dyn ArrayPlugin {
160 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
161 f.debug_tuple("ArrayPlugin").field(&self.id()).finish()
162 }
163}
164
165impl<V: VTable> ArrayPlugin for V {
166 fn id(&self) -> ArrayId {
167 VTable::id(self)
168 }
169
170 fn serialize(
171 &self,
172 array: &ArrayRef,
173 session: &VortexSession,
174 ) -> VortexResult<Option<ArraySerialization>> {
175 vortex_ensure!(
176 self.id() == array.encoding_id(),
177 "array plugin {} cannot serialize in-memory array {}",
178 self.id(),
179 array.encoding_id(),
180 );
181 Ok(V::serialize(array.as_::<V>(), session)?
182 .map(|metadata| ArraySerialization::from_array(self.id(), array, metadata)))
183 }
184
185 fn deserialize(
186 &self,
187 parts: ArrayDeserialization<'_>,
188 session: &VortexSession,
189 ) -> VortexResult<ArrayRef> {
190 vortex_ensure!(
191 self.id() == parts.serialized_id,
192 "array plugin {} does not recognize serialized ID {}",
193 self.id(),
194 parts.serialized_id,
195 );
196 Ok(Array::<V>::try_from_parts(V::deserialize(
197 self,
198 parts.dtype,
199 parts.len,
200 parts.metadata,
201 parts.buffers,
202 parts.children,
203 session,
204 )?)?
205 .into_array())
206 }
207}