blob: 779ee72b772bad950d639c0b4bba295e1c15f105 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <vector>
#include "drill/recordBatch.hpp"
#include "drill/decimalUtils.hpp"
#ifdef _WIN32
#define bswap_16 _byteswap_ushort
#define bswap_32 _byteswap_ulong
#define bswap_64 _byteswap_uint64
#elif defined(__linux__)
#include <byteswap.h> //for bswap_16,32,64
#elif defined(__APPLE__)
#include <libkern/OSByteOrder.h>
#define bswap_16 OSSwapInt16
#define bswap_32 OSSwapInt32
#define bswap_64 OSSwapInt64
#endif
using namespace boost::multiprecision;
#define MAX_DIGITS 9
#define INTEGER_SIZE sizeof(uint32_t)
#define DIGITS_BASE 1000000000
namespace Drill
{
// Code is ported from Java DecimalUtility.java implementation.
// The big differences are that we expose a DecimalValue struct,
// We store the unscaled value in a boost cpp_int
// And that when we retrieve integer data off the wire, we need to do an endianness swap on.
// We only do the endianness swap on dense types.
// on little-endian platforms.
DecimalValue getDecimalValueFromByteBuf(SlicedByteBuf& data, size_t startIndex, int nDecimalDigits, int scale, bool truncateScale)
{
// For sparse decimal type we have padded zeroes at the end, strip them while converting to BigDecimal.
int32_t actualDigits;
// Indicate if we need an endianness swap (truncateScale = true for sparse types, which don't need an endianness swap).
bool needsEndiannessSwap = !truncateScale;
// Initialize the BigDecimal, first digit in the ByteBuf has the sign so mask it out
cpp_int decimalDigits = (needsEndiannessSwap ?
bswap_32(data.getUint32(startIndex)) & 0x7FFFFFFF :
(data.getUint32(startIndex) & 0x7FFFFFFF));
cpp_int base(DIGITS_BASE);
for (int i = 1; i < nDecimalDigits; i++) {
// Note: we need to byteswap when we retrieve integers off the wire since they are
// stored in big-endian format.
cpp_int temp = (needsEndiannessSwap ?
bswap_32(data.getUint32(startIndex + (i * INTEGER_SIZE))) :
(data.getUint32(startIndex + (i * INTEGER_SIZE))));
decimalDigits *= base;
decimalDigits += temp;
}
// Truncate any additional padding we might have added
if (truncateScale && scale > 0 && (actualDigits = scale % MAX_DIGITS) != 0) {
cpp_int truncate = (int32_t) std::pow(10.0, (MAX_DIGITS - actualDigits));
decimalDigits /= truncate;
}
DecimalValue val;
val.m_unscaledValue = decimalDigits;
// set the sign
if ((data.getUint32(startIndex) & 0x80000000) != 0)
{
val.m_unscaledValue *= -1;
}
val.m_scale = scale;
return val;
}
DecimalValue getDecimalValueFromDense(SlicedByteBuf& data, size_t startIndex, int nDecimalDigits, int scale, int maxPrecision, int width)
{
/* This method converts the dense representation to
* an intermediate representation. The intermediate
* representation has one more integer than the dense
* representation.
*/
std::vector<uint8_t> intermediateBytes((nDecimalDigits + 1) * INTEGER_SIZE, 0);
int32_t intermediateIndex = 3;
int32_t mask[] = {0x03, 0x0F, 0x3F, 0xFF};
int32_t reverseMask[] = {0xFC, 0xF0, 0xC0, 0x00};
int32_t maskIndex;
int32_t shiftOrder;
uint8_t shiftBits;
// TODO: Some of the logic here is common with casting from Dense to Sparse types, factor out common code
if (maxPrecision == 38) {
maskIndex = 0;
shiftOrder = 6;
shiftBits = 0x00;
intermediateBytes[intermediateIndex++] = (uint8_t) (data.getByte(startIndex) & 0x7F);
} else if (maxPrecision == 28) {
maskIndex = 1;
shiftOrder = 4;
shiftBits = (uint8_t) ((data.getByte(startIndex) & 0x03) << shiftOrder);
intermediateBytes[intermediateIndex++] = (uint8_t) (((data.getByte(startIndex) & 0x3C) & 0xFF) >> 2);
} else {
assert("Dense types with max precision 38 and 28 are only supported");
}
int32_t inputIndex = 1;
bool sign = false;
if ((data.getByte(startIndex) & 0x80) != 0) {
sign = true;
}
while (inputIndex < width) {
intermediateBytes[intermediateIndex] = (uint8_t) ((shiftBits) | (((data.getByte(startIndex + inputIndex) & reverseMask[maskIndex]) & 0xFF) >> (8 - shiftOrder)));
shiftBits = (uint8_t) ((data.getByte(startIndex + inputIndex) & mask[maskIndex]) << shiftOrder);
inputIndex++;
intermediateIndex++;
if (((inputIndex - 1) % INTEGER_SIZE) == 0) {
shiftBits = (uint8_t) ((shiftBits & 0xFF) >> 2);
maskIndex++;
shiftOrder -= 2;
}
}
/* copy the last byte */
intermediateBytes[intermediateIndex] = shiftBits;
if (sign) {
intermediateBytes[0] = (uint8_t) (intermediateBytes[0] | 0x80);
}
SlicedByteBuf intermediateData(&intermediateBytes[0], 0, intermediateBytes.size());
return getDecimalValueFromIntermediate(intermediateData, 0, nDecimalDigits + 1, scale);
}
}