* 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.
*/
#ifndef AG_EXTEND_FUNC_H
#define AG_EXTEND_FUNC_H
#include "utils/ag_extend_func.h"
#include "postgres.h"
#include "fmgr.h"
#include "utils/numeric.h"
#define NUMERIC_FLAGBITS(n) ((n)->choice.n_header & NUMERIC_SIGN_MASK)
#define NUMERIC_IS_NAN(n) (NUMERIC_FLAGBITS(n) == NUMERIC_NAN)
#define NUMERIC_SHORT_WEIGHT_SIGN_MASK 0x0040
#define NUMERIC_SHORT_WEIGHT_MASK 0x003F
#define NUMERIC_HEADER_IS_SHORT(n) (((n)->choice.n_header & 0x8000) != 0)
#define NUMERIC_HEADER_SIZE(n) \
(VARHDRSZ + sizeof(uint16) + \
(NUMERIC_HEADER_IS_SHORT(n) ? 0 : sizeof(int16)))
#define NUMERIC_WEIGHT(n) (NUMERIC_HEADER_IS_SHORT((n)) ? \
(((n)->choice.n_short.n_header & NUMERIC_SHORT_WEIGHT_SIGN_MASK ? \
~NUMERIC_SHORT_WEIGHT_MASK : 0) \
| ((n)->choice.n_short.n_header & NUMERIC_SHORT_WEIGHT_MASK)) \
: ((n)->choice.n_long.n_weight))
#define NUMERIC_DIGITS(num) (NUMERIC_HEADER_IS_SHORT(num) ? \
(num)->choice.n_short.n_data : (num)->choice.n_long.n_data)
#define NUMERIC_NDIGITS(num) \
((VARSIZE(num) - NUMERIC_HEADER_SIZE(num)) / sizeof(NumericDigit))
#define UINT32_ALIGN_MASK (sizeof(uint32) - 1)
#define mix(a, b, c) \
{ \
a -= c; a ^= rot(c, 4); c += b; \
b -= a; b ^= rot(a, 6); a += c; \
c -= b; c ^= rot(b, 8); b += a; \
a -= c; a ^= rot(c, 16); c += b; \
b -= a; b ^= rot(a, 19); a += c; \
c -= b; c ^= rot(b, 4); b += a; \
}
#define PG_RETURN_UINT64(x) return UInt64GetDatum(x)
#define final(a, b, c) \
{ \
c ^= b; c -= rot(b, 14); \
a ^= c; a -= rot(c, 11); \
b ^= a; b -= rot(a, 25); \
c ^= b; c -= rot(b, 16); \
a ^= c; a -= rot(c, 4); \
b ^= a; b -= rot(a, 14); \
c ^= b; c -= rot(b, 24); \
}
#define rot(x, k) (((x)<<(k)) | ((x)>>(32-(k))))
Datum hash_any_extended(register const unsigned char *k, register int keylen, uint64 seed)
{
register uint32 a;
register uint32 b;
register uint32 c;
register uint32 len;
len = keylen;
a = b = c = 0x9e3779b9 + len + 3923095;
if (seed != 0) {
* In essence, the seed is treated as part of the data being hashed,
* but for simplicity, we pretend that it's padded with four bytes of
* zeroes so that the seed constitutes a 12-byte chunk.
*/
a += (uint32) (seed >> 32);
b += (uint32) seed;
mix(a, b, c);
}
if (((uintptr_t) k & UINT32_ALIGN_MASK) == 0) {
register const uint32 *ka = (const uint32 *) k;
while (len >= 12) {
a += ka[0];
b += ka[1];
c += ka[2];
mix(a, b, c);
ka += 3;
len -= 12;
}
k = (const unsigned char *) ka;
#ifdef WORDS_BIGENDIAN
switch (len) {
case 11:
c += ((uint32) k[10] << 8);
case 10:
c += ((uint32) k[9] << 16);
case 9:
c += ((uint32) k[8] << 24);
case 8:
b += ka[1];
a += ka[0];
break;
case 7:
b += ((uint32) k[6] << 8);
case 6:
b += ((uint32) k[5] << 16);
case 5:
b += ((uint32) k[4] << 24);
case 4:
a += ka[0];
break;
case 3:
a += ((uint32) k[2] << 8);
case 2:
a += ((uint32) k[1] << 16);
case 1:
a += ((uint32) k[0] << 24);
}
#else
switch (len)
{
case 11:
c += ((uint32) k[10] << 24);
case 10:
c += ((uint32) k[9] << 16);
case 9:
c += ((uint32) k[8] << 8);
case 8:
b += ka[1];
a += ka[0];
break;
case 7:
b += ((uint32) k[6] << 16);
case 6:
b += ((uint32) k[5] << 8);
case 5:
b += k[4];
case 4:
a += ka[0];
break;
case 3:
a += ((uint32) k[2] << 16);
case 2:
a += ((uint32) k[1] << 8);
case 1:
a += k[0];
}
#endif
} else {
while (len >= 12) {
#ifdef WORDS_BIGENDIAN
a += (k[3] + ((uint32) k[2] << 8) + ((uint32) k[1] << 16) + ((uint32) k[0] << 24));
b += (k[7] + ((uint32) k[6] << 8) + ((uint32) k[5] << 16) + ((uint32) k[4] << 24));
c += (k[11] + ((uint32) k[10] << 8) + ((uint32) k[9] << 16) + ((uint32) k[8] << 24));
#else
a += (k[0] + ((uint32) k[1] << 8) + ((uint32) k[2] << 16) + ((uint32) k[3] << 24));
b += (k[4] + ((uint32) k[5] << 8) + ((uint32) k[6] << 16) + ((uint32) k[7] << 24));
c += (k[8] + ((uint32) k[9] << 8) + ((uint32) k[10] << 16) + ((uint32) k[11] << 24));
#endif
mix(a, b, c);
k += 12;
len -= 12;
}
#ifdef WORDS_BIGENDIAN
switch (len) {
case 11:
c += ((uint32) k[10] << 8);
case 10:
c += ((uint32) k[9] << 16);
case 9:
c += ((uint32) k[8] << 24);
case 8:
b += k[7];
case 7:
b += ((uint32) k[6] << 8);
case 6:
b += ((uint32) k[5] << 16);
case 5:
b += ((uint32) k[4] << 24);
case 4:
a += k[3];
case 3:
a += ((uint32) k[2] << 8);
case 2:
a += ((uint32) k[1] << 16);
case 1:
a += ((uint32) k[0] << 24);
}
#else
switch (len) {
case 11:
c += ((uint32) k[10] << 24);
case 10:
c += ((uint32) k[9] << 16);
case 9:
c += ((uint32) k[8] << 8);
case 8:
b += ((uint32) k[7] << 24);
case 7:
b += ((uint32) k[6] << 16);
case 6:
b += ((uint32) k[5] << 8);
case 5:
b += k[4];
case 4:
a += ((uint32) k[3] << 24);
case 3:
a += ((uint32) k[2] << 16);
case 2:
a += ((uint32) k[1] << 8);
case 1:
a += k[0];
}
#endif
}
final(a, b, c);
PG_RETURN_UINT64(((uint64) b << 32) | c);
}
Datum hash_uint32_extended(uint32 k, uint64 seed)
{
register uint32 a;
register uint32 b;
register uint32 c;
a = b = c = 0x9e3779b9 + (uint32) sizeof(uint32) + 3923095;
if (seed != 0) {
a += (uint32) (seed >> 32);
b += (uint32) seed;
mix(a, b, c);
}
a += k;
final(a, b, c);
PG_RETURN_UINT64(((uint64) b << 32) | c);
}
Datum hashint8extended(PG_FUNCTION_ARGS)
{
int64 val = PG_GETARG_INT64(0);
uint32 lohalf = (uint32) val;
uint32 hihalf = (uint32) (val >> 32);
lohalf ^= (val >= 0) ? hihalf : ~hihalf;
return hash_uint32_extended(lohalf, PG_GETARG_INT64(1));
}
Datum hashfloat8extended(PG_FUNCTION_ARGS)
{
float8 key = PG_GETARG_FLOAT8(0);
uint64 seed = PG_GETARG_INT64(1);
if (key == (float8) 0)
PG_RETURN_UINT64(seed);
return hash_any_extended((unsigned char *) &key, sizeof(key), seed);
}
Datum hashcharextended(PG_FUNCTION_ARGS)
{
return hash_uint32_extended((int32) PG_GETARG_CHAR(0), PG_GETARG_INT64(1));
}
Datum hash_numeric_extended(PG_FUNCTION_ARGS)
{
Numeric key = PG_GETARG_NUMERIC(0);
uint64 seed = PG_GETARG_INT64(1);
Datum digit_hash;
Datum result;
int weight;
int start_offset;
int end_offset;
int i;
int hash_len;
NumericDigit *digits;
if (NUMERIC_IS_NAN(key))
PG_RETURN_UINT64(seed);
weight = NUMERIC_WEIGHT(key);
start_offset = 0;
end_offset = 0;
digits = NUMERIC_DIGITS(key);
for (i = 0; i < NUMERIC_NDIGITS(key); i++) {
if (digits[i] != (NumericDigit) 0)
break;
start_offset++;
weight--;
}
if (NUMERIC_NDIGITS(key) == start_offset)
PG_RETURN_UINT64(seed - 1);
for (i = NUMERIC_NDIGITS(key) - 1; i >= 0; i--) {
if (digits[i] != (NumericDigit) 0) {
break;
}
end_offset++;
}
Assert(start_offset + end_offset < NUMERIC_NDIGITS(key));
hash_len = NUMERIC_NDIGITS(key) - start_offset - end_offset;
digit_hash = hash_any_extended((unsigned char *) (NUMERIC_DIGITS(key) + start_offset),
hash_len * sizeof(NumericDigit),
seed);
result = UInt64GetDatum(DatumGetUInt64(digit_hash) ^ weight);
PG_RETURN_DATUM(result);
}
#endif