47 class ClusterHostname {
54 ClusterHostname(
const string &name,
const string &prefix,
55 int num,
const string &postfix)
56 : name(name), prefix(prefix), postfix(postfix), num(num) { }
62 ClusterHostname(
const string &name) : name(name) {
63 const char *base = name.c_str();
64 const char *ptr = base;
65 while (*ptr && !isdigit(*ptr))
67 prefix.append(base, (
size_t)(ptr-base));
70 num = (int)strtol(ptr, &end, 10);
92 inline bool operator<(
const ClusterHostname &ch1,
const ClusterHostname &ch2) {
93 if (ch1.prefix.length() == ch2.prefix.length() &&
94 ch1.postfix.length() == ch2.postfix.length() &&
95 ch1.num != -1 && ch2.num != -1) {
96 int cmp = ch1.prefix.compare(ch2.prefix);
99 if (ch1.num != ch2.num)
100 return ch1.num < ch2.num;
101 cmp = ch1.postfix.compare(ch2.postfix);
104 return ch1.name.compare(ch2.name) < 0;
113 set<ClusterHostname> hosts;
117 typedef std::shared_ptr<Frame> FramePtr;
128 set<ClusterHostname> hosts;
137 Tokenizer(
const std::string &spec) : m_spec(spec) {
138 m_ptr = m_spec.c_str();
145 bool next(Token &token);
148 int current_position() {
149 return (m_ptr - m_spec.c_str()) + 1;
154 void skip_whitespace() {
155 while (*m_ptr && isspace(*m_ptr))
160 void save_position() { m_saved_ptr = m_ptr; }
163 int saved_position() {
164 return (m_saved_ptr - m_spec.c_str()) + 1;
167 const char *current_character() {
169 if (*m_ptr ==
'\'' || *m_ptr ==
'\"' || *m_ptr ==
'\?' ||
170 *m_ptr ==
'\\' || *m_ptr ==
'\a' || *m_ptr ==
'\b' ||
171 *m_ptr ==
'\f' || *m_ptr ==
'\n' || *m_ptr ==
'\r' ||
172 *m_ptr ==
'\t' || *m_ptr ==
'\v') {
173 character_buf[index++] =
'\'';
174 character_buf[index++] =
'\\';
175 if (*m_ptr ==
'\'' || *m_ptr ==
'\"' || *m_ptr ==
'\?' ||
177 character_buf[index++] = *m_ptr;
178 else if (*m_ptr ==
'\a')
179 character_buf[index++] =
'a';
180 else if (*m_ptr ==
'\b')
181 character_buf[index++] =
'b';
182 else if (*m_ptr ==
'\f')
183 character_buf[index++] =
'f';
184 else if (*m_ptr ==
'\n')
185 character_buf[index++] =
'n';
186 else if (*m_ptr ==
'\r')
187 character_buf[index++] =
'r';
188 else if (*m_ptr ==
'\t')
189 character_buf[index++] =
't';
190 else if (*m_ptr ==
'\v')
191 character_buf[index++] =
'v';
192 character_buf[index++] =
'\'';
193 character_buf[index++] =
'\0';
195 else if (*m_ptr >= 32) {
196 character_buf[index++] =
'\'';
197 character_buf[index++] = *m_ptr;
198 character_buf[index++] =
'\'';
199 character_buf[index++] =
'\0';
202 sprintf(character_buf,
"0x%x", *m_ptr);
203 return character_buf;
206 std::string extract_numeric_range(
int *beginp,
int *endp);
208 std::string m_spec {};
210 const char *m_ptr {};
212 const char *m_saved_ptr {};
214 char character_buf[8];
217 bool Tokenizer::next(Token &token) {
224 if (*m_ptr ==
'(' || *m_ptr ==
')' || *m_ptr ==
'+' ||
225 *m_ptr ==
',' || *m_ptr ==
'-') {
226 token.value = *m_ptr++;
230 if (!isalnum(*m_ptr) && *m_ptr ==
']')
235 string host_pattern {};
240 if (isalnum(*m_ptr)) {
241 const char *base = m_ptr;
242 while (*m_ptr != 0 && (isalnum(*m_ptr) || *m_ptr ==
'.' || *m_ptr ==
'-'))
244 prefix = string(base, m_ptr-base);
248 host_pattern = prefix;
249 host_pattern += extract_numeric_range(&range_begin, &range_end);
250 if (isalnum(*m_ptr) || *m_ptr ==
'.' || *m_ptr ==
'-') {
251 const char *base = m_ptr;
252 while (*m_ptr != 0 && (isalnum(*m_ptr) || *m_ptr ==
'.' || *m_ptr ==
'-'))
254 postfix_len = m_ptr - base;
255 host_pattern.append(base, postfix_len);
259 if (*m_ptr != 0 && !isspace(*m_ptr) && *m_ptr !=
'(' && *m_ptr !=
')' &&
260 *m_ptr !=
'+' && *m_ptr !=
',' && *m_ptr !=
'-')
262 current_character(), current_position());
266 if (!host_pattern.empty()) {
267 for (
int i= range_begin; i<= range_end; i++) {
268 string name =
format(host_pattern.c_str(), i);
269 string postfix = name.substr( name.length() - postfix_len);
270 token.hosts.insert( ClusterHostname(name, prefix, i, postfix) );
274 token.hosts.insert( ClusterHostname(prefix) );
279 string Tokenizer::extract_numeric_range(
int *beginp,
int *endp) {
291 "Truncated range pattern at position %d",
293 else if (!isdigit(*m_ptr))
295 "Invalid character %s in range pattern at position %d",
296 current_character(), current_position());
297 bool range_begin_leading_zero = *m_ptr ==
'0';
300 const char *base = m_ptr;
301 while (*m_ptr && isdigit(*m_ptr))
303 size_t range_begin_width = m_ptr - base;
304 *beginp = atoi(base);
312 msg =
format(
"Missing range separation character '-' at position %d",
314 else if (*m_ptr ==
'\0')
315 msg =
format(
"Truncated range pattern at position %d",
318 msg =
format(
"Invalid character %s in range pattern at position %d",
319 current_character(), current_position());
330 "Truncated range pattern at position %d",
332 else if (!isdigit(*m_ptr))
334 "Invalid character %s in range pattern at position %d",
335 current_character(), current_position());
336 bool range_end_leading_zero = *m_ptr ==
'0';
340 while (*m_ptr && isdigit(*m_ptr))
342 size_t range_end_width = m_ptr - base;
349 "Truncated range pattern at position %d",
351 else if (*m_ptr !=
']')
353 "Invalid character %s in range pattern at position %d",
354 current_character(), current_position());
355 else if (range_begin_width != range_end_width &&
356 (range_begin_leading_zero || range_end_leading_zero))
361 if (range_begin_width != range_end_width)
364 return format(
"%%0%dd", (
int)range_begin_width);
369 HostSpecification::operator std::vector<std::string>() {
370 char last_token {(char)255};
371 stack<FramePtr> frame_stack;
373 frame_stack.push(std::make_shared<Frame>());
376 Tokenizer tokenizer(m_spec);
377 while (tokenizer.next(token)) {
378 if (token.value == 0) {
379 if (frame_stack.top()->subtract) {
380 for (
auto & host : token.hosts)
381 frame_stack.top()->hosts.erase(host);
384 for (
auto & host : token.hosts)
385 frame_stack.top()->hosts.insert(host);
388 else if (token.value ==
'(')
389 frame_stack.push(std::make_shared<Frame>());
390 else if (token.value ==
')') {
391 if (last_token ==
'+' || last_token ==
',' || last_token ==
'-')
393 last_token, tokenizer.current_position() - 1);
394 if (frame_stack.size() == 1)
396 FramePtr top = frame_stack.top();
398 if (frame_stack.top()->subtract) {
399 for (
auto & host : top->hosts)
400 frame_stack.top()->hosts.erase(host);
403 for (
auto & host : top->hosts)
404 frame_stack.top()->hosts.insert(host);
407 else if (token.value ==
'+' || token.value ==
',') {
408 if (last_token != 0 && last_token !=
')')
410 token.value, tokenizer.current_position() - 1);
411 frame_stack.top()->subtract =
false;
413 else if (token.value ==
'-') {
414 if (last_token != 0 && last_token !=
')')
416 tokenizer.current_position() - 1);
417 frame_stack.top()->subtract =
true;
421 last_token = token.value;
424 if (frame_stack.size() != 1)
427 if (last_token ==
'+' || last_token ==
',' || last_token ==
'-')
429 last_token, tokenizer.current_position() - 1);
431 vector<string> hosts;
433 hosts.reserve(frame_stack.top()->hosts.size());
434 for (
auto & ch : frame_stack.top()->hosts)
435 hosts.push_back(ch.name);
Declarations for HostSpecification.
String format(const char *fmt,...)
Returns a String using printf like format facilities Vanilla snprintf is about 1.5x faster than this...
Logging routines and macros.
Compatibility Macros for C/C++.
bool operator<(const directory_entry< _Key, _Tp > &lhs, const directory_entry< _Key, _Tp > &rhs)
#define HT_THROWF(_code_, _fmt_,...)
Error codes, Exception handling, error logging.
#define HT_THROW(_code_, _msg_)