You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
spOSMroute/mapsys/map_searchhash.c

607 lines
17 KiB

/***************************************************************************
* map_searchhash.c
*
* Copyright 2010 - Steffen Pohle
* steffen@gulpe.de
****************************************************************************/
/*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_LOCALE_H
#include <locale.h>
#endif
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "osmroute.h"
#include "map.h"
#include "memoryleak.h"
#include "system.h"
int map_search_fd = 0;
int map_search_disabled = 0;
/****************************************************************************/
struct map_search_data map_search_line2data (char *line);
/*****************************************************************************
*
* searching and filemanagment for the search files, this also includes
* saving and reading files. All saved data will be in text format.
* Country and Name will always be converted to lowercase text
*/
void map_search_get_filename (char *fn, int len) {
#ifdef HAVE_LOCALE_H
setlocale(LC_ALL, "C");
snprintf (fn, len, "%s/search.mapidx", cfg.mappath);
setlocale(LC_ALL, NULL);
#else
snprintf (fn, len, "%s/search.mapidx", cfg.mappath);
#endif
};
void map_search_open () {
char fn[LEN_FILENAME];
if (map_search_disabled) return;
if (map_search_fd <= 0) {
map_search_get_filename (fn, LEN_FILENAME);
map_search_fd = open (fn, O_RDWR | O_CREAT, 0644);
}
};
/****************************************************************************
* convert between line and structure
*/
struct map_search_data map_search_line2data (char *line) {
struct map_search_data msd;
char *p = line;
strncpy (msd.name, p, MAP_SEARCHLINE_LEN);
p = p + strlen(p) + 1;
strncpy (msd.country, p, MAP_SEARCHLINE_LEN);
p = p + strlen(p) + 1;
sscanf (p, "%lld %f %f", &msd.id, &msd.lon, &msd.lat);
return msd;
};
/**************************************************************************
* add a new entry to the search list.
* be carefull searching works only fine on sorted lists. So after adding
* lines to the structure it has to be sorted again.
*/
void map_search_add (char *country, char *name, unsigned long long int id, float lon, float lat) {
char dataline[MAP_SEARCHLINE_LEN];
char *n, *c, *cp;
char nullstring[2] = "\0\0";
if (map_search_disabled) return;
// d_printf ("map_search_add id:%lld name:'%s'", id, name);
if (name == NULL) n = nullstring;
else n = name;
if (country == NULL) c = nullstring;
else c = country;
if (map_search_fd <= 0) map_search_open ();
if (map_search_fd) {
lseek (map_search_fd, 0, SEEK_END);
/* copy country */
cp = dataline;
strncpy (cp, n, MAP_SEARCHLINE_LEN-(cp-dataline));
cp = cp + strlen (cp); cp[0] = 0; cp++;
strncpy (cp, c, MAP_SEARCHLINE_LEN-(cp-dataline));
cp = cp + strlen (cp); cp[0] = 0; cp++;
snprintf (cp, MAP_SEARCHLINE_LEN-(cp-dataline), "%lld %f %f\n", id, lon, lat);
cp = cp + strlen (cp);
write (map_search_fd, dataline, cp-dataline);
}
};
/*
* just reset all data
*/
void map_search_init () {
if (map_search_disabled) return;
if (map_search_fd) close (map_search_fd);
map_search_fd = 0;
};
/**************************************************************************
* sort the search.mapidx file in alphabetic order
* - read full file into memory
* - create pointer array to all elements
* - sort the pointers in alphabetic order
* - delete old file, save new file ignore double id entrys
*/
#define MAP_SEARCH_SORT_BLOCK 4096
struct maps_sort {
char **larray;
int max;
int cnt;
};
void swap (char **data1, char **data2) {
char *tmp = *data1;
*data1 = *data2;
*data2 = tmp;
};
struct maps_sort map_search_sort_checkmem (struct maps_sort sdat, int size) {
struct maps_sort ns = { NULL, 0, 0};
int i;
if (size == 0) {
if (sdat.larray) ml_free (sdat.larray);
return ns;
}
else if (size+1 >= sdat.max) {
ns.max = size + MAP_SEARCH_SORT_BLOCK;
ns.larray = (char **) ml_malloc (sizeof (char *) * ns.max);
for (i = 0; i < sdat.cnt; i++)
ns.larray[i] = sdat.larray[i];
ns.cnt = sdat.cnt;
ml_free (sdat.larray);
return ns;
}
else return sdat;
};
/*****************************************************************************
* bubblesort
*/
void map_search_sort_alg1 (struct maps_sort *data) {
int i, j;
char text[256];
for (j = 0; j < data->cnt; j++) {
if (j > 0 && data->larray[j]) {
i = j-1;
while (i >= 0 && (data->larray[i] == NULL || strcasecmp (data->larray[i+1], data->larray[i]) > 0)) {
swap (&data->larray[i+1], &data->larray[i]);
i--;
}
}
if (j % 100 == 0) {
sprintf (text, _("Sorting search.mapidx %d/%d"), j, data->cnt);
app_status (-1, text, 0.0);
main_wnd_update ();
}
}
};
/*****************************************************************************
* quicksort
*/
/*
* quicksortstack - needed for storing unsorted fields.
*/
struct maps_qsort {
int elements;
int start;
};
struct maps_qsort *maps_qs_data = NULL; /* pointer to stack array */
int maps_qs_size = 0; /* allocated elements on stack */
int maps_qs_pos = 0; /* current position on the stack */
/* push data to the quicksort stack */
void maps_qsort_push (struct maps_qsort *data) {
int i;
struct maps_qsort *tmp;
if (maps_qs_size <= maps_qs_pos+1) {
maps_qs_size += 1024;
tmp = ml_malloc (sizeof (struct maps_qsort)*maps_qs_size);
for (i = 0; i < maps_qs_pos; i++) tmp[i] = maps_qs_data[i];
ml_free (maps_qs_data);
maps_qs_data = tmp;
}
maps_qs_data[maps_qs_pos] = *data;
maps_qs_pos++;
};
/* pop data form the quicksort stack */
void maps_qsort_pop (struct maps_qsort *data) {
if (maps_qs_pos > 0) {
maps_qs_pos--;
*data = maps_qs_data[maps_qs_pos];
}
if (maps_qs_pos == 0 && maps_qs_size > 0) {
ml_free (maps_qs_data);
maps_qs_data = NULL;
maps_qs_size = 0;
}
};
/*
* quicksort routine:
* to prevent too many compares in case we have already sorted data we
* check if the data is already sorted.
*/
void map_search_sort_alg2 (struct maps_sort *data) {
int i, j, to = 0;
char text[256];
struct maps_qsort qsort, left, right;
int pivot;
if (data->cnt < 2) return; /* only one element --> return */
/* select full data block */
qsort.elements = data->cnt;
qsort.start = 0;
/*
* loop until we have no data on the quicksort stack
*/
do {
/*
* select first element as pivot to compare against
* pivot is the selected element inside the block
* i - element from left (smaller as pivot)
* j - element from right (larger as pivot)
*/
pivot = 0;
i = 1;
j = qsort.elements-1;
/* make sure we have always mixed up lists.. */
if (qsort.elements > 50) swap (&data->larray[qsort.start], &data->larray[qsort.start+qsort.elements/2]);
do {
/*
* go to right until we found something larger as pivot
*/
while (i < j && (strcasecmp (data->larray[qsort.start+pivot], data->larray[qsort.start+i]) >= 0 || pivot == i)) {
i++;
}
/*
* go to the left until we found something smaller as pivot
*/
while (i < j && (strcasecmp (data->larray[qsort.start+pivot], data->larray[qsort.start+j]) < 0 || pivot == j)) {
j--;
}
/*
* still i < j: swap i<-->j , swap i<-->pivot
*/
if (i < j) {
swap (&data->larray[qsort.start+j], &data->larray[qsort.start+i]);
swap (&data->larray[qsort.start+i], &data->larray[qsort.start+pivot]);
pivot = i;
i++;
j--;
}
/*
* finished? but check in case the last elements are not in order
*/
if (i == j) {
/* i < pivot: swap pivot<-->i */
if (strcasecmp (data->larray[qsort.start+pivot], data->larray[qsort.start+i]) >= 0) {
swap (&data->larray[qsort.start+i], &data->larray[qsort.start+pivot]);
pivot = i;
}
/* i-1 < pivot: swap pivot<-->i-1 */
else if (strcasecmp (data->larray[qsort.start+pivot], data->larray[qsort.start+i]-1) >= 0) {
swap (&data->larray[qsort.start+i-1], &data->larray[qsort.start+pivot]);
pivot = i-1;
}
}
if (i == pivot) i++;
if (j == pivot) j--;
} while (i < j);
/*
* split block in two and put them on the quicksort stack
*/
left.start = qsort.start;
left.elements = pivot;
right.start = pivot+qsort.start+1;
right.elements = qsort.elements-pivot-1;
/* only push if it is nessesary (not clean, or more then one element) */
if (left.elements > 1) maps_qsort_push (&left);
if (right.elements > 1) maps_qsort_push (&right);
if (maps_qs_pos > 0) maps_qsort_pop (&qsort);
else qsort.elements = 0;
/* display update on the screen */
if (to++ < 0 || to > 250) {
sprintf (text, _("sorting search.mapidx %d"), maps_qs_pos);
app_status (-1, text, 0.0);
main_wnd_update ();
to = 0;
}
} while (qsort.elements > 1);
};
void map_search_sort () {
char fn_src[LEN_FILENAME];
char text[256];
int fd_src;
char *fdata = NULL;
char *c, *ci;
struct maps_sort data = {NULL, 0, 0};
struct map_search_data msd;
struct stat fs;
int i;
size_t rs;
unsigned long long int lid;
/* close open files - reset and disable searching */
d_printf ("map_search_sort entering");
#ifdef HAVE_LOCALE_H
setlocale(LC_ALL, "C");
#endif
map_search_init ();
map_search_disabled = 1;
data.max = data.cnt = 0;
/* get filenames */
map_search_get_filename (fn_src, LEN_FILENAME);
d_printf ("map_search_sort: src:%s", fn_src);
/* size of file and read file into memory */
if (stat(fn_src, &fs) != 0) {
d_printf ("map_search_sort: error on stat %s", fn_src);
map_search_disabled = 0;
return;
}
d_printf ("map_search_sort: read %ld of data", fs.st_size);
fdata = ml_malloc (fs.st_size);
#if defined(__MINGW32CE__) || defined(_WIN32_WCE) || defined(__MINGW32__)
fd_src = open (fn_src, O_RDWR | _O_BINARY);
#else
fd_src = open (fn_src, O_RDWR);
#endif
rs = read (fd_src, fdata, fs.st_size);
d_printf (" got %ld", fs.st_size, rs);
close (fd_src);
/*
* create pointers of all new lines
*/
for (i = 0, c = fdata, ci = fdata; ci < fdata + rs; ci++) {
if (i > 1 && (*ci == '\n' || *ci == '\r')) {
if (*c != '\n' && *c != '\r') { /* save line */
if (data.cnt+1 >= data.max) data = map_search_sort_checkmem (data, data.cnt+1024);
data.larray[data.cnt] = c;
data.cnt++;
c = ci;
i = 0;
}
}
else { /* reset line */
if (*ci == '\0') i++;
if (*c == '\n' || *c == '\r')
c = ci;
}
}
d_printf ("map_search_sort: found %d entrys", data.cnt);
/* sorting */
map_search_sort_alg2 (&data);
map_search_disabled = 0;
d_printf ("map_search_sort: finished sorting.");
lid = 0;
unlink (fn_src);
for (i = 0; i < data.cnt; i++) {
if (data.larray[i]) {
msd = map_search_line2data (data.larray[i]);
if (lid != msd.id) { /* ignore double entries */
map_search_add (msd.country, msd.name, msd.id, msd.lon, msd.lat);
}
lid = msd.id;
if (i % 10000 == 0) {
sprintf (text, _("save search.mapidx %d"), i);
app_status (-1, text, 0.0);
main_wnd_update ();
}
}
if (i > 0) if (strcasecmp (data.larray[i-1], data.larray[i]) > 0) {
d_printf ("sorting went wrong. %d '%s' < %d '%s'", i-1, data.larray[i-1], i, data.larray[i]);
}
}
d_printf ("map_search_sort: complete sorted");
#ifdef HAVE_LOCALE_H
setlocale(LC_ALL, NULL);
#endif
data = map_search_sort_checkmem (data, 0);
ml_free (fdata);
};
/*****************************************************************************
* searching routines: we will use the same way as searching on hashnodes
* during converting the planet.osm file.
* check the center of the file and test fo larger or smaller value,
* go into the larger or smaller value, check here also the center.. and so on.
*/
/*
* read and convert data form position, if the correct flag is set it
* will check for the corrent start address and return it together with
* the size on file.
*/
static char maps_line[MAP_SEARCHLINE_LEN*4];
#warning maps_readpos.. read current position without checking
#warning and return start of next line.
/*
* read an random position, check if we are at the start of valid data. If not
* search for it.
* parameter: pos - entry position inside file
* data - destination of found data
* size - size of data block
* return: final start of current data block
*/
unsigned long int maps_readpos (unsigned long int pos, struct map_search_data *data, int *size) {
size_t realpos;
int readsize, start, end;
if (map_search_fd <= 0) return -1;
if (pos < MAP_SEARCHLINE_LEN*2) realpos = 0;
else realpos = pos - MAP_SEARCHLINE_LEN*2;
/* read data from file */
lseek (map_search_fd, realpos, SEEK_SET);
readsize = read (map_search_fd, maps_line, MAP_SEARCHLINE_LEN*4);
if (readsize <= 0) return -1;
/* search where the current line starts (go to the left) */
// d_printf ("start at: pos:%ld realpos:%d start:%d", pos, realpos, pos-realpos);
for (start = pos-realpos; start > 0; start--) {
// d_printf (" found:start:%-4d %x '%c' delta:%d", start, maps_line[start], maps_line[start], realpos+start-pos);
if (maps_line[start] == '\n') {
start++;
break;
}
}
if (start < 0) { /* underrun buffer? */
d_printf ("maps_readpos: found no start. pos:%ld realpos:%lld", pos, realpos);
return -1;
}
// d_printf (" maps_readpos pos:%ld start:%d delta:%d", pos, start, (realpos+start)-pos);
/* search where the current line ends */
for (end = start; end < readsize && maps_line[end] != '\n'; end++);
if (end >= readsize) {
d_printf ("maps_readpos: found no end.");
return -1;
}
*data = map_search_line2data (maps_line+start);
*size = end-start;
// d_printf (" size:%d", *size);
return realpos+start;
};
int map_search (char *name, struct map_search_data *data, int datasize) {
uint64_t blocksize, blockstart, realstart;
struct stat buf;
int i, j, cmpres, k;
if (map_search_disabled || strlen (name) == 0) return 0;
if (map_search_fd <= 0) map_search_open ();
d_printf ("map_search searching for :'%s'", name);
fstat (map_search_fd, &buf);
blocksize = buf.st_size;
blockstart = 0;
/* search for name inside the data */
do {
realstart = maps_readpos (blocksize/2+blockstart, data, &j);
if (realstart < 0) {
d_printf ("map_search something went wrong. realstart < 0 name:%s", name);
return 0;
}
d_printf ("search blocksize:%lld blockstart:%lld realstart:%lld j:%d", (long long int)blocksize, (long long int)blockstart, (long long int)realstart, j, data->name);
if ((cmpres = strncasecmp (name, data->name, strlen (name)))> 0) {
/* name is lager than data */
blocksize = blocksize - (realstart+j-blockstart);
blockstart = realstart+j+1;
}
else if (cmpres < 0) {
/* name is smaller than data */
blocksize = realstart - blockstart;
}
d_printf ("compare: '%s' with '%s' returns:%d", name, data->name, cmpres);
} while (cmpres != 0 && blocksize > MAP_SEARCHLINE_LEN*2);
/* find first data inside list, go backwards until no match found */
d_printf ("found blockstart:%lld blocksize:%lld, realstart:%lld text:'%-12s'", (long long int)blockstart,
(long long int)blocksize, (long long int)realstart, data->name);
j = 0;
if (cmpres <= 0) {
blockstart = realstart;
d_printf ("<=0 blockstart:%lld realstart:%lld name:%s", (long long int)blockstart,
(long long int)realstart, name);
do {
realstart = maps_readpos (blockstart, data, &j);
d_printf ("blockstart:%lld realstart:%lld data->name:%s", (long long int) blockstart, (long long int) realstart, data->name);
blockstart = realstart-2;
cmpres = strncasecmp (name, data->name, strlen (name));
d_printf (" cmpres:%d", cmpres);
} while (blockstart >= 0 && cmpres <= 0 && realstart >= 0);
blockstart = realstart+j;
}
else if (cmpres > 0) {
blockstart = realstart;
d_printf (">0 blockstart:%lld realstart:%lld name:%s", (long long int) blockstart, (long long int) realstart, name);
do {
realstart = maps_readpos (blockstart, data, &j);
d_printf ("blockstart:%lld realstart:%lld data->name:%s", (long long int)blockstart, (long long int)realstart, data->name);
blockstart = realstart+j;
cmpres = strncasecmp (name, data->name, strlen (name));
d_printf (" cmpres:%d", cmpres);
} while (cmpres > 0 && realstart >= 0);
blockstart = realstart;
}
if (realstart < 0) return 0;
if (blockstart < 0) blockstart = 0;
for (i = 0, k = 0; i < datasize*2 && k < datasize; i++) {
realstart = maps_readpos (blockstart, data+k, &j);
d_printf ("create list: k:%d i:%d blockstart:%lld j:%d '%s'", k, i, (long long int)blockstart, j, data[k].name);
if (realstart < 0) break;
blockstart = realstart + j;
if (strncasecmp (name, data[k].name, strlen (name)) == 0) k++;
}
return k;
};