5353// the next add (≤ 8 bytes) within the 24-byte capacity.
5454//
5555// `codes_offsets` is templated over the four unsigned integer widths
56- // (u8/u16/u32/u64). `output_offsets` is uint64_t.
56+ // (u8/u16/u32/u64). `output_offsets` is uint64_t for the view kernels
57+ // (`fsst_*`, which also take an optional views pointer) and int32_t Arrow
58+ // varbin offsets for the bytes-only varbin kernels (`fsst_varbin_*`).
5759
5860// 24-byte scratch buffer split across three u64 lanes. `cursor` is the
5961// number of bytes currently buffered and the next-push offset.
@@ -123,13 +125,18 @@ struct Scratch {
123125 }
124126};
125127
126- template <typename OffT>
128+ // Arrow/Vortex variable-length view records are 16 bytes. Values up to 12
129+ // bytes are stored inline after the u32 length. Longer values store their
130+ // first four bytes, backing-buffer index, and byte offset.
131+ constexpr uint32_t MAX_INLINED_SIZE = 12 ;
132+
133+ template <typename CodeOffsetT, typename OutputOffsetT>
127134struct FSSTArgs {
128135 // Compressed FSST code stream, contiguous across all strings. String
129136 // `sid`'s codes live in `[codes_offsets[sid], codes_offsets[sid + 1])`.
130137 const uint8_t *__restrict codes_bytes;
131138 // Per-string offsets into `codes_bytes`, length `num_strings + 1`.
132- const OffT *__restrict codes_offsets;
139+ const CodeOffsetT *__restrict codes_offsets;
133140 // FSST symbol table.
134141 const uint64_t *__restrict symbols;
135142 // Length in bytes (1..=8) of each entry in `symbols`. The remaining bits
@@ -138,19 +145,57 @@ struct FSSTArgs {
138145 // Buffer to write decoded data into.
139146 uint8_t *__restrict output_bytes;
140147 // Per-string offsets into `output_bytes`, length `num_strings + 1`.
141- const uint64_t *__restrict output_offsets;
148+ const OutputOffsetT *__restrict output_offsets;
142149 // Validity of each string.
143150 const uint8_t *__restrict validity_bits;
151+ // Bit offset of string zero within the first validity byte (0..7).
152+ uint64_t validity_bit_offset;
153+ // Optional output views, one 16-byte uint4 per string. A null pointer
154+ // requests bytes-only decoding for heaps that need the host rollover path.
155+ uint4 *__restrict output_views;
144156};
145157
146- template <typename OffT>
147- __device__ inline void fsst_decode_string (const FSSTArgs<OffT> &args, uint64_t sid) {
148- if (((args.validity_bits [sid >> 3 ] >> (sid & 7u )) & 1u ) == 0u ) {
158+ // Build one BinaryView from the bytes this thread just decoded. The Rust
159+ // caller only provides output_views when every offset fits in the view's u32
160+ // fields and the decoded heap is exposed as backing buffer zero.
161+ template <typename CodeOffsetT, typename OutputOffsetT>
162+ __device__ inline void fsst_write_view (const FSSTArgs<CodeOffsetT, OutputOffsetT> &args, uint64_t sid) {
163+ if (args.output_views == nullptr ) {
149164 return ;
150165 }
151166
152- OffT in_pos = args.codes_offsets [sid];
153- const OffT in_end = args.codes_offsets [sid + 1 ];
167+ const uint64_t start = args.output_offsets [sid];
168+ const uint32_t len = (uint32_t )(args.output_offsets [sid + 1 ] - start);
169+ if (len <= MAX_INLINED_SIZE ) {
170+ uint32_t words[3 ] = {0 , 0 , 0 };
171+ #pragma unroll
172+ for (uint32_t i = 0 ; i < MAX_INLINED_SIZE ; i++) {
173+ if (i < len) {
174+ words[i >> 2 ] |= (uint32_t )args.output_bytes [start + i] << (8u * (i & 3u ));
175+ }
176+ }
177+ args.output_views [sid] = make_uint4 (len, words[0 ], words[1 ], words[2 ]);
178+ return ;
179+ }
180+
181+ const uint32_t prefix =
182+ (uint32_t )args.output_bytes [start] | ((uint32_t )args.output_bytes [start + 1 ] << 8u ) |
183+ ((uint32_t )args.output_bytes [start + 2 ] << 16u ) | ((uint32_t )args.output_bytes [start + 3 ] << 24u );
184+ args.output_views [sid] = make_uint4 (len, prefix, 0 , (uint32_t )start);
185+ }
186+
187+ template <typename CodeOffsetT, typename OutputOffsetT>
188+ __device__ inline void fsst_decode_string (const FSSTArgs<CodeOffsetT, OutputOffsetT> &args, uint64_t sid) {
189+ const uint64_t validity_index = sid + args.validity_bit_offset ;
190+ if (((args.validity_bits [validity_index >> 3 ] >> (validity_index & 7u )) & 1u ) == 0u ) {
191+ if (args.output_views != nullptr ) {
192+ args.output_views [sid] = make_uint4 (0 , 0 , 0 , 0 );
193+ }
194+ return ;
195+ }
196+
197+ CodeOffsetT in_pos = args.codes_offsets [sid];
198+ const CodeOffsetT in_end = args.codes_offsets [sid + 1 ];
154199 uint64_t out_pos = args.output_offsets [sid];
155200 const uint64_t out_end = args.output_offsets [sid + 1 ];
156201
@@ -181,46 +226,81 @@ __device__ inline void fsst_decode_string(const FSSTArgs<OffT> &args, uint64_t s
181226 sym &= mask;
182227
183228 scratch.push (sym, len);
184- in_pos += (OffT )consumed;
229+ in_pos += (CodeOffsetT )consumed;
185230 }
186231
187232 // Epilogue: drain everything that's left.
188233 while (scratch.cursor > 0 ) {
189234 scratch.drain (args.output_bytes , out_pos, out_end);
190235 }
236+
237+ fsst_write_view (args, sid);
191238}
192239
193- #define GENERATE_FSST_KERNEL (suffix, OffT ) \
240+ #define FSST_GRID_STRIDE_LOOP (CodeOffsetT, OutputOffsetT, args ) \
241+ const uint64_t elements_per_block = (uint64_t )blockDim .x * ELEMENTS_PER_THREAD ; \
242+ const uint64_t block_start = (uint64_t )blockIdx .x * elements_per_block; \
243+ const uint64_t block_end = \
244+ (block_start + elements_per_block < num_strings) ? (block_start + elements_per_block) : num_strings; \
245+ for (uint64_t sid = block_start + threadIdx .x; sid < block_end; sid += blockDim .x) { \
246+ fsst_decode_string<CodeOffsetT, OutputOffsetT>(args, sid); \
247+ }
248+
249+ #define GENERATE_FSST_VIEW_KERNEL (suffix, CodeOffsetT ) \
194250 extern " C" __global__ void fsst_##suffix(const uint8_t *__restrict codes_bytes, \
195- const OffT *__restrict codes_offsets, \
251+ const CodeOffsetT *__restrict codes_offsets, \
196252 const uint64_t *__restrict symbols, \
197253 const uint8_t *__restrict symbol_lengths, \
198254 const uint64_t *__restrict output_offsets, \
199255 const uint8_t *__restrict validity_bits, \
256+ uint64_t validity_bit_offset, \
200257 uint8_t *__restrict output_bytes, \
258+ uint4 *__restrict output_views, \
201259 uint64_t num_strings) { \
202- const FSSTArgs<OffT > args = { \
260+ const FSSTArgs<CodeOffsetT, uint64_t > args = { \
203261 codes_bytes, \
204262 codes_offsets, \
205263 symbols, \
206264 symbol_lengths, \
207265 output_bytes, \
208266 output_offsets, \
209267 validity_bits, \
268+ validity_bit_offset, \
269+ output_views, \
210270 }; \
211- \
212- const uint64_t elements_per_block = (uint64_t )blockDim .x * ELEMENTS_PER_THREAD ; \
213- const uint64_t block_start = (uint64_t )blockIdx .x * elements_per_block; \
214- const uint64_t block_end = (block_start + elements_per_block < num_strings) \
215- ? (block_start + elements_per_block) \
216- : num_strings; \
217- \
218- for (uint64_t sid = block_start + threadIdx .x ; sid < block_end; sid += blockDim .x ) { \
219- fsst_decode_string<OffT>(args, sid); \
220- } \
271+ FSST_GRID_STRIDE_LOOP (CodeOffsetT, uint64_t , args) \
221272 }
222273
223- GENERATE_FSST_KERNEL (u8 , uint8_t )
224- GENERATE_FSST_KERNEL (u16 , uint16_t )
225- GENERATE_FSST_KERNEL (u32 , uint32_t )
226- GENERATE_FSST_KERNEL (u64 , uint64_t )
274+ #define GENERATE_FSST_VARBIN_KERNEL (suffix, CodeOffsetT ) \
275+ extern " C" __global__ void fsst_varbin_##suffix(const uint8_t *__restrict codes_bytes, \
276+ const CodeOffsetT *__restrict codes_offsets, \
277+ const uint64_t *__restrict symbols, \
278+ const uint8_t *__restrict symbol_lengths, \
279+ const int32_t *__restrict output_offsets, \
280+ const uint8_t *__restrict validity_bits, \
281+ uint64_t validity_bit_offset, \
282+ uint8_t *__restrict output_bytes, \
283+ uint64_t num_strings) { \
284+ const FSSTArgs<CodeOffsetT, int32_t > args = { \
285+ codes_bytes, \
286+ codes_offsets, \
287+ symbols, \
288+ symbol_lengths, \
289+ output_bytes, \
290+ output_offsets, \
291+ validity_bits, \
292+ validity_bit_offset, \
293+ nullptr , \
294+ }; \
295+ FSST_GRID_STRIDE_LOOP (CodeOffsetT, int32_t , args) \
296+ }
297+
298+ GENERATE_FSST_VIEW_KERNEL (u8 , uint8_t )
299+ GENERATE_FSST_VIEW_KERNEL (u16 , uint16_t )
300+ GENERATE_FSST_VIEW_KERNEL (u32 , uint32_t )
301+ GENERATE_FSST_VIEW_KERNEL (u64 , uint64_t )
302+
303+ GENERATE_FSST_VARBIN_KERNEL (u8 , uint8_t )
304+ GENERATE_FSST_VARBIN_KERNEL (u16 , uint16_t )
305+ GENERATE_FSST_VARBIN_KERNEL (u32 , uint32_t )
306+ GENERATE_FSST_VARBIN_KERNEL (u64 , uint64_t )
0 commit comments