1 /** @file stringutils.h
2  * @brief Various handy helpers which std::string really should provide.
3  */
4 /* Copyright (C) 2004,2005,2006,2007,2008,2009,2010 Olly Betts
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
19  */
20 
21 #ifndef XAPIAN_INCLUDED_STRINGUTILS_H
22 #define XAPIAN_INCLUDED_STRINGUTILS_H
23 
24 #include <xapian/visibility.h>
25 
26 #include <algorithm>
27 #include <string>
28 #include <cstring>
29 
30 /** Helper macro for STRINGIZE - the nested call is required because of how
31  *  # works in macros.
32  */
33 #define STRINGIZE_(X) #X
34 
35 /// The STRINGIZE macro converts its parameter into a string constant.
36 #define STRINGIZE(X) STRINGIZE_(X)
37 
38 /** Returns the length of a string constant.
39  *
40  *  We rely on concatenation of string literals to produce an error if this
41  *  macro is applied to something other than a string literal.
42  */
43 #define CONST_STRLEN(S) (sizeof(S"") - 1)
44 
45 inline bool
startswith(const std::string & s,char pfx)46 startswith(const std::string & s, char pfx)
47 {
48     return !s.empty() && s[0] == pfx;
49 }
50 
51 inline bool
startswith(const std::string & s,const char * pfx,size_t len)52 startswith(const std::string & s, const char * pfx, size_t len)
53 {
54     return s.size() >= len && (std::memcmp(s.data(), pfx, len) == 0);
55 }
56 
57 inline bool
startswith(const std::string & s,const char * pfx)58 startswith(const std::string & s, const char * pfx)
59 {
60     return startswith(s, pfx, std::strlen(pfx));
61 }
62 
63 inline bool
startswith(const std::string & s,const std::string & pfx)64 startswith(const std::string & s, const std::string & pfx)
65 {
66     return startswith(s, pfx.data(), pfx.size());
67 }
68 
69 inline bool
endswith(const std::string & s,char sfx)70 endswith(const std::string & s, char sfx)
71 {
72     return !s.empty() && s[s.size() - 1] == sfx;
73 }
74 
75 inline bool
endswith(const std::string & s,const char * sfx,size_t len)76 endswith(const std::string & s, const char * sfx, size_t len)
77 {
78     return s.size() >= len && (std::memcmp(s.data() + s.size() - len, sfx, len) == 0);
79 }
80 
81 inline bool
endswith(const std::string & s,const char * sfx)82 endswith(const std::string & s, const char * sfx)
83 {
84     return endswith(s, sfx, std::strlen(sfx));
85 }
86 
87 inline bool
endswith(const std::string & s,const std::string & sfx)88 endswith(const std::string & s, const std::string & sfx)
89 {
90     return endswith(s, sfx.data(), sfx.size());
91 }
92 
93 inline std::string::size_type
common_prefix_length(const std::string & a,const std::string & b)94 common_prefix_length(const std::string &a, const std::string &b)
95 {
96     std::string::size_type minlen = std::min(a.size(), b.size());
97     std::string::size_type common;
98     for (common = 0; common < minlen; ++common) {
99 	if (a[common] != b[common]) break;
100     }
101     return common;
102 }
103 
104 // Like C's isXXXXX() but:
105 //  (a) always work in the C locale
106 //  (b) handle signed char as well as unsigned char
107 //  (c) have a suitable signature for use as predicates with find_if()
108 //  (d) add negated versions isnotXXXXX() which are useful as predicates
109 //  (e) add some extra categories we find useful
110 
111 namespace Xapian {
112     namespace Internal {
113 	const unsigned char IS_DIGIT = 0x01;
114 	const unsigned char IS_LOWER = 0x02;
115 	const unsigned char IS_UPPER = 0x04;
116 	const unsigned char IS_HEX   = 0x08;
117 	const unsigned char IS_SIGN  = 0x10;
118 	const unsigned char IS_SPACE = 0x20;
119 	XAPIAN_VISIBILITY_DEFAULT
120 	extern const unsigned char is_tab[];
121 	XAPIAN_VISIBILITY_DEFAULT
122 	extern const unsigned char lo_tab[];
123 	XAPIAN_VISIBILITY_DEFAULT
124 	extern const unsigned char up_tab[];
125     }
126 }
127 
128 // Add explicit conversion to bool to prevent compiler warning from "aCC +w":
129 // Warning (suggestion) 818: [...] # Type `int' is larger than type `bool',
130 // truncation in value may result.
131 
C_tab_(char ch)132 inline unsigned char C_tab_(char ch) {
133     using Xapian::Internal::is_tab;
134     return is_tab[static_cast<unsigned char>(ch)];
135 }
136 
C_isdigit(char ch)137 inline bool C_isdigit(char ch) {
138     using namespace Xapian::Internal;
139     return bool(C_tab_(ch) & IS_DIGIT);
140 }
141 
C_isxdigit(char ch)142 inline bool C_isxdigit(char ch) {
143     using namespace Xapian::Internal;
144     return bool(C_tab_(ch) & IS_HEX);
145 }
146 
C_islcxdigit(char ch)147 inline bool C_islcxdigit(char ch) {
148     using namespace Xapian::Internal;
149     return (C_tab_(ch) & (IS_UPPER|IS_HEX)) == IS_HEX;
150 }
151 
C_isupper(char ch)152 inline bool C_isupper(char ch) {
153     using namespace Xapian::Internal;
154     return bool(C_tab_(ch) & IS_UPPER);
155 }
156 
C_islower(char ch)157 inline bool C_islower(char ch) {
158     using namespace Xapian::Internal;
159     return bool(C_tab_(ch) & IS_LOWER);
160 }
161 
C_isalpha(char ch)162 inline bool C_isalpha(char ch) {
163     using namespace Xapian::Internal;
164     return bool(C_tab_(ch) & (IS_UPPER|IS_LOWER));
165 }
166 
C_isalnum(char ch)167 inline bool C_isalnum(char ch) {
168     using namespace Xapian::Internal;
169     return bool(C_tab_(ch) & (IS_UPPER|IS_LOWER|IS_DIGIT));
170 }
171 
C_isspace(char ch)172 inline bool C_isspace(char ch) {
173     using namespace Xapian::Internal;
174     return bool(C_tab_(ch) & IS_SPACE);
175 }
176 
C_issign(char ch)177 inline bool C_issign(char ch) {
178     using namespace Xapian::Internal;
179     return bool(C_tab_(ch) & IS_SIGN);
180 }
181 
C_isupdig(char ch)182 inline bool C_isupdig(char ch) {
183     using namespace Xapian::Internal;
184     return bool(C_tab_(ch) & (IS_UPPER|IS_DIGIT));
185 }
186 
C_isnotdigit(char ch)187 inline bool C_isnotdigit(char ch) { return !C_isdigit(ch); }
C_isnotxdigit(char ch)188 inline bool C_isnotxdigit(char ch) { return !C_isxdigit(ch); }
C_isnotupper(char ch)189 inline bool C_isnotupper(char ch) { return !C_isupper(ch); }
C_isnotlower(char ch)190 inline bool C_isnotlower(char ch) { return !C_islower(ch); }
C_isnotalpha(char ch)191 inline bool C_isnotalpha(char ch) { return !C_isalpha(ch); }
C_isnotalnum(char ch)192 inline bool C_isnotalnum(char ch) { return !C_isalnum(ch); }
C_isnotspace(char ch)193 inline bool C_isnotspace(char ch) { return !C_isspace(ch); }
C_isnotsign(char ch)194 inline bool C_isnotsign(char ch) { return !C_issign(ch); }
195 
C_tolower(char ch)196 inline char C_tolower(char ch) {
197     using namespace Xapian::Internal;
198     return lo_tab[static_cast<unsigned char>(ch)];
199 }
200 
C_toupper(char ch)201 inline char C_toupper(char ch) {
202     using namespace Xapian::Internal;
203     return up_tab[static_cast<unsigned char>(ch)];
204 }
205 
206 #endif // XAPIAN_INCLUDED_STRINGUTILS_H
207