ArrowErrorCode Read()

in c/driver/postgresql/copy/reader.h [258:398]


  ArrowErrorCode Read(ArrowBufferView* data, int32_t field_size_bytes, ArrowArray* array,
                      ArrowError* error) override {
    // -1 for NULL
    if (field_size_bytes < 0) {
      return ArrowArrayAppendNull(array, 1);
    }

    // Read the input
    if (data->size_bytes < static_cast<int64_t>(4 * sizeof(int16_t))) {
      ArrowErrorSet(error,
                    "Expected at least %d bytes of field data for numeric copy data but "
                    "only %d bytes of input remain",
                    static_cast<int>(4 * sizeof(int16_t)),
                    static_cast<int>(data->size_bytes));  // NOLINT(runtime/int)
      return EINVAL;
    }

    int16_t ndigits = ReadUnsafe<int16_t>(data);
    int16_t weight = ReadUnsafe<int16_t>(data);
    uint16_t sign = ReadUnsafe<uint16_t>(data);
    uint16_t dscale = ReadUnsafe<uint16_t>(data);

    if (data->size_bytes < static_cast<int64_t>(ndigits * sizeof(int16_t))) {
      ArrowErrorSet(error,
                    "Expected at least %d bytes of field data for numeric digits copy "
                    "data but only %d bytes of input remain",
                    static_cast<int>(ndigits * sizeof(int16_t)),
                    static_cast<int>(data->size_bytes));  // NOLINT(runtime/int)
      return EINVAL;
    }

    digits_.clear();
    for (int16_t i = 0; i < ndigits; i++) {
      digits_.push_back(ReadUnsafe<int16_t>(data));
    }

    // Handle special values
    std::string special_value;
    switch (sign) {
      case kNumericNAN:
        special_value = std::string("nan");
        break;
      case kNumericPinf:
        special_value = std::string("inf");
        break;
      case kNumericNinf:
        special_value = std::string("-inf");
        break;
      case kNumericPos:
      case kNumericNeg:
        special_value = std::string("");
        break;
      default:
        ArrowErrorSet(error,
                      "Unexpected value for sign read from Postgres numeric field: %d",
                      static_cast<int>(sign));
        return EINVAL;
    }

    if (!special_value.empty()) {
      NANOARROW_RETURN_NOT_OK(
          ArrowBufferAppend(data_, special_value.data(), special_value.size()));
      NANOARROW_RETURN_NOT_OK(ArrowBufferAppendInt32(offsets_, data_->size_bytes));
      return AppendValid(array);
    }

    // Calculate string space requirement
    int64_t max_chars_required = std::max<int64_t>(1, (weight + 1) * kDecDigits);
    max_chars_required += dscale + kDecDigits + 2;
    NANOARROW_RETURN_NOT_OK(ArrowBufferReserve(data_, max_chars_required));
    char* out0 = reinterpret_cast<char*>(data_->data + data_->size_bytes);
    char* out = out0;

    // Build output string in-place, starting with the negative sign
    if (sign == kNumericNeg) {
      *out++ = '-';
    }

    // ...then digits before the decimal point
    int d;
    int d1;
    int16_t dig;

    if (weight < 0) {
      d = weight + 1;
      *out++ = '0';
    } else {
      for (d = 0; d <= weight; d++) {
        if (d < ndigits) {
          dig = digits_[d];
        } else {
          dig = 0;
        }

        // To strip leading zeroes
        int append = (d > 0);

        for (const auto pow10 : {1000, 100, 10}) {
          d1 = dig / pow10;
          dig -= d1 * pow10;
          append |= (d1 > 0);
          if (append) {
            *out++ = d1 + '0';
          }
        }

        *out++ = dig + '0';
      }
    }

    // ...then the decimal point + digits after it. This may write more digits
    // than specified by dscale so we need to keep track of how many we want to
    // keep here.
    int64_t actual_chars_required = out - out0;

    if (dscale > 0) {
      *out++ = '.';
      actual_chars_required += dscale + 1;

      for (int i = 0; i < dscale; d++, i += kDecDigits) {
        if (d >= 0 && d < ndigits) {
          dig = digits_[d];
        } else {
          dig = 0;
        }

        for (const auto pow10 : {1000, 100, 10}) {
          d1 = dig / pow10;
          dig -= d1 * pow10;
          *out++ = d1 + '0';
        }

        *out++ = dig + '0';
      }
    }

    // Update data buffer size and add offsets
    data_->size_bytes += actual_chars_required;
    NANOARROW_RETURN_NOT_OK(ArrowBufferAppendInt32(offsets_, data_->size_bytes));
    return AppendValid(array);
  }