| 493 | n_pending_(0) {} |
| 494 | |
| 495 | arrow::Result<std::shared_ptr<arrow::Buffer>> operator()( |
| 496 | const std::shared_ptr<arrow::Buffer>& src) { |
| 497 | // A pre-allocation factor to account for possible data growth when |
| 498 | // converting to UTF-8. |
| 499 | constexpr double kOversizeFactor = 1.2; |
| 500 | |
| 501 | arrow::BufferBuilder builder; |
| 502 | const int64_t initial_size = static_cast<int64_t>(src->size() * kOversizeFactor); |
| 503 | RETURN_NOT_OK(builder.Reserve(initial_size)); |
| 504 | |
| 505 | int64_t out_bytes_left = builder.capacity(); |
| 506 | uint8_t* out_buf = builder.mutable_data(); |
| 507 | |
| 508 | int64_t in_bytes_left; |
| 509 | const uint8_t* in_buf; |
| 510 | int64_t n_src_bytes_in_pending = 0; |
| 511 | |
| 512 | // There may be a few left over bytes from the last call to iconv. |
| 513 | // Process these first using the internal buffer (with as many bytes |
| 514 | // as possible added from src) as the source. This may also result in |
| 515 | // a partial character left over but will always get us into the src buffer. |
| 516 | if (n_pending_ > 0) { |
| 517 | n_src_bytes_in_pending = |
| 518 | std::min<int64_t>(sizeof(pending_) - n_pending_, src->size()); |
| 519 | memcpy(pending_ + n_pending_, src->data(), n_src_bytes_in_pending); |
| 520 | in_buf = pending_; |
| 521 | in_bytes_left = n_pending_ + n_src_bytes_in_pending; |
| 522 | |
| 523 | iconv_->iconv(&in_buf, &in_bytes_left, &out_buf, &out_bytes_left); |
| 524 | |
| 525 | // Rather than check the error return code (which is often returned |
| 526 | // in the case of a partial character at the end of the pending_ |
| 527 | // buffer), check that we have read enough characters to get into |
| 528 | // `src` (after which the loop below will error for invalid characters). |
| 529 | int64_t bytes_read_in = in_buf - pending_; |
| 530 | if (bytes_read_in < n_pending_) { |
| 531 | return StatusInvalidInput(); |
| 532 | } |
| 533 | |
| 534 | int64_t bytes_read_out = out_buf - builder.mutable_data(); |
| 535 | builder.UnsafeAdvance(bytes_read_out); |
| 536 | |
| 537 | int64_t chars_read_in = n_pending_ + n_src_bytes_in_pending - in_bytes_left; |
| 538 | in_buf = src->data() + chars_read_in - n_pending_; |
| 539 | in_bytes_left = src->size() + n_pending_ - chars_read_in; |
| 540 | } else { |
| 541 | in_buf = src->data(); |
| 542 | in_bytes_left = src->size(); |
| 543 | } |
| 544 | |
| 545 | // Try to call iconv() as many times as we need, potentially enlarging |
| 546 | // the output buffer as needed. When zero bytes are appended, the loop |
| 547 | // will either error (if there are more than 4 bytes left) or copy the |
| 548 | // bytes to pending_ and wait for more input. We use 4 bytes because |
| 549 | // this is the maximum number of bytes per complete character in UTF-8, |
| 550 | // UTF-16, and UTF-32. |
| 551 | while (in_bytes_left > 0) { |
| 552 | // Make enough place in the output to hopefully consume all of the input. |
nothing calls this directly
no test coverage detected