#include "hostlist.h"
#include "simplestring.h"
#include <stdio.h>
#include <ctype.h>
#include <limits.h>
#include <algorithm>
#include <string>
using namespace lcb;
Hostlist::~Hostlist()
{
reset_strlist();
}
void
Hostlist::reset_strlist()
{
for (size_t ii = 0; ii < hoststrs.size(); ++ii) {
if (hoststrs[ii] != NULL) {
delete[] hoststrs[ii];
}
}
hoststrs.clear();
}
lcb_error_t
lcb_host_parse(lcb_host_t *host, const char *spec, int speclen, int deflport)
{
std::vector<char> zspec;
char *host_s;
char *port_s;
char *delim;
if (speclen < 0) {
speclen = strlen(spec);
} else if (speclen == 0) {
return LCB_INVALID_HOST_FORMAT;
}
if (deflport < 1) {
return LCB_INVALID_HOST_FORMAT;
}
zspec.assign(spec, spec + speclen);
zspec.push_back('\0');
host_s = &zspec[0];
if (*host_s == ':') {
return LCB_INVALID_HOST_FORMAT;
}
if ( (delim = strstr(host_s, "://"))) {
host_s = delim + 3;
}
if ((delim = strstr(host_s, "/"))) {
*delim = '\0';
}
port_s = strstr(host_s, ":");
if (port_s != NULL) {
char *endp;
long ll;
*port_s = '\0';
port_s++;
if (! *port_s) {
return LCB_INVALID_HOST_FORMAT;
}
ll = strtol(port_s, &endp, 10);
if (ll == LONG_MIN || ll == LONG_MAX) {
return LCB_INVALID_HOST_FORMAT;
}
if (*endp) {
return LCB_INVALID_HOST_FORMAT;
}
} else {
port_s = const_cast<char*>("");
}
if (strlen(host_s) > sizeof(host->host)-1 ||
strlen(port_s) > sizeof(host->port)-1 ||
*host_s == '\0') {
return LCB_INVALID_HOST_FORMAT;
}
size_t ii, hostlen = strlen(host_s);
for (ii = 0; ii < hostlen; ii++) {
if (isalnum(host_s[ii])) {
continue;
}
switch (host_s[ii]) {
case '.':
case '-':
case '_':
break;
default:
return LCB_INVALID_HOST_FORMAT;
}
}
strcpy(host->host, host_s);
if (*port_s) {
strcpy(host->port, port_s);
} else {
sprintf(host->port, "%d", deflport);
}
return LCB_SUCCESS;
}
int lcb_host_equals(const lcb_host_t *a, const lcb_host_t *b)
{
return strcmp(a->host, b->host) == 0 && strcmp(a->port, b->port) == 0;
}
bool
Hostlist::exists(const lcb_host_t& host) const
{
for (size_t ii = 0; ii < hosts.size(); ++ii) {
if (lcb_host_equals(&host, &hosts[ii])) {
return true;
}
}
return false;
}
bool
Hostlist::exists(const char *s) const
{
lcb_host_t tmp;
if (lcb_host_parse(&tmp, s, -1, 1) != LCB_SUCCESS) {
return false;
}
return exists(tmp);
}
void
Hostlist::add(const lcb_host_t& host)
{
if (exists(host)) {
return;
}
hosts.push_back(host);
reset_strlist();
}
lcb_error_t
Hostlist::add(const char *hostport, long len, int deflport)
{
lcb_error_t err = LCB_SUCCESS;
if (len < 0) {
len = strlen(hostport);
}
std::string ss(hostport, len);
if (ss.empty()) {
return LCB_SUCCESS;
}
if (ss[ss.length()-1] != ';') {
ss += ';';
}
const char *curstart = ss.c_str();
const char *delim;
while ( (delim = strstr(curstart, ";"))) {
lcb_host_t curhost;
size_t curlen;
if (delim == curstart) {
curstart++;
continue;
}
curlen = delim - curstart;
err = lcb_host_parse(&curhost, curstart, curlen, deflport);
if (err != LCB_SUCCESS) {
return err;
}
add(curhost);
curstart = delim + 1;
}
return LCB_SUCCESS;
}
lcb_host_t *
Hostlist::next(bool wrap)
{
lcb_host_t *ret;
if (empty()) {
return NULL;
}
if (ix == size()) {
if (wrap) {
ix = 0;
} else {
return NULL;
}
}
ret = &hosts[ix];
ix++;
return ret;
}
void
Hostlist::randomize()
{
std::random_shuffle(hosts.begin(), hosts.end());
reset_strlist();
}
void Hostlist::ensure_strlist() {
if (hoststrs.size()) {
return;
}
for (size_t ii = 0; ii < hosts.size(); ii++) {
const lcb_host_t& host = hosts[ii];
std::string ss(host.host);
ss.append(":").append(host.port);
char *newstr = new char[ss.size() + 1];
newstr[ss.size()] = '\0';
memcpy(newstr, ss.c_str(), ss.size());
hoststrs.push_back(newstr);
}
hoststrs.push_back(NULL);
}
Hostlist&
Hostlist::assign(const Hostlist& src)
{
clear();
reset_strlist();
for (size_t ii = 0; ii < src.size(); ++ii) {
add(src.hosts[ii]);
}
return *this;
}
extern "C" {
Hostlist* hostlist_create(void) { return new Hostlist(); }
void hostlist_destroy(hostlist_t l) { delete l; }
void hostlist_clear(hostlist_t l) { l->clear(); }
void hostlist_reset_strlist(hostlist_t l) { l->reset_strlist(); }
lcb_error_t hostlist_add_host(hostlist_t l, const lcb_host_t *h) { l->add(*h); return LCB_SUCCESS; }
lcb_host_t *hostlist_shift_next(hostlist_t hl, int wrap) { return hl->next(wrap); }
int hostlist_finished(hostlist_t l) { return l->ix == l->hosts.size(); }
size_t hostlist_size(hostlist_t l) { return l->size(); }
void hostlist_randomize(hostlist_t l) { l->randomize(); }
lcb_error_t
hostlist_add_string(hostlist_t hl, const char *spec, int len, int deflport) {
return hl->add(spec, len, deflport);
}
void
hostlist_assign(hostlist_t dst, const hostlist_t src) {
dst->assign(*src);
}
const lcb_host_t*
hostlist_get(const hostlist_t h, size_t ix) { return &h->hosts[ix]; }
const char * const *
hostlist_strents(const hostlist_t h) {
h->ensure_strlist();
if (h->hoststrs.size()) {
return &h->hoststrs[0];
} else {
return NULL;
}
}
}