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);
}