]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/hashcomp.cpp
Merge pull request #92 from Robby-/insp20-headers
[user/henk/code/inspircd.git] / src / hashcomp.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
5  *   Copyright (C) 2005-2009 Craig Edwards <craigedwards@brainbox.cc>
6  *   Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
7  *   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
8  *
9  * This file is part of InspIRCd.  InspIRCd is free software: you can
10  * redistribute it and/or modify it under the terms of the GNU General Public
11  * License as published by the Free Software Foundation, version 2.
12  *
13  * This program is distributed in the hope that it will be useful, but WITHOUT
14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  */
21
22
23 /* $Core */
24
25 #include "inspircd.h"
26 #include "hashcomp.h"
27 #include "hash_map.h"
28
29 /******************************************************
30  *
31  * The hash functions of InspIRCd are the centrepoint
32  * of the entire system. If these functions are
33  * inefficient or wasteful, the whole program suffers
34  * as a result. A lot of C programmers in the ircd
35  * scene spend a lot of time debating (arguing) about
36  * the best way to write hash functions to hash irc
37  * nicknames, channels etc.
38  * We are lucky as C++ developers as hash_map does
39  * a lot of this for us. It does intellegent memory
40  * requests, bucketing, search functions, insertion
41  * and deletion etc. All we have to do is write some
42  * overloaded comparison and hash value operators which
43  * cause it to act in an irc-like way. The features we
44  * add to the standard hash_map are:
45  *
46  * Case insensitivity: The hash_map will be case
47  * insensitive.
48  *
49  * Scandanavian Comparisons: The characters [, ], \ will
50  * be considered the lowercase of {, } and |.
51  *
52  ******************************************************/
53
54 /** A mapping of uppercase to lowercase, including scandinavian
55  * 'oddities' as specified by RFC1459, e.g. { -> [, and | -> \
56  */
57 unsigned const char rfc_case_insensitive_map[256] = {
58         0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,                                   /* 0-19 */
59         20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,                         /* 20-39 */
60         40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,                         /* 40-59 */
61         60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,             /* 60-79 */
62         112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 94, 95, 96, 97, 98, 99,           /* 80-99 */
63         100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,     /* 100-119 */
64         120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139,     /* 120-139 */
65         140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,     /* 140-159 */
66         160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,     /* 160-179 */
67         180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199,     /* 180-199 */
68         200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219,     /* 200-219 */
69         220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,     /* 220-239 */
70         240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255                          /* 240-255 */
71 };
72
73 /** Case insensitive map, ASCII rules.
74  * That is;
75  * [ != {, but A == a.
76  */
77 unsigned const char ascii_case_insensitive_map[256] = {
78         0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,                                   /* 0-19 */
79         20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,                         /* 20-39 */
80         40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,                         /* 40-59 */
81         60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,             /* 60-79 */
82         112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 91, 92, 93, 94, 95, 96, 97, 98, 99,              /* 80-99 */
83         100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,     /* 100-119 */
84         120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139,     /* 120-139 */
85         140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,     /* 140-159 */
86         160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,     /* 160-179 */
87         180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199,     /* 180-199 */
88         200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219,     /* 200-219 */
89         220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,     /* 220-239 */
90         240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255                          /* 240-255 */
91 };
92
93 /** Case sensitive map.
94  * Can technically also be used for ASCII case sensitive comparisons, as [ != {, etc.
95  */
96 unsigned const char rfc_case_sensitive_map[256] = {
97         0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
98         21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
99         41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
100         61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
101         81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100,
102         101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120,
103         121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140,
104         141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160,
105         161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180,
106         181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200,
107         201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220,
108         221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240,
109         241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255
110 };
111
112 /* convert a string to lowercase. Note following special circumstances
113  * taken from RFC 1459. Many "official" server branches still hold to this
114  * rule so i will too;
115  *
116  *  Because of IRC's scandanavian origin, the characters {}| are
117  *  considered to be the lower case equivalents of the characters []\,
118  *  respectively. This is a critical issue when determining the
119  *  equivalence of two nicknames.
120  */
121 void nspace::strlower(char *n)
122 {
123         if (n)
124         {
125                 for (char* t = n; *t; t++)
126                         *t = national_case_insensitive_map[(unsigned char)*t];
127         }
128 }
129
130 #if defined(WINDOWS) && !defined(HASHMAP_DEPRECATED)
131         size_t nspace::hash_compare<std::string, std::less<std::string> >::operator()(const std::string &s) const
132 #else
133         #ifdef HASHMAP_DEPRECATED
134                 size_t CoreExport nspace::insensitive::operator()(const std::string &s) const
135         #else
136                 size_t nspace::hash<std::string>::operator()(const std::string &s) const
137         #endif
138 #endif
139 {
140         /* XXX: NO DATA COPIES! :)
141          * The hash function here is practically
142          * a copy of the one in STL's hash_fun.h,
143          * only with *x replaced with national_case_insensitive_map[*x].
144          * This avoids a copy to use hash<const char*>
145          */
146         register size_t t = 0;
147         for (std::string::const_iterator x = s.begin(); x != s.end(); ++x) /* ++x not x++, as its faster */
148                 t = 5 * t + national_case_insensitive_map[(unsigned char)*x];
149         return t;
150 }
151
152
153 #if defined(WINDOWS) && !defined(HASHMAP_DEPRECATED)
154         size_t nspace::hash_compare<irc::string, std::less<irc::string> >::operator()(const irc::string &s) const
155 #else
156         size_t CoreExport irc::hash::operator()(const irc::string &s) const
157 #endif
158 {
159         register size_t t = 0;
160         for (irc::string::const_iterator x = s.begin(); x != s.end(); ++x) /* ++x not x++, as its faster */
161                 t = 5 * t + national_case_insensitive_map[(unsigned char)*x];
162         return t;
163 }
164
165 bool irc::StrHashComp::operator()(const std::string& s1, const std::string& s2) const
166 {
167         const unsigned char* n1 = (const unsigned char*)s1.c_str();
168         const unsigned char* n2 = (const unsigned char*)s2.c_str();
169         for (; *n1 && *n2; n1++, n2++)
170                 if (national_case_insensitive_map[*n1] != national_case_insensitive_map[*n2])
171                         return false;
172         return (national_case_insensitive_map[*n1] == national_case_insensitive_map[*n2]);
173 }
174
175 /******************************************************
176  *
177  * This is the implementation of our special irc::string
178  * class which is a case-insensitive equivalent to
179  * std::string which is not only case-insensitive but
180  * can also do scandanavian comparisons, e.g. { = [, etc.
181  *
182  * This class depends on the const array 'national_case_insensitive_map'.
183  *
184  ******************************************************/
185
186 bool irc::irc_char_traits::eq(char c1st, char c2nd)
187 {
188         return national_case_insensitive_map[(unsigned char)c1st] == national_case_insensitive_map[(unsigned char)c2nd];
189 }
190
191 bool irc::irc_char_traits::ne(char c1st, char c2nd)
192 {
193         return national_case_insensitive_map[(unsigned char)c1st] != national_case_insensitive_map[(unsigned char)c2nd];
194 }
195
196 bool irc::irc_char_traits::lt(char c1st, char c2nd)
197 {
198         return national_case_insensitive_map[(unsigned char)c1st] < national_case_insensitive_map[(unsigned char)c2nd];
199 }
200
201 int irc::irc_char_traits::compare(const char* str1, const char* str2, size_t n)
202 {
203         for(unsigned int i = 0; i < n; i++)
204         {
205                 if(national_case_insensitive_map[(unsigned char)*str1] > national_case_insensitive_map[(unsigned char)*str2])
206                         return 1;
207
208                 if(national_case_insensitive_map[(unsigned char)*str1] < national_case_insensitive_map[(unsigned char)*str2])
209                         return -1;
210
211                 if(*str1 == 0 || *str2 == 0)
212                         return 0;
213
214                 str1++;
215                 str2++;
216         }
217         return 0;
218 }
219
220 const char* irc::irc_char_traits::find(const char* s1, int  n, char c)
221 {
222         while(n-- > 0 && national_case_insensitive_map[(unsigned char)*s1] != national_case_insensitive_map[(unsigned char)c])
223                 s1++;
224         return (n >= 0) ? s1 : NULL;
225 }
226
227 irc::tokenstream::tokenstream(const std::string &source) : tokens(source), last_pushed(false)
228 {
229         /* Record starting position and current position */
230         last_starting_position = tokens.begin();
231         n = tokens.begin();
232 }
233
234 irc::tokenstream::~tokenstream()
235 {
236 }
237
238 bool irc::tokenstream::GetToken(std::string &token)
239 {
240         std::string::iterator lsp = last_starting_position;
241
242         while (n != tokens.end())
243         {
244                 /** Skip multi space, converting "  " into " "
245                  */
246                 while ((n+1 != tokens.end()) && (*n == ' ') && (*(n+1) == ' '))
247                         n++;
248
249                 if ((last_pushed) && (*n == ':'))
250                 {
251                         /* If we find a token thats not the first and starts with :,
252                          * this is the last token on the line
253                          */
254                         std::string::iterator curr = ++n;
255                         n = tokens.end();
256                         token = std::string(curr, tokens.end());
257                         return true;
258                 }
259
260                 last_pushed = false;
261
262                 if ((*n == ' ') || (n+1 == tokens.end()))
263                 {
264                         /* If we find a space, or end of string, this is the end of a token.
265                          */
266                         last_starting_position = n+1;
267                         last_pushed = *n == ' ';
268
269                         std::string strip(lsp, n+1 == tokens.end() ? n+1  : n++);
270                         while ((strip.length()) && (strip.find_last_of(' ') == strip.length() - 1))
271                                 strip.erase(strip.end() - 1);
272
273                         token = strip;
274                         return !token.empty();
275                 }
276
277                 n++;
278         }
279         token.clear();
280         return false;
281 }
282
283 bool irc::tokenstream::GetToken(irc::string &token)
284 {
285         std::string stdstring;
286         bool returnval = GetToken(stdstring);
287         token = assign(stdstring);
288         return returnval;
289 }
290
291 bool irc::tokenstream::GetToken(int &token)
292 {
293         std::string tok;
294         bool returnval = GetToken(tok);
295         token = ConvToInt(tok);
296         return returnval;
297 }
298
299 bool irc::tokenstream::GetToken(long &token)
300 {
301         std::string tok;
302         bool returnval = GetToken(tok);
303         token = ConvToInt(tok);
304         return returnval;
305 }
306
307 irc::sepstream::sepstream(const std::string &source, char seperator) : tokens(source), sep(seperator)
308 {
309         last_starting_position = tokens.begin();
310         n = tokens.begin();
311 }
312
313 bool irc::sepstream::GetToken(std::string &token)
314 {
315         std::string::iterator lsp = last_starting_position;
316
317         while (n != tokens.end())
318         {
319                 if ((*n == sep) || (n+1 == tokens.end()))
320                 {
321                         last_starting_position = n+1;
322                         token = std::string(lsp, n+1 == tokens.end() ? n+1  : n++);
323
324                         while ((token.length()) && (token.find_last_of(sep) == token.length() - 1))
325                                 token.erase(token.end() - 1);
326
327                         if (token.empty())
328                                 n++;
329
330                         return n == tokens.end() ? false : true;
331                 }
332
333                 n++;
334         }
335
336         token = "";
337         return false;
338 }
339
340 const std::string irc::sepstream::GetRemaining()
341 {
342         return std::string(n, tokens.end());
343 }
344
345 bool irc::sepstream::StreamEnd()
346 {
347         return ((n + 1) == tokens.end());
348 }
349
350 irc::sepstream::~sepstream()
351 {
352 }
353
354 std::string irc::hex(const unsigned char *raw, size_t rawsz)
355 {
356         if (!rawsz)
357                 return "";
358
359         /* EWW! This used to be using sprintf, which is WAY inefficient. -Special */
360
361         const char *hex = "0123456789abcdef";
362         static char hexbuf[MAXBUF];
363
364         size_t i, j;
365         for (i = 0, j = 0; j < rawsz; ++j)
366         {
367                 hexbuf[i++] = hex[raw[j] / 16];
368                 hexbuf[i++] = hex[raw[j] % 16];
369         }
370         hexbuf[i] = 0;
371
372         return hexbuf;
373 }
374
375 CoreExport const char* irc::Spacify(const char* n)
376 {
377         static char x[MAXBUF];
378         strlcpy(x,n,MAXBUF);
379         for (char* y = x; *y; y++)
380                 if (*y == '_')
381                         *y = ' ';
382         return x;
383 }
384
385
386 irc::modestacker::modestacker(bool add) : adding(add)
387 {
388         sequence.clear();
389         sequence.push_back("");
390 }
391
392 void irc::modestacker::Push(char modeletter, const std::string &parameter)
393 {
394         *(sequence.begin()) += modeletter;
395         sequence.push_back(parameter);
396 }
397
398 void irc::modestacker::Push(char modeletter)
399 {
400         this->Push(modeletter,"");
401 }
402
403 void irc::modestacker::PushPlus()
404 {
405         this->Push('+',"");
406 }
407
408 void irc::modestacker::PushMinus()
409 {
410         this->Push('-',"");
411 }
412
413 int irc::modestacker::GetStackedLine(std::vector<std::string> &result, int max_line_size)
414 {
415         if (sequence.empty())
416         {
417                 return 0;
418         }
419
420         unsigned int n = 0;
421         int size = 1; /* Account for initial +/- char */
422         int nextsize = 0;
423         int start = result.size();
424         std::string modeline = adding ? "+" : "-";
425         result.push_back(modeline);
426
427         if (sequence.size() > 1)
428                 nextsize = sequence[1].length() + 2;
429
430         while (!sequence[0].empty() && (sequence.size() > 1) && (n < ServerInstance->Config->Limits.MaxModes) && ((size + nextsize) < max_line_size))
431         {
432                 modeline += *(sequence[0].begin());
433                 if (!sequence[1].empty())
434                 {
435                         result.push_back(sequence[1]);
436                         size += nextsize; /* Account for mode character and whitespace */
437                 }
438                 sequence[0].erase(sequence[0].begin());
439                 sequence.erase(sequence.begin() + 1);
440
441                 if (sequence.size() > 1)
442                         nextsize = sequence[1].length() + 2;
443
444                 n++;
445         }
446         result[start] = modeline;
447
448         return n;
449 }
450
451 irc::stringjoiner::stringjoiner(const std::string &seperator, const std::vector<std::string> &sequence, int begin, int end)
452 {
453         if (end < begin)
454                 throw "stringjoiner logic error, this causes problems.";
455
456         for (int v = begin; v < end; v++)
457                 joined.append(sequence[v]).append(seperator);
458         joined.append(sequence[end]);
459 }
460
461 irc::stringjoiner::stringjoiner(const std::string &seperator, const std::deque<std::string> &sequence, int begin, int end)
462 {
463         if (end < begin)
464                 throw "stringjoiner logic error, this causes problems.";
465
466         for (int v = begin; v < end; v++)
467                 joined.append(sequence[v]).append(seperator);
468         joined.append(sequence[end]);
469 }
470
471 irc::stringjoiner::stringjoiner(const std::string &seperator, const char* const* sequence, int begin, int end)
472 {
473         if (end < begin)
474                 throw "stringjoiner logic error, this causes problems.";
475
476         for (int v = begin; v < end; v++)
477                 joined.append(sequence[v]).append(seperator);
478         joined.append(sequence[end]);
479 }
480
481 std::string& irc::stringjoiner::GetJoined()
482 {
483         return joined;
484 }
485
486 irc::portparser::portparser(const std::string &source, bool allow_overlapped) : in_range(0), range_begin(0), range_end(0), overlapped(allow_overlapped)
487 {
488         sep = new irc::commasepstream(source);
489         overlap_set.clear();
490 }
491
492 irc::portparser::~portparser()
493 {
494         delete sep;
495 }
496
497 bool irc::portparser::Overlaps(long val)
498 {
499         if (!overlapped)
500                 return false;
501
502         if (overlap_set.find(val) == overlap_set.end())
503         {
504                 overlap_set[val] = true;
505                 return false;
506         }
507         else
508                 return true;
509 }
510
511 long irc::portparser::GetToken()
512 {
513         if (in_range > 0)
514         {
515                 in_range++;
516                 if (in_range <= range_end)
517                 {
518                         if (!Overlaps(in_range))
519                         {
520                                 return in_range;
521                         }
522                         else
523                         {
524                                 while (((Overlaps(in_range)) && (in_range <= range_end)))
525                                         in_range++;
526
527                                 if (in_range <= range_end)
528                                         return in_range;
529                         }
530                 }
531                 else
532                         in_range = 0;
533         }
534
535         std::string x;
536         sep->GetToken(x);
537
538         if (x.empty())
539                 return 0;
540
541         while (Overlaps(atoi(x.c_str())))
542         {
543                 if (!sep->GetToken(x))
544                         return 0;
545         }
546
547         std::string::size_type dash = x.rfind('-');
548         if (dash != std::string::npos)
549         {
550                 std::string sbegin = x.substr(0, dash);
551                 std::string send = x.substr(dash+1, x.length());
552                 range_begin = atoi(sbegin.c_str());
553                 range_end = atoi(send.c_str());
554
555                 if ((range_begin > 0) && (range_end > 0) && (range_begin < 65536) && (range_end < 65536) && (range_begin < range_end))
556                 {
557                         in_range = range_begin;
558                         return in_range;
559                 }
560                 else
561                 {
562                         /* Assume its just the one port */
563                         return atoi(sbegin.c_str());
564                 }
565         }
566         else
567         {
568                 return atoi(x.c_str());
569         }
570 }
571
572 /*const std::basic_string& SearchAndReplace(std::string& text, const std::string& pattern, const std::string& replace)
573 {
574         std::string replacement;
575         if ((!pattern.empty()) && (!text.empty()))
576         {
577                 for (std::string::size_type n = 0; n != text.length(); ++n)
578                 {
579                         if (text.length() >= pattern.length() && text.substr(n, pattern.length()) == pattern)
580                         {
581                                 replacement.append(replace);
582                                 n = n + pattern.length() - 1;
583                         }
584                         else
585                         {
586                                 replacement += text[n];
587                         }
588                 }
589         }
590         text = replacement;
591         return text;
592 }*/