| 305 | } |
| 306 | |
| 307 | fn pad_nulls( |
| 308 | &mut self, |
| 309 | read_offset: usize, |
| 310 | values_read: usize, |
| 311 | levels_read: usize, |
| 312 | valid_mask: &[u8], |
| 313 | ) { |
| 314 | let byte_length = self.byte_length.unwrap_or_default(); |
| 315 | |
| 316 | assert_eq!(self.buffer.len(), (read_offset + values_read) * byte_length); |
| 317 | self.buffer |
| 318 | .resize((read_offset + levels_read) * byte_length, 0); |
| 319 | |
| 320 | let values_range = read_offset..read_offset + values_read; |
| 321 | // Move the bytes from value_pos to level_pos. For values of `byte_length` <= 4, |
| 322 | // the simple loop is preferred as the compiler can eliminate the loop via unrolling. |
| 323 | // For `byte_length > 4`, we instead copy from non-overlapping slices. This allows |
| 324 | // the loop to be vectorized, yielding much better performance. |
| 325 | const VEC_CUTOFF: usize = 4; |
| 326 | if byte_length > VEC_CUTOFF { |
| 327 | let op = |buffer: &mut Vec<u8>, level_pos_bytes, value_pos_bytes, byte_length| { |
| 328 | let split = buffer.split_at_mut(level_pos_bytes); |
| 329 | let dst = &mut split.1[..byte_length]; |
| 330 | let src = &split.0[value_pos_bytes..value_pos_bytes + byte_length]; |
| 331 | dst.copy_from_slice(src); |
| 332 | }; |
| 333 | move_values(&mut self.buffer, byte_length, values_range, valid_mask, op); |
| 334 | } else { |
| 335 | let op = |buffer: &mut Vec<u8>, level_pos_bytes, value_pos_bytes, byte_length| { |
| 336 | for i in 0..byte_length { |
| 337 | buffer[level_pos_bytes + i] = buffer[value_pos_bytes + i] |
| 338 | } |
| 339 | }; |
| 340 | move_values(&mut self.buffer, byte_length, values_range, valid_mask, op); |
| 341 | } |
| 342 | } |
| 343 | } |
| 344 | |
| 345 | struct ValueDecoder { |