Repository: apache/arrow
File: cpp/src/arrow/compute/kernels/scalar_cast_string.cc (C++)

Code before:
                                output->length + 1);
    return Status::OK();
  }
}

// Offset String -> Offset String
template <typename O, typename I>
enable_if_t<is_base_binary_type<I>::value && is_base_binary_type<O>::value, Status>
BinaryToBinaryCastExec(KernelContext* ctx, const ExecSpan& batch, ExecResult* out) {
  const CastOptions& options = checked_cast<const CastState&>(*ctx->state()).options;
  const ArraySpan& input = batch[0].array;

  if constexpr (!I::is_utf8 && O::is_utf8) {
    if (!options.allow_invalid_utf8) {
      InitializeUTF8();
      ArraySpanVisitor<I> visitor;
      Utf8Validator validator;
      RETURN_NOT_OK(visitor.Visit(input, &validator));
    }
  }

  if constexpr (sizeof(typename I::offset_type) != sizeof(typename O::offset_type)) {
    std::shared_ptr<ArrayData> input_arr = input.ToArrayData();
    ArrayData* output = out->array_data().get();
    output->length = input_arr->length;
    // output->offset is set below
    output->SetNullCount(input_arr->null_count);
    output->buffers = std::move(input_arr->buffers);
    // binary/string arrays don't have child_data

    // Slice buffers to reduce allocation when casting the offsets buffer
    int64_t offset = input_arr->offset;
    size_t input_offset_type_size = sizeof(typename I::offset_type);
    if (output->null_count != 0 && output->buffers[0]) {
      // Avoid reallocation of the validity buffer by allowing some padding bits
      output->offset = input_arr->offset % 8;
    } else {
      output->offset = 0;
    }
    if (output->buffers[0]) {
      output->buffers[0] = SliceBuffer(output->buffers[0], offset / 8);
    }
    output->buffers[1] = SliceBuffer(output->buffers[1], offset * input_offset_type_size);

    return CastBinaryToBinaryOffsets<typename I::offset_type, typename O::offset_type>(
        ctx, input, out->array_data().get());
  } else {
    return ZeroCopyCastExec(ctx, batch, out);
  }
}


Review comment (performance):
Shall we organize the logic as:
```
RETURN_NOT_OK(ZeroCopyCastExec(ctx, batch, out));
if constexpr (sizeof(typename I::offset_type) != sizeof(typename O::offset_type)) {
  // Change output offset and slice buffers correspondingly.
  return CastBinaryToBinaryOffsets<typename I::offset_type, typename O::offset_type>(ctx, input, out->array_data().get());
}
return Status::OK();
```
to reuse some logic already in `ZeroCopyCastExec`? (I also see other cast functions having similar code organization).

Code after:
                                output->GetMutableValues<output_offset_type>(1),
                                output->length + 1);
    return Status::OK();
  }
}

// Offset String -> Offset String
template <typename O, typename I>
enable_if_t<is_base_binary_type<I>::value && is_base_binary_type<O>::value, Status>
BinaryToBinaryCastExec(KernelContext* ctx, const ExecSpan& batch, ExecResult* out) {
  const CastOptions& options = checked_cast<const CastState&>(*ctx->state()).options;
  const ArraySpan& input = batch[0].array;

  if constexpr (!I::is_utf8 && O::is_utf8) {
    if (!options.allow_invalid_utf8) {
      InitializeUTF8();
      ArraySpanVisitor<I> visitor;
      Utf8Validator validator;
      RETURN_NOT_OK(visitor.Visit(input, &validator));
    }
  }

  // Start with a zero-copy cast, but change indices to the correct size and set validity
  // bitmap and offset if needed.
  RETURN_NOT_OK(ZeroCopyCastExec(ctx, batch, out));
  if constexpr (sizeof(typename I::offset_type) != sizeof(typename O::offset_type)) {
    std::shared_ptr<ArrayData> input_arr = input.ToArrayData();
    ArrayData* output = out->array_data().get();

    // Slice buffers to minimize the output's offset. We need a small offset because
    // CastBinaryToBinaryOffsets() will reallocate the offsets buffer with size
    // (out_length + out_offset + 1) * sizeof(offset_type).
    int64_t input_offset = input_arr->offset;
    size_t input_offset_type_size = sizeof(typename I::offset_type);
    if (output->null_count != 0 && output->buffers[0]) {
      // Avoid reallocation of the validity buffer by allowing some padding bits
      output->offset = input_offset % 8;
    } else {
      output->offset = 0;
    }
    if (output->buffers[0]) {
      output->buffers[0] = SliceBuffer(output->buffers[0], input_offset / 8);
    }
    output->buffers[1] = SliceBuffer(
        output->buffers[1], (input_offset - output->offset) * input_offset_type_size);

    return CastBinaryToBinaryOffsets<typename I::offset_type, typename O::offset_type>(
        ctx, input, out->array_data().get());
  }
  return Status::OK();
}