blob: 3433cb1498849c8e7d46fb198f8d15403a7adb47 [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 "exprs/bit-byte-functions.h"
#include <boost/type_traits/make_unsigned.hpp>
#include "gutil/strings/substitute.h"
#include "util/bit-util.h"
#include "common/names.h"
using namespace impala_udf;
using boost::make_unsigned;
using impala::BitUtil;
namespace impala {
// Generic algorithm for shifting and rotating signed integers
// Declare here to resolve mutual recursion
template<typename T>
static T RotateLeftImpl(T v, int32_t shift);
template<typename T>
static T RotateRightImpl(T v, int32_t shift);
template<typename T>
static T ShiftLeftImpl(T v, int32_t shift);
template<typename T>
static T ShiftRightLogicalImpl(T v, int32_t shift);
template <typename T>
IntVal BitByteFunctions::CountSet(FunctionContext* ctx, const T& v) {
if (v.is_null) return IntVal::null();
return IntVal(BitUtil::PopcountSigned(v.val));
}
template IntVal BitByteFunctions::CountSet(FunctionContext*, const TinyIntVal&);
template IntVal BitByteFunctions::CountSet(FunctionContext*, const SmallIntVal&);
template IntVal BitByteFunctions::CountSet(FunctionContext*, const IntVal&);
template IntVal BitByteFunctions::CountSet(FunctionContext*, const BigIntVal&);
template <typename T>
IntVal BitByteFunctions::CountSet(FunctionContext* ctx, const T& v, const IntVal &bitval) {
if (v.is_null || bitval.is_null) return IntVal::null();
if (bitval.val == 0) {
return IntVal(sizeof(v.val) * 8 - BitUtil::PopcountSigned(v.val));
} else if (bitval.val == 1) {
return IntVal(BitUtil::PopcountSigned(v.val));
}
ctx->SetError(Substitute("Invalid bit val: $0", bitval.val).c_str());
return IntVal::null();
}
template IntVal BitByteFunctions::CountSet(FunctionContext*, const TinyIntVal&,
const IntVal&);
template IntVal BitByteFunctions::CountSet(FunctionContext*, const SmallIntVal&,
const IntVal&);
template IntVal BitByteFunctions::CountSet(FunctionContext*, const IntVal&,
const IntVal&);
template IntVal BitByteFunctions::CountSet(FunctionContext*, const BigIntVal&,
const IntVal&);
template<typename T>
TinyIntVal BitByteFunctions::GetBit(FunctionContext* ctx, const T& v,
const IntVal& bitpos) {
if (v.is_null || bitpos.is_null) return TinyIntVal::null();
if (bitpos.val < 0 || bitpos.val >= sizeof(v.val) * 8) {
ctx->SetError(Substitute("Invalid bit position: $0", bitpos.val).c_str());
return TinyIntVal::null();
}
return TinyIntVal(BitUtil::GetBit(v.val, bitpos.val));
}
template TinyIntVal BitByteFunctions::GetBit(FunctionContext*, const TinyIntVal&,
const IntVal&);
template TinyIntVal BitByteFunctions::GetBit(FunctionContext*, const SmallIntVal&,
const IntVal&);
template TinyIntVal BitByteFunctions::GetBit(FunctionContext*, const IntVal&,
const IntVal&);
template TinyIntVal BitByteFunctions::GetBit(FunctionContext*, const BigIntVal&,
const IntVal&);
template<typename T>
T BitByteFunctions::SetBit(FunctionContext* ctx, const T& v,
const IntVal& bitpos) {
if (v.is_null || bitpos.is_null) return T::null();
if (bitpos.val < 0 || bitpos.val >= sizeof(v.val) * 8) {
ctx->SetError(Substitute("Invalid bit position: $0", bitpos.val).c_str());
return T::null();
}
return T(BitUtil::SetBit(v.val, bitpos.val));
}
template<typename T>
T BitByteFunctions::SetBit(FunctionContext* ctx, const T& v,
const IntVal& bitpos, const IntVal& bitval) {
if (v.is_null || bitpos.is_null || bitval.is_null) return T::null();
if (bitpos.val < 0 || bitpos.val >= sizeof(v.val) * 8) {
ctx->SetError(Substitute("Invalid bit position: $0", bitpos.val).c_str());
return T::null();
}
if (bitval.val == 0) {
return T(BitUtil::UnsetBit(v.val, bitpos.val));
} else if (bitval.val == 1) {
return T(BitUtil::SetBit(v.val, bitpos.val));
}
ctx->SetError(Substitute("Invalid bit val: $0", bitval.val).c_str());
return T::null();
}
template TinyIntVal BitByteFunctions::SetBit(FunctionContext*, const TinyIntVal&,
const IntVal&);
template SmallIntVal BitByteFunctions::SetBit(FunctionContext*, const SmallIntVal&,
const IntVal&);
template IntVal BitByteFunctions::SetBit(FunctionContext*, const IntVal&, const IntVal&);
template BigIntVal BitByteFunctions::SetBit(FunctionContext*, const BigIntVal&,
const IntVal&);
template TinyIntVal BitByteFunctions::SetBit(FunctionContext*, const TinyIntVal&,
const IntVal&, const IntVal&);
template SmallIntVal BitByteFunctions::SetBit(FunctionContext*, const SmallIntVal&,
const IntVal&, const IntVal&);
template IntVal BitByteFunctions::SetBit(FunctionContext*, const IntVal&, const IntVal&,
const IntVal&);
template BigIntVal BitByteFunctions::SetBit(FunctionContext*, const BigIntVal&,
const IntVal&, const IntVal&);
template<typename T>
static T RotateLeftImpl(T v, int32_t shift) {
// Handle negative shifts
if (shift < 0) return RotateRightImpl(v, -shift);
// Handle wrapping around multiple times
shift = shift % (sizeof(T) * 8);
return static_cast<T>((static_cast<std::make_unsigned_t<T>>(v) << shift)
| BitUtil::ShiftRightLogical(v, sizeof(T) * 8 - shift));
}
template<typename T>
static T RotateRightImpl(T v, int32_t shift) {
// Handle negative shifts
if (shift < 0) return RotateLeftImpl(v, -shift);
// Handle wrapping around multiple times
shift = shift % (sizeof(T) * 8);
return static_cast<T>(BitUtil::ShiftRightLogical(v, shift)
| (static_cast<std::make_unsigned_t<T>>(v) << (sizeof(T) * 8 - shift)));
}
template<typename T>
static T ShiftLeftImpl(T v, int32_t shift) {
if (shift < 0) return ShiftRightLogicalImpl(v, -shift);
return static_cast<T>(static_cast<std::make_unsigned_t<T>>(v) << shift);
}
// Logical right shift rather than arithmetic right shift
template<typename T>
static T ShiftRightLogicalImpl(T v, int32_t shift) {
if (shift < 0) return ShiftLeftImpl(v, -shift);
// Conversion to unsigned ensures most significant bits always filled with 0's
return BitUtil::ShiftRightLogical(v, shift);
}
// Generates a shift/rotate function for Impala integer value type based on the shift
// algorithm implemented by ALGO
#define SHIFT_FN(NAME, INPUT_TYPE, ALGO) \
INPUT_TYPE BitByteFunctions::NAME(FunctionContext* ctx, const INPUT_TYPE& v, \
const IntVal& shift) { \
if (v.is_null || shift.is_null) return INPUT_TYPE::null(); \
return INPUT_TYPE(ALGO(v.val, shift.val)); \
}
SHIFT_FN(RotateLeft, TinyIntVal, RotateLeftImpl);
SHIFT_FN(RotateLeft, SmallIntVal, RotateLeftImpl);
SHIFT_FN(RotateLeft, IntVal, RotateLeftImpl);
SHIFT_FN(RotateLeft, BigIntVal, RotateLeftImpl);
SHIFT_FN(RotateRight, TinyIntVal, RotateRightImpl);
SHIFT_FN(RotateRight, SmallIntVal, RotateRightImpl);
SHIFT_FN(RotateRight, IntVal, RotateRightImpl);
SHIFT_FN(RotateRight, BigIntVal, RotateRightImpl);
SHIFT_FN(ShiftLeft, TinyIntVal, ShiftLeftImpl);
SHIFT_FN(ShiftLeft, SmallIntVal, ShiftLeftImpl);
SHIFT_FN(ShiftLeft, IntVal, ShiftLeftImpl);
SHIFT_FN(ShiftLeft, BigIntVal, ShiftLeftImpl);
SHIFT_FN(ShiftRight, TinyIntVal, ShiftRightLogicalImpl);
SHIFT_FN(ShiftRight, SmallIntVal, ShiftRightLogicalImpl);
SHIFT_FN(ShiftRight, IntVal, ShiftRightLogicalImpl);
SHIFT_FN(ShiftRight, BigIntVal, ShiftRightLogicalImpl);
}