Repository: apache/arrow
File: cpp/src/arrow/ipc/writer.cc (C++)

Code before:
      offset += size + padding;
    }

    variadic_counts_ = out_->variadic_buffer_counts;

    out_->body_length = offset - buffer_start_offset_;
    DCHECK(bit_util::IsMultipleOf8(out_->body_length));

    // Now that we have computed the locations of all of the buffers in shared
    // memory, the data header can be converted to a flatbuffer and written out
    //
    // Note: The memory written here is prefixed by the size of the flatbuffer
    // itself as an int32_t.
    return SerializeMetadata(batch.num_rows());
  }

  template <typename ArrayType>
  Status GetZeroBasedValueOffsets(const ArrayType& array,
                                  std::shared_ptr<Buffer>* value_offsets) {
    // Share slicing logic between ListArray, BinaryArray and LargeBinaryArray
    using offset_type = typename ArrayType::offset_type;

    auto offsets = array.value_offsets();

    int64_t required_bytes = sizeof(offset_type) * (array.length() + 1);
    if (array.offset() != 0) {
      // If we have a non-zero offset, then the value offsets do not start at
      // zero. We must a) create a new offsets array with shifted offsets and
      // b) slice the values array accordingly

      ARROW_ASSIGN_OR_RAISE(auto shifted_offsets,
                            AllocateBuffer(required_bytes, options_.memory_pool));

      auto dest_offsets = shifted_offsets->mutable_span_as<offset_type>();
      const offset_type start_offset = array.value_offset(0);

      for (int i = 0; i < array.length(); ++i) {
        dest_offsets[i] = array.value_offset(i) - start_offset;
      }
      // Final offset
      dest_offsets[array.length()] = array.value_offset(array.length()) - start_offset;
      offsets = std::move(shifted_offsets);
    } else {
      // ARROW-6046: Slice offsets to used extent, in case we have a truncated
      // slice
      if (offsets != nullptr && offsets->size() > required_bytes) {
        offsets = SliceBuffer(offsets, 0, required_bytes);
      }
    }
    *value_offsets = std::move(offsets);
    return Status::OK();

Review comment (suggestion):
I wonder if we cannot remove the old condition:
```suggestion
    if (array.length() > 0 && array.value_offset(0) > 0) {
```

For example, if the offsets buffer contains `[0, 0, 0, 2, 3]`, then with `array.offset() == 2` we would just need to truncate the offsets buffer without rebuilding a new one. That could be done by generalizing the `SliceBuffer` path below.

(but we would have to make sure this case is properly unit-tested, of course)

Code after:
      offset += size + padding;
    }

    variadic_counts_ = out_->variadic_buffer_counts;

    out_->body_length = offset - buffer_start_offset_;
    DCHECK(bit_util::IsMultipleOf8(out_->body_length));

    // Now that we have computed the locations of all of the buffers in shared
    // memory, the data header can be converted to a flatbuffer and written out
    //
    // Note: The memory written here is prefixed by the size of the flatbuffer
    // itself as an int32_t.
    return SerializeMetadata(batch.num_rows());
  }

  template <typename ArrayType>
  Status GetZeroBasedValueOffsets(const ArrayType& array,
                                  std::shared_ptr<Buffer>* value_offsets) {
    // Share slicing logic between ListArray, BinaryArray and LargeBinaryArray
    using offset_type = typename ArrayType::offset_type;

    if (array.length() == 0) {
      *value_offsets = array.value_offsets();
      return Status::OK();
    }

    int64_t required_bytes = sizeof(offset_type) * (array.length() + 1);
    if (array.value_offset(0) > 0) {
      // If the offset of the first value is non-zero, then we must create a new
      // offsets buffer with shifted offsets.
      ARROW_ASSIGN_OR_RAISE(auto shifted_offsets,
                            AllocateBuffer(required_bytes, options_.memory_pool));

      auto dest_offsets = shifted_offsets->mutable_span_as<offset_type>();
      const offset_type* source_offsets = array.raw_value_offsets();
      const offset_type start_offset = source_offsets[0];

      for (int i = 0; i <= array.length(); ++i) {
        dest_offsets[i] = source_offsets[i] - start_offset;
      }
      *value_offsets = std::move(shifted_offsets);
    } else {
      // ARROW-6046: if we have a truncated slice with unused leading or
      // trailing data, then we slice it.
      if (array.offset() > 0 || array.value_offsets()->size() > required_bytes) {
        *value_offsets = SliceBuffer(
            array.value_offsets(), array.offset() * sizeof(offset_type), required_bytes);
      } else {
        *value_offsets = array.value_offsets();
      }