@@ -252,7 +252,13 @@ pub fn assemble_decimal(
252252 let len = msp. len ( ) ;
253253 let lower: Vec < & [ u64 ] > = lower_parts
254254 . iter ( )
255- . map ( |part| {
255+ . enumerate ( )
256+ . map ( |( idx, part) | {
257+ vortex_ensure ! (
258+ part. dtype( ) == & LOWER_PART_DTYPE ,
259+ "lower part {idx} must have dtype {LOWER_PART_DTYPE}, got {}" ,
260+ part. dtype( )
261+ ) ;
256262 let part = part. as_slice :: < u64 > ( ) ;
257263 vortex_ensure ! (
258264 part. len( ) == len,
@@ -265,17 +271,17 @@ pub fn assemble_decimal(
265271
266272 Ok ( match lower. as_slice ( ) {
267273 [ first] => DecimalArray :: new (
268- assemble_wide :: < i128 , 1 > ( msp, [ first] ) ,
274+ assemble_wide_decimal :: < i128 , 1 > ( msp, [ first] ) ,
269275 decimal_dtype,
270276 validity,
271277 ) ,
272278 [ first, second] => DecimalArray :: new (
273- assemble_wide :: < i256 , 2 > ( msp, [ first, second] ) ,
279+ assemble_wide_decimal :: < i256 , 2 > ( msp, [ first, second] ) ,
274280 decimal_dtype,
275281 validity,
276282 ) ,
277283 [ first, second, third] => DecimalArray :: new (
278- assemble_wide :: < i256 , 3 > ( msp, [ first, second, third] ) ,
284+ assemble_wide_decimal :: < i256 , 3 > ( msp, [ first, second, third] ) ,
279285 decimal_dtype,
280286 validity,
281287 ) ,
@@ -286,39 +292,10 @@ pub fn assemble_decimal(
286292 } )
287293}
288294
289- /// Combine a single row's parts into an `i128`.
290- #[ inline]
291- pub ( crate ) fn combine_i128 ( msp : i64 , lower : impl IntoIterator < Item = u64 > ) -> i128 {
292- lower. into_iter ( ) . fold ( i128:: from ( msp) , |acc, part| {
293- ( acc << LOWER_PART_BITS ) | i128:: from ( part)
294- } )
295- }
296-
297- /// Combine a signed MSP and two or three lower parts into an `i256`.
298- #[ inline]
299- pub ( crate ) fn combine_i256 ( msp : i64 , lower : impl ExactSizeIterator < Item = u64 > ) -> i256 {
300- let count = lower. len ( ) ;
301- let mut high = i128:: from ( msp) ;
302- let mut low = 0u128 ;
303- for ( index, part) in lower. enumerate ( ) {
304- if count == 3 && index == 0 {
305- high = ( high << LOWER_PART_BITS ) | i128:: from ( part) ;
306- } else {
307- low = ( low << LOWER_PART_BITS ) | u128:: from ( part) ;
308- }
309- }
310- i256:: from_parts ( low, high)
311- }
312-
313- /// Reassemble a signed MSP and `K` unsigned lower parts into wide integers.
295+ /// Assemble a column of wide decimal values from the MSP and `K` lower-part columns.
314296///
315- /// Each row starts with the MSP sign-extended to `T`. Appending a lower word shifts the
316- /// accumulated value left by 64 bits and fills the low bits with that word. Lower parts
317- /// are appended most significant first.
318- ///
319- /// The callers select `i128` for one lower part and `i256` for two or three. Since `K`
320- /// is constant, the compiler can unroll the loop that appends the lower words.
321- fn assemble_wide < T , const K : usize > ( msp : & PrimitiveArray , lower : [ & [ u64 ] ; K ] ) -> Buffer < T >
297+ /// A fixed part count lets the compiler unroll each call to [`assemble_wide_decimal_value`].
298+ fn assemble_wide_decimal < T , const K : usize > ( msp : & PrimitiveArray , lower : [ & [ u64 ] ; K ] ) -> Buffer < T >
322299where
323300 T : NativeDecimalType + Shl < usize , Output = T > + BitOr < Output = T > ,
324301{
@@ -329,16 +306,30 @@ where
329306 clippy:: useless_conversion,
330307 reason = "the widening to i64 is a no-op only for the i64 arm of the ptype match"
331308 ) ]
332- let mut value = T :: from( i64 :: from( * value) ) . vortex_expect( "MSP fits in the output type" ) ;
333- for part in lower {
334- value = ( value << LOWER_PART_BITS )
335- | T :: from( part[ row] ) . vortex_expect( "lower word fits in the output type" ) ;
336- }
337- value
309+ let msp = i64 :: from( * value) ;
310+ assemble_wide_decimal_value( msp, lower. map( |part| part[ row] ) )
338311 } ) ) ;
339312 } ) ;
340313 out. freeze ( )
341314}
342315
316+ /// Reassemble a decimal's unscaled integer from its signed MSP and `K` lower words.
317+ ///
318+ /// Sign-extend the MSP to `T`, then append each lower word by shifting left 64 bits and
319+ /// filling the low bits. Lower words are ordered most significant first. Callers select
320+ /// `i128` for one lower word and `i256` for two or three.
321+ #[ inline]
322+ pub ( crate ) fn assemble_wide_decimal_value < T , const K : usize > ( msp : i64 , lower : [ u64 ; K ] ) -> T
323+ where
324+ T : NativeDecimalType + Shl < usize , Output = T > + BitOr < Output = T > ,
325+ {
326+ let mut value = T :: from ( msp) . vortex_expect ( "MSP fits in the output type" ) ;
327+ for part in lower {
328+ value = ( value << LOWER_PART_BITS )
329+ | T :: from ( part) . vortex_expect ( "lower word fits in the output type" ) ;
330+ }
331+ value
332+ }
333+
343334#[ cfg( test) ]
344335mod tests;
0 commit comments