Your IP : 52.14.125.137
/* Copyright (c) 2018, 2021, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
#ifndef SQL_TYPE_INT_INCLUDED
#define SQL_TYPE_INT_INCLUDED
#include "my_bit.h" // my_count_bits()
class Null_flag
{
protected:
bool m_is_null;
public:
bool is_null() const { return m_is_null; }
Null_flag(bool is_null) :m_is_null(is_null) { }
};
class Longlong
{
protected:
longlong m_value;
public:
longlong value() const { return m_value; }
Longlong(longlong nr) :m_value(nr) { }
ulonglong abs()
{
if (m_value == LONGLONG_MIN) // avoid undefined behavior
return ((ulonglong) LONGLONG_MAX) + 1;
return m_value < 0 ? -m_value : m_value;
}
};
class Longlong_null: public Longlong, public Null_flag
{
public:
Longlong_null(longlong nr, bool is_null)
:Longlong(nr), Null_flag(is_null)
{ }
explicit Longlong_null()
:Longlong(0), Null_flag(true)
{ }
explicit Longlong_null(longlong nr)
:Longlong(nr), Null_flag(false)
{ }
Longlong_null operator|(const Longlong_null &other) const
{
if (is_null() || other.is_null())
return Longlong_null();
return Longlong_null(value() | other.value());
}
Longlong_null operator&(const Longlong_null &other) const
{
if (is_null() || other.is_null())
return Longlong_null();
return Longlong_null(value() & other.value());
}
Longlong_null operator^(const Longlong_null &other) const
{
if (is_null() || other.is_null())
return Longlong_null();
return Longlong_null((longlong) (value() ^ other.value()));
}
Longlong_null operator~() const
{
if (is_null())
return *this;
return Longlong_null((longlong) ~ (ulonglong) value());
}
Longlong_null operator<<(const Longlong_null &llshift) const
{
ulonglong res;
uint shift;
if (is_null() || llshift.is_null())
return Longlong_null();
shift= (uint) llshift.value();
res= 0;
if (shift < sizeof(longlong) * 8)
res= ((ulonglong) value()) << shift;
return Longlong_null((longlong) res);
}
Longlong_null operator>>(const Longlong_null &llshift) const
{
ulonglong res;
uint shift;
if (is_null() || llshift.is_null())
return Longlong_null();
shift= (uint) llshift.value();
res= 0;
if (shift < sizeof(longlong) * 8)
res= ((ulonglong) value()) >> shift;
return Longlong_null(res);
}
Longlong_null bit_count() const
{
if (is_null())
return *this;
return Longlong_null((longlong) my_count_bits((ulonglong) value()));
}
};
class ULonglong
{
protected:
ulonglong m_value;
public:
ulonglong value() const { return m_value; }
explicit ULonglong(ulonglong nr) :m_value(nr) { }
static bool test_if_sum_overflows_ull(ulonglong arg1, ulonglong arg2)
{
return ULONGLONG_MAX - arg1 < arg2;
}
Longlong_null operator-() const
{
if (m_value > (ulonglong) LONGLONG_MAX) // Avoid undefined behaviour
{
return m_value == (ulonglong) LONGLONG_MAX + 1 ?
Longlong_null(LONGLONG_MIN, false) :
Longlong_null(0, true);
}
return Longlong_null(-(longlong) m_value, false);
}
// Convert to Longlong_null with the range check
Longlong_null to_longlong_null() const
{
if (m_value > (ulonglong) LONGLONG_MAX)
return Longlong_null(0, true);
return Longlong_null((longlong) m_value, false);
}
};
class ULonglong_null: public ULonglong, public Null_flag
{
public:
ULonglong_null(ulonglong nr, bool is_null)
:ULonglong(nr), Null_flag(is_null)
{ }
/*
Multiply two ulonglong values.
Let a = a1 * 2^32 + a0 and b = b1 * 2^32 + b0. Then
a * b = (a1 * 2^32 + a0) * (b1 * 2^32 + b0) = a1 * b1 * 2^64 +
+ (a1 * b0 + a0 * b1) * 2^32 + a0 * b0;
We can determine if the above sum overflows the ulonglong range by
sequentially checking the following conditions:
1. If both a1 and b1 are non-zero.
2. Otherwise, if (a1 * b0 + a0 * b1) is greater than ULONG_MAX.
3. Otherwise, if (a1 * b0 + a0 * b1) * 2^32 + a0 * b0 is greater than
ULONGLONG_MAX.
*/
static ULonglong_null ullmul(ulonglong a, ulonglong b)
{
ulong a1= (ulong)(a >> 32);
ulong b1= (ulong)(b >> 32);
if (a1 && b1)
return ULonglong_null(0, true);
ulong a0= (ulong)(0xFFFFFFFFUL & a);
ulong b0= (ulong)(0xFFFFFFFFUL & b);
ulonglong res1= (ulonglong) a1 * b0 + (ulonglong) a0 * b1;
if (res1 > 0xFFFFFFFFUL)
return ULonglong_null(0, true);
res1= res1 << 32;
ulonglong res0= (ulonglong) a0 * b0;
if (test_if_sum_overflows_ull(res1, res0))
return ULonglong_null(0, true);
return ULonglong_null(res1 + res0, false);
}
};
// A longlong/ulonglong hybrid. Good to store results of val_int().
class Longlong_hybrid: public Longlong
{
protected:
bool m_unsigned;
int cmp_signed(const Longlong_hybrid& other) const
{
return m_value < other.m_value ? -1 : m_value == other.m_value ? 0 : 1;
}
int cmp_unsigned(const Longlong_hybrid& other) const
{
return (ulonglong) m_value < (ulonglong) other.m_value ? -1 :
m_value == other.m_value ? 0 : 1;
}
public:
Longlong_hybrid(longlong nr, bool unsigned_flag)
:Longlong(nr), m_unsigned(unsigned_flag)
{ }
bool is_unsigned() const { return m_unsigned; }
bool is_unsigned_outside_of_signed_range() const
{
return m_unsigned && ((ulonglong) m_value) > (ulonglong) LONGLONG_MAX;
}
bool neg() const { return m_value < 0 && !m_unsigned; }
ulonglong abs() const
{
if (m_unsigned)
return (ulonglong) m_value;
return Longlong(m_value).abs();
}
/*
Convert to an unsigned number:
- Negative numbers are converted to 0.
- Positive numbers bigger than upper_bound are converted to upper_bound.
- Other numbers are returned as is.
*/
ulonglong to_ulonglong(ulonglong upper_bound) const
{
return neg() ? 0 :
(ulonglong) m_value > upper_bound ? upper_bound :
(ulonglong) m_value;
}
uint to_uint(uint upper_bound) const
{
return (uint) to_ulonglong(upper_bound);
}
Longlong_null val_int_signed() const
{
if (m_unsigned)
return ULonglong((ulonglong) m_value).to_longlong_null();
return Longlong_null(m_value, false);
}
Longlong_null val_int_unsigned() const
{
if (!m_unsigned && m_value < 0)
return Longlong_null(0, true);
return Longlong_null(m_value, false);
}
/*
Return in Item compatible val_int() format:
- signed numbers as a straight longlong value
- unsigned numbers as a ulonglong value reinterpreted to longlong
*/
Longlong_null val_int(bool want_unsigned_value) const
{
return want_unsigned_value ? val_int_unsigned() :
val_int_signed();
}
int cmp(const Longlong_hybrid& other) const
{
if (m_unsigned == other.m_unsigned)
return m_unsigned ? cmp_unsigned(other) : cmp_signed(other);
if (is_unsigned_outside_of_signed_range())
return 1;
if (other.is_unsigned_outside_of_signed_range())
return -1;
/*
The unsigned argument is in the range 0..LONGLONG_MAX.
The signed argument is in the range LONGLONG_MIN..LONGLONG_MAX.
Safe to compare as signed.
*/
return cmp_signed(other);
}
bool operator==(const Longlong_hybrid &nr) const
{
return cmp(nr) == 0;
}
bool operator==(ulonglong nr) const
{
return cmp(Longlong_hybrid((longlong) nr, true)) == 0;
}
bool operator==(uint nr) const
{
return cmp(Longlong_hybrid((longlong) nr, true)) == 0;
}
bool operator==(longlong nr) const
{
return cmp(Longlong_hybrid(nr, false)) == 0;
}
bool operator==(int nr) const
{
return cmp(Longlong_hybrid(nr, false)) == 0;
}
};
class Longlong_hybrid_null: public Longlong_hybrid,
public Null_flag
{
public:
Longlong_hybrid_null(const Longlong_null &nr, bool unsigned_flag)
:Longlong_hybrid(nr.value(), unsigned_flag),
Null_flag(nr.is_null())
{ }
};
/*
Stores the absolute value of a number, and the sign.
Value range: -ULONGLONG_MAX .. +ULONGLONG_MAX.
Provides a wider range for negative numbers than Longlong_hybrid does.
Usefull to store intermediate results of an expression whose value
is further needed to be negated. For example, these methods:
- Item_func_mul::int_op()
- Item_func_int_div::val_int()
- Item_func_mod::int_op()
calculate the result of absolute values of the arguments,
then optionally negate the result.
*/
class ULonglong_hybrid: public ULonglong
{
bool m_neg;
public:
ULonglong_hybrid(ulonglong value, bool neg)
:ULonglong(value), m_neg(neg)
{
if (m_neg && !m_value)
m_neg= false; // convert -0 to +0
}
Longlong_null val_int_unsigned() const
{
return m_neg ? Longlong_null(0, true) :
Longlong_null((longlong) m_value, false);
}
Longlong_null val_int_signed() const
{
return m_neg ? -ULonglong(m_value) : ULonglong::to_longlong_null();
}
/*
Return in Item compatible val_int() format:
- signed numbers as a straight longlong value
- unsigned numbers as a ulonglong value reinterpreted to longlong
*/
Longlong_null val_int(bool want_unsigned_value) const
{
return want_unsigned_value ? val_int_unsigned() :
val_int_signed();
}
};
#endif // SQL_TYPE_INT_INCLUDED