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

Code before:
  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));
    }
  }

  std::shared_ptr<ArrayData> input_arr = input.ToArrayData();
  ArrayData* output = out->array_data().get();
  output->length = input_arr->length;
  output->SetNullCount(input_arr->null_count);
  output->buffers = std::move(input_arr->buffers);
  output->child_data = std::move(input_arr->child_data);

  if (output->buffers[0]) {
    // If reusing the null bitmap, ensure offset into the first byte is the same as input.
    output->offset = input_arr->offset % 8;
    output->buffers[0] = SliceBuffer(output->buffers[0], input_arr->offset / 8);
  } else {
    output->offset = 0;
  }

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

// String View -> Offset String
template <typename O, typename I>
enable_if_t<is_binary_view_like_type<I>::value && is_base_binary_type<O>::value, Status>
BinaryToBinaryCastExec(KernelContext* ctx, const ExecSpan& batch, ExecResult* out) {
  using offset_type = typename O::offset_type;
  using DataBuilder = TypedBufferBuilder<uint8_t>;
  using OffsetBuilder = TypedBufferBuilder<offset_type>;
  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));
    }
  }

  ArrayData* output = out->array_data().get();

Review comment (suggestion):
I believe you should also slice the offsets buffer when you change the array offset.

```suggestion
  // slice buffers to reduce allocation when casting the offsets buffer
  auto length = input_arr->length;
  int64_t buffer_offset = 0;
  if (output->null_count != 0 && output->buffers[0]) {
    // avoiding reallocation of the validity buffer by allowing some padding bits
    output->offset = input_arr->offset % 8;
    buffer_offset = input_arr->offset / 8;
  } else {
    output->offset = 0;
    buffer_offset = input_arr->offset;
  }
  output->buffers[0] = SliceBuffer(output->buffers[0], buffer_offset, length);
  output->buffers[1] = SliceBuffer(output->buffers[1], buffer_offset, length);
```

Code after:
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();
}

// String View -> Offset String
template <typename O, typename I>
enable_if_t<is_binary_view_like_type<I>::value && is_base_binary_type<O>::value, Status>
BinaryToBinaryCastExec(KernelContext* ctx, const ExecSpan& batch, ExecResult* out) {
  using offset_type = typename O::offset_type;
  using DataBuilder = TypedBufferBuilder<uint8_t>;
  using OffsetBuilder = TypedBufferBuilder<offset_type>;
  const CastOptions& options = checked_cast<const CastState&>(*ctx->state()).options;