mirror of
https://github.com/dragonflydb/dragonfly.git
synced 2025-05-11 10:25:47 +02:00
chore: import geo related files to Dragonfly repo (#1540)
Taken from redis source repo, tag: 7.0.8. Signed-off-by: Roman Gershman <roman@dragonflydb.io>
This commit is contained in:
parent
e2b61a3839
commit
4a38fb7786
9 changed files with 2045 additions and 2 deletions
|
@ -8,9 +8,10 @@ else()
|
|||
set(ZMALLOC_DEPS "")
|
||||
endif()
|
||||
|
||||
add_library(redis_lib crc16.c crc64.c crcspeed.c debug.c dict.c intset.c
|
||||
add_library(redis_lib crc16.c crc64.c crcspeed.c debug.c dict.c intset.c geo.c
|
||||
geohash.c geohash_helper.c
|
||||
listpack.c mt19937-64.c object.c lzf_c.c lzf_d.c sds.c
|
||||
quicklist.c rax.c redis_aux.c siphash.c t_hash.c t_stream.c t_zset.c
|
||||
quicklist.c rax.c pqsort.c redis_aux.c siphash.c t_hash.c t_stream.c t_zset.c
|
||||
util.c ziplist.c hyperloglog.c ${ZMALLOC_SRC})
|
||||
|
||||
cxx_link(redis_lib ${ZMALLOC_DEPS})
|
||||
|
|
1016
src/redis/geo.c
Normal file
1016
src/redis/geo.c
Normal file
File diff suppressed because it is too large
Load diff
22
src/redis/geo.h
Normal file
22
src/redis/geo.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
#ifndef __GEO_H__
|
||||
#define __GEO_H__
|
||||
|
||||
#include <stddef.h> /* for size_t */
|
||||
|
||||
/* Structures used inside geo.c in order to represent points and array of
|
||||
* points on the earth. */
|
||||
typedef struct geoPoint {
|
||||
double longitude;
|
||||
double latitude;
|
||||
double dist;
|
||||
double score;
|
||||
char *member;
|
||||
} geoPoint;
|
||||
|
||||
typedef struct geoArray {
|
||||
struct geoPoint *array;
|
||||
size_t buckets;
|
||||
size_t used;
|
||||
} geoArray;
|
||||
|
||||
#endif
|
299
src/redis/geohash.c
Normal file
299
src/redis/geohash.c
Normal file
|
@ -0,0 +1,299 @@
|
|||
/*
|
||||
* Copyright (c) 2013-2014, yinqiwen <yinqiwen@gmail.com>
|
||||
* Copyright (c) 2014, Matt Stancliff <matt@genges.com>.
|
||||
* Copyright (c) 2015-2016, Salvatore Sanfilippo <antirez@gmail.com>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include "geohash.h"
|
||||
|
||||
/**
|
||||
* Hashing works like this:
|
||||
* Divide the world into 4 buckets. Label each one as such:
|
||||
* -----------------
|
||||
* | | |
|
||||
* | | |
|
||||
* | 0,1 | 1,1 |
|
||||
* -----------------
|
||||
* | | |
|
||||
* | | |
|
||||
* | 0,0 | 1,0 |
|
||||
* -----------------
|
||||
*/
|
||||
|
||||
/* Interleave lower bits of x and y, so the bits of x
|
||||
* are in the even positions and bits from y in the odd;
|
||||
* x and y must initially be less than 2**32 (4294967296).
|
||||
* From: https://graphics.stanford.edu/~seander/bithacks.html#InterleaveBMN
|
||||
*/
|
||||
static inline uint64_t interleave64(uint32_t xlo, uint32_t ylo) {
|
||||
static const uint64_t B[] = {0x5555555555555555ULL, 0x3333333333333333ULL,
|
||||
0x0F0F0F0F0F0F0F0FULL, 0x00FF00FF00FF00FFULL,
|
||||
0x0000FFFF0000FFFFULL};
|
||||
static const unsigned int S[] = {1, 2, 4, 8, 16};
|
||||
|
||||
uint64_t x = xlo;
|
||||
uint64_t y = ylo;
|
||||
|
||||
x = (x | (x << S[4])) & B[4];
|
||||
y = (y | (y << S[4])) & B[4];
|
||||
|
||||
x = (x | (x << S[3])) & B[3];
|
||||
y = (y | (y << S[3])) & B[3];
|
||||
|
||||
x = (x | (x << S[2])) & B[2];
|
||||
y = (y | (y << S[2])) & B[2];
|
||||
|
||||
x = (x | (x << S[1])) & B[1];
|
||||
y = (y | (y << S[1])) & B[1];
|
||||
|
||||
x = (x | (x << S[0])) & B[0];
|
||||
y = (y | (y << S[0])) & B[0];
|
||||
|
||||
return x | (y << 1);
|
||||
}
|
||||
|
||||
/* reverse the interleave process
|
||||
* derived from http://stackoverflow.com/questions/4909263
|
||||
*/
|
||||
static inline uint64_t deinterleave64(uint64_t interleaved) {
|
||||
static const uint64_t B[] = {0x5555555555555555ULL, 0x3333333333333333ULL,
|
||||
0x0F0F0F0F0F0F0F0FULL, 0x00FF00FF00FF00FFULL,
|
||||
0x0000FFFF0000FFFFULL, 0x00000000FFFFFFFFULL};
|
||||
static const unsigned int S[] = {0, 1, 2, 4, 8, 16};
|
||||
|
||||
uint64_t x = interleaved;
|
||||
uint64_t y = interleaved >> 1;
|
||||
|
||||
x = (x | (x >> S[0])) & B[0];
|
||||
y = (y | (y >> S[0])) & B[0];
|
||||
|
||||
x = (x | (x >> S[1])) & B[1];
|
||||
y = (y | (y >> S[1])) & B[1];
|
||||
|
||||
x = (x | (x >> S[2])) & B[2];
|
||||
y = (y | (y >> S[2])) & B[2];
|
||||
|
||||
x = (x | (x >> S[3])) & B[3];
|
||||
y = (y | (y >> S[3])) & B[3];
|
||||
|
||||
x = (x | (x >> S[4])) & B[4];
|
||||
y = (y | (y >> S[4])) & B[4];
|
||||
|
||||
x = (x | (x >> S[5])) & B[5];
|
||||
y = (y | (y >> S[5])) & B[5];
|
||||
|
||||
return x | (y << 32);
|
||||
}
|
||||
|
||||
void geohashGetCoordRange(GeoHashRange *long_range, GeoHashRange *lat_range) {
|
||||
/* These are constraints from EPSG:900913 / EPSG:3785 / OSGEO:41001 */
|
||||
/* We can't geocode at the north/south pole. */
|
||||
long_range->max = GEO_LONG_MAX;
|
||||
long_range->min = GEO_LONG_MIN;
|
||||
lat_range->max = GEO_LAT_MAX;
|
||||
lat_range->min = GEO_LAT_MIN;
|
||||
}
|
||||
|
||||
int geohashEncode(const GeoHashRange *long_range, const GeoHashRange *lat_range,
|
||||
double longitude, double latitude, uint8_t step,
|
||||
GeoHashBits *hash) {
|
||||
/* Check basic arguments sanity. */
|
||||
if (hash == NULL || step > 32 || step == 0 ||
|
||||
RANGEPISZERO(lat_range) || RANGEPISZERO(long_range)) return 0;
|
||||
|
||||
/* Return an error when trying to index outside the supported
|
||||
* constraints. */
|
||||
if (longitude > GEO_LONG_MAX || longitude < GEO_LONG_MIN ||
|
||||
latitude > GEO_LAT_MAX || latitude < GEO_LAT_MIN) return 0;
|
||||
|
||||
hash->bits = 0;
|
||||
hash->step = step;
|
||||
|
||||
if (latitude < lat_range->min || latitude > lat_range->max ||
|
||||
longitude < long_range->min || longitude > long_range->max) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
double lat_offset =
|
||||
(latitude - lat_range->min) / (lat_range->max - lat_range->min);
|
||||
double long_offset =
|
||||
(longitude - long_range->min) / (long_range->max - long_range->min);
|
||||
|
||||
/* convert to fixed point based on the step size */
|
||||
lat_offset *= (1ULL << step);
|
||||
long_offset *= (1ULL << step);
|
||||
hash->bits = interleave64(lat_offset, long_offset);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int geohashEncodeType(double longitude, double latitude, uint8_t step, GeoHashBits *hash) {
|
||||
GeoHashRange r[2] = {{0}};
|
||||
geohashGetCoordRange(&r[0], &r[1]);
|
||||
return geohashEncode(&r[0], &r[1], longitude, latitude, step, hash);
|
||||
}
|
||||
|
||||
int geohashEncodeWGS84(double longitude, double latitude, uint8_t step,
|
||||
GeoHashBits *hash) {
|
||||
return geohashEncodeType(longitude, latitude, step, hash);
|
||||
}
|
||||
|
||||
int geohashDecode(const GeoHashRange long_range, const GeoHashRange lat_range,
|
||||
const GeoHashBits hash, GeoHashArea *area) {
|
||||
if (HASHISZERO(hash) || NULL == area || RANGEISZERO(lat_range) ||
|
||||
RANGEISZERO(long_range)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
area->hash = hash;
|
||||
uint8_t step = hash.step;
|
||||
uint64_t hash_sep = deinterleave64(hash.bits); /* hash = [LAT][LONG] */
|
||||
|
||||
double lat_scale = lat_range.max - lat_range.min;
|
||||
double long_scale = long_range.max - long_range.min;
|
||||
|
||||
uint32_t ilato = hash_sep; /* get lat part of deinterleaved hash */
|
||||
uint32_t ilono = hash_sep >> 32; /* shift over to get long part of hash */
|
||||
|
||||
/* divide by 2**step.
|
||||
* Then, for 0-1 coordinate, multiply times scale and add
|
||||
to the min to get the absolute coordinate. */
|
||||
area->latitude.min =
|
||||
lat_range.min + (ilato * 1.0 / (1ull << step)) * lat_scale;
|
||||
area->latitude.max =
|
||||
lat_range.min + ((ilato + 1) * 1.0 / (1ull << step)) * lat_scale;
|
||||
area->longitude.min =
|
||||
long_range.min + (ilono * 1.0 / (1ull << step)) * long_scale;
|
||||
area->longitude.max =
|
||||
long_range.min + ((ilono + 1) * 1.0 / (1ull << step)) * long_scale;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int geohashDecodeType(const GeoHashBits hash, GeoHashArea *area) {
|
||||
GeoHashRange r[2] = {{0}};
|
||||
geohashGetCoordRange(&r[0], &r[1]);
|
||||
return geohashDecode(r[0], r[1], hash, area);
|
||||
}
|
||||
|
||||
int geohashDecodeWGS84(const GeoHashBits hash, GeoHashArea *area) {
|
||||
return geohashDecodeType(hash, area);
|
||||
}
|
||||
|
||||
int geohashDecodeAreaToLongLat(const GeoHashArea *area, double *xy) {
|
||||
if (!xy) return 0;
|
||||
xy[0] = (area->longitude.min + area->longitude.max) / 2;
|
||||
if (xy[0] > GEO_LONG_MAX) xy[0] = GEO_LONG_MAX;
|
||||
if (xy[0] < GEO_LONG_MIN) xy[0] = GEO_LONG_MIN;
|
||||
xy[1] = (area->latitude.min + area->latitude.max) / 2;
|
||||
if (xy[1] > GEO_LAT_MAX) xy[1] = GEO_LAT_MAX;
|
||||
if (xy[1] < GEO_LAT_MIN) xy[1] = GEO_LAT_MIN;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int geohashDecodeToLongLatType(const GeoHashBits hash, double *xy) {
|
||||
GeoHashArea area = {{0}};
|
||||
if (!xy || !geohashDecodeType(hash, &area))
|
||||
return 0;
|
||||
return geohashDecodeAreaToLongLat(&area, xy);
|
||||
}
|
||||
|
||||
int geohashDecodeToLongLatWGS84(const GeoHashBits hash, double *xy) {
|
||||
return geohashDecodeToLongLatType(hash, xy);
|
||||
}
|
||||
|
||||
static void geohash_move_x(GeoHashBits *hash, int8_t d) {
|
||||
if (d == 0)
|
||||
return;
|
||||
|
||||
uint64_t x = hash->bits & 0xaaaaaaaaaaaaaaaaULL;
|
||||
uint64_t y = hash->bits & 0x5555555555555555ULL;
|
||||
|
||||
uint64_t zz = 0x5555555555555555ULL >> (64 - hash->step * 2);
|
||||
|
||||
if (d > 0) {
|
||||
x = x + (zz + 1);
|
||||
} else {
|
||||
x = x | zz;
|
||||
x = x - (zz + 1);
|
||||
}
|
||||
|
||||
x &= (0xaaaaaaaaaaaaaaaaULL >> (64 - hash->step * 2));
|
||||
hash->bits = (x | y);
|
||||
}
|
||||
|
||||
static void geohash_move_y(GeoHashBits *hash, int8_t d) {
|
||||
if (d == 0)
|
||||
return;
|
||||
|
||||
uint64_t x = hash->bits & 0xaaaaaaaaaaaaaaaaULL;
|
||||
uint64_t y = hash->bits & 0x5555555555555555ULL;
|
||||
|
||||
uint64_t zz = 0xaaaaaaaaaaaaaaaaULL >> (64 - hash->step * 2);
|
||||
if (d > 0) {
|
||||
y = y + (zz + 1);
|
||||
} else {
|
||||
y = y | zz;
|
||||
y = y - (zz + 1);
|
||||
}
|
||||
y &= (0x5555555555555555ULL >> (64 - hash->step * 2));
|
||||
hash->bits = (x | y);
|
||||
}
|
||||
|
||||
void geohashNeighbors(const GeoHashBits *hash, GeoHashNeighbors *neighbors) {
|
||||
neighbors->east = *hash;
|
||||
neighbors->west = *hash;
|
||||
neighbors->north = *hash;
|
||||
neighbors->south = *hash;
|
||||
neighbors->south_east = *hash;
|
||||
neighbors->south_west = *hash;
|
||||
neighbors->north_east = *hash;
|
||||
neighbors->north_west = *hash;
|
||||
|
||||
geohash_move_x(&neighbors->east, 1);
|
||||
geohash_move_y(&neighbors->east, 0);
|
||||
|
||||
geohash_move_x(&neighbors->west, -1);
|
||||
geohash_move_y(&neighbors->west, 0);
|
||||
|
||||
geohash_move_x(&neighbors->south, 0);
|
||||
geohash_move_y(&neighbors->south, -1);
|
||||
|
||||
geohash_move_x(&neighbors->north, 0);
|
||||
geohash_move_y(&neighbors->north, 1);
|
||||
|
||||
geohash_move_x(&neighbors->north_west, -1);
|
||||
geohash_move_y(&neighbors->north_west, 1);
|
||||
|
||||
geohash_move_x(&neighbors->north_east, 1);
|
||||
geohash_move_y(&neighbors->north_east, 1);
|
||||
|
||||
geohash_move_x(&neighbors->south_east, 1);
|
||||
geohash_move_y(&neighbors->south_east, -1);
|
||||
|
||||
geohash_move_x(&neighbors->south_west, -1);
|
||||
geohash_move_y(&neighbors->south_west, -1);
|
||||
}
|
135
src/redis/geohash.h
Normal file
135
src/redis/geohash.h
Normal file
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* Copyright (c) 2013-2014, yinqiwen <yinqiwen@gmail.com>
|
||||
* Copyright (c) 2014, Matt Stancliff <matt@genges.com>.
|
||||
* Copyright (c) 2015, Salvatore Sanfilippo <antirez@gmail.com>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef GEOHASH_H_
|
||||
#define GEOHASH_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define HASHISZERO(r) (!(r).bits && !(r).step)
|
||||
#define RANGEISZERO(r) (!(r).max && !(r).min)
|
||||
#define RANGEPISZERO(r) (r == NULL || RANGEISZERO(*r))
|
||||
|
||||
#define GEO_STEP_MAX 26 /* 26*2 = 52 bits. */
|
||||
|
||||
/* Limits from EPSG:900913 / EPSG:3785 / OSGEO:41001 */
|
||||
#define GEO_LAT_MIN -85.05112878
|
||||
#define GEO_LAT_MAX 85.05112878
|
||||
#define GEO_LONG_MIN -180
|
||||
#define GEO_LONG_MAX 180
|
||||
|
||||
typedef enum {
|
||||
GEOHASH_NORTH = 0,
|
||||
GEOHASH_EAST,
|
||||
GEOHASH_WEST,
|
||||
GEOHASH_SOUTH,
|
||||
GEOHASH_SOUTH_WEST,
|
||||
GEOHASH_SOUTH_EAST,
|
||||
GEOHASH_NORT_WEST,
|
||||
GEOHASH_NORT_EAST
|
||||
} GeoDirection;
|
||||
|
||||
typedef struct {
|
||||
uint64_t bits;
|
||||
uint8_t step;
|
||||
} GeoHashBits;
|
||||
|
||||
typedef struct {
|
||||
double min;
|
||||
double max;
|
||||
} GeoHashRange;
|
||||
|
||||
typedef struct {
|
||||
GeoHashBits hash;
|
||||
GeoHashRange longitude;
|
||||
GeoHashRange latitude;
|
||||
} GeoHashArea;
|
||||
|
||||
typedef struct {
|
||||
GeoHashBits north;
|
||||
GeoHashBits east;
|
||||
GeoHashBits west;
|
||||
GeoHashBits south;
|
||||
GeoHashBits north_east;
|
||||
GeoHashBits south_east;
|
||||
GeoHashBits north_west;
|
||||
GeoHashBits south_west;
|
||||
} GeoHashNeighbors;
|
||||
|
||||
#define CIRCULAR_TYPE 1
|
||||
#define RECTANGLE_TYPE 2
|
||||
typedef struct {
|
||||
int type; /* search type */
|
||||
double xy[2]; /* search center point, xy[0]: lon, xy[1]: lat */
|
||||
double conversion; /* km: 1000 */
|
||||
double bounds[4]; /* bounds[0]: min_lon, bounds[1]: min_lat
|
||||
* bounds[2]: max_lon, bounds[3]: max_lat */
|
||||
union {
|
||||
/* CIRCULAR_TYPE */
|
||||
double radius;
|
||||
/* RECTANGLE_TYPE */
|
||||
struct {
|
||||
double height;
|
||||
double width;
|
||||
} r;
|
||||
} t;
|
||||
} GeoShape;
|
||||
|
||||
/*
|
||||
* 0:success
|
||||
* -1:failed
|
||||
*/
|
||||
void geohashGetCoordRange(GeoHashRange *long_range, GeoHashRange *lat_range);
|
||||
int geohashEncode(const GeoHashRange *long_range, const GeoHashRange *lat_range,
|
||||
double longitude, double latitude, uint8_t step,
|
||||
GeoHashBits *hash);
|
||||
int geohashEncodeType(double longitude, double latitude,
|
||||
uint8_t step, GeoHashBits *hash);
|
||||
int geohashEncodeWGS84(double longitude, double latitude, uint8_t step,
|
||||
GeoHashBits *hash);
|
||||
int geohashDecode(const GeoHashRange long_range, const GeoHashRange lat_range,
|
||||
const GeoHashBits hash, GeoHashArea *area);
|
||||
int geohashDecodeType(const GeoHashBits hash, GeoHashArea *area);
|
||||
int geohashDecodeWGS84(const GeoHashBits hash, GeoHashArea *area);
|
||||
int geohashDecodeAreaToLongLat(const GeoHashArea *area, double *xy);
|
||||
int geohashDecodeToLongLatType(const GeoHashBits hash, double *xy);
|
||||
int geohashDecodeToLongLatWGS84(const GeoHashBits hash, double *xy);
|
||||
void geohashNeighbors(const GeoHashBits *hash, GeoHashNeighbors *neighbors);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
#endif /* GEOHASH_H_ */
|
280
src/redis/geohash_helper.c
Normal file
280
src/redis/geohash_helper.c
Normal file
|
@ -0,0 +1,280 @@
|
|||
/*
|
||||
* Copyright (c) 2013-2014, yinqiwen <yinqiwen@gmail.com>
|
||||
* Copyright (c) 2014, Matt Stancliff <matt@genges.com>.
|
||||
* Copyright (c) 2015-2016, Salvatore Sanfilippo <antirez@gmail.com>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* This is a C++ to C conversion from the ardb project.
|
||||
* This file started out as:
|
||||
* https://github.com/yinqiwen/ardb/blob/d42503/src/geo/geohash_helper.cpp
|
||||
*/
|
||||
|
||||
#define __USE_XOPEN
|
||||
|
||||
#include "geohash_helper.h"
|
||||
#include <math.h>
|
||||
|
||||
#define D_R (M_PI / 180.0)
|
||||
#define R_MAJOR 6378137.0
|
||||
#define R_MINOR 6356752.3142
|
||||
#define RATIO (R_MINOR / R_MAJOR)
|
||||
#define ECCENT (sqrt(1.0 - (RATIO *RATIO)))
|
||||
#define COM (0.5 * ECCENT)
|
||||
|
||||
/// @brief The usual PI/180 constant
|
||||
const double DEG_TO_RAD = 0.017453292519943295769236907684886;
|
||||
/// @brief Earth's quatratic mean radius for WGS-84
|
||||
const double EARTH_RADIUS_IN_METERS = 6372797.560856;
|
||||
|
||||
const double MERCATOR_MAX = 20037726.37;
|
||||
const double MERCATOR_MIN = -20037726.37;
|
||||
|
||||
static inline double deg_rad(double ang) { return ang * D_R; }
|
||||
static inline double rad_deg(double ang) { return ang / D_R; }
|
||||
|
||||
/* This function is used in order to estimate the step (bits precision)
|
||||
* of the 9 search area boxes during radius queries. */
|
||||
uint8_t geohashEstimateStepsByRadius(double range_meters, double lat) {
|
||||
if (range_meters == 0) return 26;
|
||||
int step = 1;
|
||||
while (range_meters < MERCATOR_MAX) {
|
||||
range_meters *= 2;
|
||||
step++;
|
||||
}
|
||||
step -= 2; /* Make sure range is included in most of the base cases. */
|
||||
|
||||
/* Wider range towards the poles... Note: it is possible to do better
|
||||
* than this approximation by computing the distance between meridians
|
||||
* at this latitude, but this does the trick for now. */
|
||||
if (lat > 66 || lat < -66) {
|
||||
step--;
|
||||
if (lat > 80 || lat < -80) step--;
|
||||
}
|
||||
|
||||
/* Frame to valid range. */
|
||||
if (step < 1) step = 1;
|
||||
if (step > 26) step = 26;
|
||||
return step;
|
||||
}
|
||||
|
||||
/* Return the bounding box of the search area by shape (see geohash.h GeoShape)
|
||||
* bounds[0] - bounds[2] is the minimum and maximum longitude
|
||||
* while bounds[1] - bounds[3] is the minimum and maximum latitude.
|
||||
* since the higher the latitude, the shorter the arc length, the box shape is as follows
|
||||
* (left and right edges are actually bent), as shown in the following diagram:
|
||||
*
|
||||
* \-----------------/ -------- \-----------------/
|
||||
* \ / / \ \ /
|
||||
* \ (long,lat) / / (long,lat) \ \ (long,lat) /
|
||||
* \ / / \ / \
|
||||
* --------- /----------------\ /---------------\
|
||||
* Northern Hemisphere Southern Hemisphere Around the equator
|
||||
*/
|
||||
int geohashBoundingBox(GeoShape *shape, double *bounds) {
|
||||
if (!bounds) return 0;
|
||||
double longitude = shape->xy[0];
|
||||
double latitude = shape->xy[1];
|
||||
double height = shape->conversion * (shape->type == CIRCULAR_TYPE ? shape->t.radius : shape->t.r.height/2);
|
||||
double width = shape->conversion * (shape->type == CIRCULAR_TYPE ? shape->t.radius : shape->t.r.width/2);
|
||||
|
||||
const double lat_delta = rad_deg(height/EARTH_RADIUS_IN_METERS);
|
||||
const double long_delta_top = rad_deg(width/EARTH_RADIUS_IN_METERS/cos(deg_rad(latitude+lat_delta)));
|
||||
const double long_delta_bottom = rad_deg(width/EARTH_RADIUS_IN_METERS/cos(deg_rad(latitude-lat_delta)));
|
||||
/* The directions of the northern and southern hemispheres
|
||||
* are opposite, so we choice different points as min/max long/lat */
|
||||
int southern_hemisphere = latitude < 0 ? 1 : 0;
|
||||
bounds[0] = southern_hemisphere ? longitude-long_delta_bottom : longitude-long_delta_top;
|
||||
bounds[2] = southern_hemisphere ? longitude+long_delta_bottom : longitude+long_delta_top;
|
||||
bounds[1] = latitude - lat_delta;
|
||||
bounds[3] = latitude + lat_delta;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Calculate a set of areas (center + 8) that are able to cover a range query
|
||||
* for the specified position and shape (see geohash.h GeoShape).
|
||||
* the bounding box saved in shaple.bounds */
|
||||
GeoHashRadius geohashCalculateAreasByShapeWGS84(GeoShape *shape) {
|
||||
GeoHashRange long_range, lat_range;
|
||||
GeoHashRadius radius;
|
||||
GeoHashBits hash;
|
||||
GeoHashNeighbors neighbors;
|
||||
GeoHashArea area;
|
||||
double min_lon, max_lon, min_lat, max_lat;
|
||||
int steps;
|
||||
|
||||
geohashBoundingBox(shape, shape->bounds);
|
||||
min_lon = shape->bounds[0];
|
||||
min_lat = shape->bounds[1];
|
||||
max_lon = shape->bounds[2];
|
||||
max_lat = shape->bounds[3];
|
||||
|
||||
double longitude = shape->xy[0];
|
||||
double latitude = shape->xy[1];
|
||||
/* radius_meters is calculated differently in different search types:
|
||||
* 1) CIRCULAR_TYPE, just use radius.
|
||||
* 2) RECTANGLE_TYPE, we use sqrt((width/2)^2 + (height/2)^2) to
|
||||
* calculate the distance from the center point to the corner */
|
||||
double radius_meters = shape->type == CIRCULAR_TYPE ? shape->t.radius :
|
||||
sqrt((shape->t.r.width/2)*(shape->t.r.width/2) + (shape->t.r.height/2)*(shape->t.r.height/2));
|
||||
radius_meters *= shape->conversion;
|
||||
|
||||
steps = geohashEstimateStepsByRadius(radius_meters,latitude);
|
||||
|
||||
geohashGetCoordRange(&long_range,&lat_range);
|
||||
geohashEncode(&long_range,&lat_range,longitude,latitude,steps,&hash);
|
||||
geohashNeighbors(&hash,&neighbors);
|
||||
geohashDecode(long_range,lat_range,hash,&area);
|
||||
|
||||
/* Check if the step is enough at the limits of the covered area.
|
||||
* Sometimes when the search area is near an edge of the
|
||||
* area, the estimated step is not small enough, since one of the
|
||||
* north / south / west / east square is too near to the search area
|
||||
* to cover everything. */
|
||||
int decrease_step = 0;
|
||||
{
|
||||
GeoHashArea north, south, east, west;
|
||||
|
||||
geohashDecode(long_range, lat_range, neighbors.north, &north);
|
||||
geohashDecode(long_range, lat_range, neighbors.south, &south);
|
||||
geohashDecode(long_range, lat_range, neighbors.east, &east);
|
||||
geohashDecode(long_range, lat_range, neighbors.west, &west);
|
||||
|
||||
if (north.latitude.max < max_lat)
|
||||
decrease_step = 1;
|
||||
if (south.latitude.min > min_lat)
|
||||
decrease_step = 1;
|
||||
if (east.longitude.max < max_lon)
|
||||
decrease_step = 1;
|
||||
if (west.longitude.min > min_lon)
|
||||
decrease_step = 1;
|
||||
}
|
||||
|
||||
if (steps > 1 && decrease_step) {
|
||||
steps--;
|
||||
geohashEncode(&long_range,&lat_range,longitude,latitude,steps,&hash);
|
||||
geohashNeighbors(&hash,&neighbors);
|
||||
geohashDecode(long_range,lat_range,hash,&area);
|
||||
}
|
||||
|
||||
/* Exclude the search areas that are useless. */
|
||||
if (steps >= 2) {
|
||||
if (area.latitude.min < min_lat) {
|
||||
GZERO(neighbors.south);
|
||||
GZERO(neighbors.south_west);
|
||||
GZERO(neighbors.south_east);
|
||||
}
|
||||
if (area.latitude.max > max_lat) {
|
||||
GZERO(neighbors.north);
|
||||
GZERO(neighbors.north_east);
|
||||
GZERO(neighbors.north_west);
|
||||
}
|
||||
if (area.longitude.min < min_lon) {
|
||||
GZERO(neighbors.west);
|
||||
GZERO(neighbors.south_west);
|
||||
GZERO(neighbors.north_west);
|
||||
}
|
||||
if (area.longitude.max > max_lon) {
|
||||
GZERO(neighbors.east);
|
||||
GZERO(neighbors.south_east);
|
||||
GZERO(neighbors.north_east);
|
||||
}
|
||||
}
|
||||
radius.hash = hash;
|
||||
radius.neighbors = neighbors;
|
||||
radius.area = area;
|
||||
return radius;
|
||||
}
|
||||
|
||||
GeoHashFix52Bits geohashAlign52Bits(const GeoHashBits hash) {
|
||||
uint64_t bits = hash.bits;
|
||||
bits <<= (52 - hash.step * 2);
|
||||
return bits;
|
||||
}
|
||||
|
||||
/* Calculate distance using simplified haversine great circle distance formula.
|
||||
* Given longitude diff is 0 the asin(sqrt(a)) on the haversine is asin(sin(abs(u))).
|
||||
* arcsin(sin(x)) equal to x when x ∈[−𝜋/2,𝜋/2]. Given latitude is between [−𝜋/2,𝜋/2]
|
||||
* we can simplify arcsin(sin(x)) to x.
|
||||
*/
|
||||
double geohashGetLatDistance(double lat1d, double lat2d) {
|
||||
return EARTH_RADIUS_IN_METERS * fabs(deg_rad(lat2d) - deg_rad(lat1d));
|
||||
}
|
||||
|
||||
/* Calculate distance using haversine great circle distance formula. */
|
||||
double geohashGetDistance(double lon1d, double lat1d, double lon2d, double lat2d) {
|
||||
double lat1r, lon1r, lat2r, lon2r, u, v, a;
|
||||
lon1r = deg_rad(lon1d);
|
||||
lon2r = deg_rad(lon2d);
|
||||
v = sin((lon2r - lon1r) / 2);
|
||||
/* if v == 0 we can avoid doing expensive math when lons are practically the same */
|
||||
if (v == 0.0)
|
||||
return geohashGetLatDistance(lat1d, lat2d);
|
||||
lat1r = deg_rad(lat1d);
|
||||
lat2r = deg_rad(lat2d);
|
||||
u = sin((lat2r - lat1r) / 2);
|
||||
a = u * u + cos(lat1r) * cos(lat2r) * v * v;
|
||||
return 2.0 * EARTH_RADIUS_IN_METERS * asin(sqrt(a));
|
||||
}
|
||||
|
||||
int geohashGetDistanceIfInRadius(double x1, double y1,
|
||||
double x2, double y2, double radius,
|
||||
double *distance) {
|
||||
*distance = geohashGetDistance(x1, y1, x2, y2);
|
||||
if (*distance > radius) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int geohashGetDistanceIfInRadiusWGS84(double x1, double y1, double x2,
|
||||
double y2, double radius,
|
||||
double *distance) {
|
||||
return geohashGetDistanceIfInRadius(x1, y1, x2, y2, radius, distance);
|
||||
}
|
||||
|
||||
/* Judge whether a point is in the axis-aligned rectangle, when the distance
|
||||
* between a searched point and the center point is less than or equal to
|
||||
* height/2 or width/2 in height and width, the point is in the rectangle.
|
||||
*
|
||||
* width_m, height_m: the rectangle
|
||||
* x1, y1 : the center of the box
|
||||
* x2, y2 : the point to be searched
|
||||
*/
|
||||
int geohashGetDistanceIfInRectangle(double width_m, double height_m, double x1, double y1,
|
||||
double x2, double y2, double *distance) {
|
||||
/* latitude distance is less expensive to compute than longitude distance
|
||||
* so we check first for the latitude condition */
|
||||
double lat_distance = geohashGetLatDistance(y2, y1);
|
||||
if (lat_distance > height_m/2) {
|
||||
return 0;
|
||||
}
|
||||
double lon_distance = geohashGetDistance(x2, y2, x1, y2);
|
||||
if (lon_distance > width_m/2) {
|
||||
return 0;
|
||||
}
|
||||
*distance = geohashGetDistance(x1, y1, x2, y2);
|
||||
return 1;
|
||||
}
|
65
src/redis/geohash_helper.h
Normal file
65
src/redis/geohash_helper.h
Normal file
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright (c) 2013-2014, yinqiwen <yinqiwen@gmail.com>
|
||||
* Copyright (c) 2014, Matt Stancliff <matt@genges.com>.
|
||||
* Copyright (c) 2015, Salvatore Sanfilippo <antirez@gmail.com>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef GEOHASH_HELPER_HPP_
|
||||
#define GEOHASH_HELPER_HPP_
|
||||
|
||||
#include "geohash.h"
|
||||
|
||||
#define GZERO(s) s.bits = s.step = 0;
|
||||
#define GISZERO(s) (!s.bits && !s.step)
|
||||
#define GISNOTZERO(s) (s.bits || s.step)
|
||||
|
||||
typedef uint64_t GeoHashFix52Bits;
|
||||
typedef uint64_t GeoHashVarBits;
|
||||
|
||||
typedef struct {
|
||||
GeoHashBits hash;
|
||||
GeoHashArea area;
|
||||
GeoHashNeighbors neighbors;
|
||||
} GeoHashRadius;
|
||||
|
||||
uint8_t geohashEstimateStepsByRadius(double range_meters, double lat);
|
||||
int geohashBoundingBox(GeoShape *shape, double *bounds);
|
||||
GeoHashRadius geohashCalculateAreasByShapeWGS84(GeoShape *shape);
|
||||
GeoHashFix52Bits geohashAlign52Bits(const GeoHashBits hash);
|
||||
double geohashGetDistance(double lon1d, double lat1d,
|
||||
double lon2d, double lat2d);
|
||||
int geohashGetDistanceIfInRadius(double x1, double y1,
|
||||
double x2, double y2, double radius,
|
||||
double *distance);
|
||||
int geohashGetDistanceIfInRadiusWGS84(double x1, double y1, double x2,
|
||||
double y2, double radius,
|
||||
double *distance);
|
||||
int geohashGetDistanceIfInRectangle(double width_m, double height_m, double x1, double y1,
|
||||
double x2, double y2, double *distance);
|
||||
|
||||
#endif /* GEOHASH_HELPER_HPP_ */
|
185
src/redis/pqsort.c
Normal file
185
src/redis/pqsort.c
Normal file
|
@ -0,0 +1,185 @@
|
|||
/* The following is the NetBSD libc qsort implementation modified in order to
|
||||
* support partial sorting of ranges for Redis.
|
||||
*
|
||||
* Copyright(C) 2009-2012 Salvatore Sanfilippo. All rights reserved.
|
||||
*
|
||||
* The original copyright notice follows. */
|
||||
|
||||
|
||||
/* $NetBSD: qsort.c,v 1.19 2009/01/30 23:38:44 lukem Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 1992, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static inline char *med3 (char *, char *, char *,
|
||||
int (*)(const void *, const void *));
|
||||
static inline void swapfunc (char *, char *, size_t, int);
|
||||
|
||||
#define min(a, b) (a) < (b) ? a : b
|
||||
|
||||
/*
|
||||
* Qsort routine from Bentley & McIlroy's "Engineering a Sort Function".
|
||||
*/
|
||||
#define swapcode(TYPE, parmi, parmj, n) { \
|
||||
size_t i = (n) / sizeof (TYPE); \
|
||||
TYPE *pi = (TYPE *)(void *)(parmi); \
|
||||
TYPE *pj = (TYPE *)(void *)(parmj); \
|
||||
do { \
|
||||
TYPE t = *pi; \
|
||||
*pi++ = *pj; \
|
||||
*pj++ = t; \
|
||||
} while (--i > 0); \
|
||||
}
|
||||
|
||||
#define SWAPINIT(a, es) swaptype = (uintptr_t)a % sizeof(long) || \
|
||||
es % sizeof(long) ? 2 : es == sizeof(long)? 0 : 1;
|
||||
|
||||
static inline void
|
||||
swapfunc(char *a, char *b, size_t n, int swaptype)
|
||||
{
|
||||
|
||||
if (swaptype <= 1)
|
||||
swapcode(long, a, b, n)
|
||||
else
|
||||
swapcode(char, a, b, n)
|
||||
}
|
||||
|
||||
#define swap(a, b) \
|
||||
if (swaptype == 0) { \
|
||||
long t = *(long *)(void *)(a); \
|
||||
*(long *)(void *)(a) = *(long *)(void *)(b); \
|
||||
*(long *)(void *)(b) = t; \
|
||||
} else \
|
||||
swapfunc(a, b, es, swaptype)
|
||||
|
||||
#define vecswap(a, b, n) if ((n) > 0) swapfunc((a), (b), (size_t)(n), swaptype)
|
||||
|
||||
static inline char *
|
||||
med3(char *a, char *b, char *c,
|
||||
int (*cmp) (const void *, const void *))
|
||||
{
|
||||
|
||||
return cmp(a, b) < 0 ?
|
||||
(cmp(b, c) < 0 ? b : (cmp(a, c) < 0 ? c : a ))
|
||||
:(cmp(b, c) > 0 ? b : (cmp(a, c) < 0 ? a : c ));
|
||||
}
|
||||
|
||||
static void
|
||||
_pqsort(void *a, size_t n, size_t es,
|
||||
int (*cmp) (const void *, const void *), void *lrange, void *rrange)
|
||||
{
|
||||
char *pa, *pb, *pc, *pd, *pl, *pm, *pn;
|
||||
size_t d, r;
|
||||
int swaptype, cmp_result;
|
||||
|
||||
loop: SWAPINIT(a, es);
|
||||
if (n < 7) {
|
||||
for (pm = (char *) a + es; pm < (char *) a + n * es; pm += es)
|
||||
for (pl = pm; pl > (char *) a && cmp(pl - es, pl) > 0;
|
||||
pl -= es)
|
||||
swap(pl, pl - es);
|
||||
return;
|
||||
}
|
||||
pm = (char *) a + (n / 2) * es;
|
||||
if (n > 7) {
|
||||
pl = (char *) a;
|
||||
pn = (char *) a + (n - 1) * es;
|
||||
if (n > 40) {
|
||||
d = (n / 8) * es;
|
||||
pl = med3(pl, pl + d, pl + 2 * d, cmp);
|
||||
pm = med3(pm - d, pm, pm + d, cmp);
|
||||
pn = med3(pn - 2 * d, pn - d, pn, cmp);
|
||||
}
|
||||
pm = med3(pl, pm, pn, cmp);
|
||||
}
|
||||
swap(a, pm);
|
||||
pa = pb = (char *) a + es;
|
||||
|
||||
pc = pd = (char *) a + (n - 1) * es;
|
||||
for (;;) {
|
||||
while (pb <= pc && (cmp_result = cmp(pb, a)) <= 0) {
|
||||
if (cmp_result == 0) {
|
||||
swap(pa, pb);
|
||||
pa += es;
|
||||
}
|
||||
pb += es;
|
||||
}
|
||||
while (pb <= pc && (cmp_result = cmp(pc, a)) >= 0) {
|
||||
if (cmp_result == 0) {
|
||||
swap(pc, pd);
|
||||
pd -= es;
|
||||
}
|
||||
pc -= es;
|
||||
}
|
||||
if (pb > pc)
|
||||
break;
|
||||
swap(pb, pc);
|
||||
pb += es;
|
||||
pc -= es;
|
||||
}
|
||||
|
||||
pn = (char *) a + n * es;
|
||||
r = min(pa - (char *) a, pb - pa);
|
||||
vecswap(a, pb - r, r);
|
||||
r = min((size_t)(pd - pc), pn - pd - es);
|
||||
vecswap(pb, pn - r, r);
|
||||
if ((r = pb - pa) > es) {
|
||||
void *_l = a, *_r = ((unsigned char*)a)+r-1;
|
||||
if (!((lrange < _l && rrange < _l) ||
|
||||
(lrange > _r && rrange > _r)))
|
||||
_pqsort(a, r / es, es, cmp, lrange, rrange);
|
||||
}
|
||||
if ((r = pd - pc) > es) {
|
||||
void *_l, *_r;
|
||||
|
||||
/* Iterate rather than recurse to save stack space */
|
||||
a = pn - r;
|
||||
n = r / es;
|
||||
|
||||
_l = a;
|
||||
_r = ((unsigned char*)a)+r-1;
|
||||
if (!((lrange < _l && rrange < _l) ||
|
||||
(lrange > _r && rrange > _r)))
|
||||
goto loop;
|
||||
}
|
||||
/* qsort(pn - r, r / es, es, cmp);*/
|
||||
}
|
||||
|
||||
void
|
||||
pqsort(void *a, size_t n, size_t es,
|
||||
int (*cmp) (const void *, const void *), size_t lrange, size_t rrange)
|
||||
{
|
||||
_pqsort(a,n,es,cmp,((unsigned char*)a)+(lrange*es),
|
||||
((unsigned char*)a)+((rrange+1)*es)-1);
|
||||
}
|
40
src/redis/pqsort.h
Normal file
40
src/redis/pqsort.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
/* The following is the NetBSD libc qsort implementation modified in order to
|
||||
* support partial sorting of ranges for Redis.
|
||||
*
|
||||
* Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* See the pqsort.c file for the original copyright notice. */
|
||||
|
||||
#ifndef __PQSORT_H
|
||||
#define __PQSORT_H
|
||||
|
||||
void
|
||||
pqsort(void *a, size_t n, size_t es,
|
||||
int (*cmp) (const void *, const void *), size_t lrange, size_t rrange);
|
||||
|
||||
#endif
|
Loading…
Add table
Add a link
Reference in a new issue