QueryString class

feature/UrlUtils
Kenneth Barbour 2018-11-27 16:26:40 -05:00
parent 373d1d02ae
commit ae9fa1a9e3
3 changed files with 250 additions and 0 deletions

121
src/QueryString.cpp 100644
View File

@ -0,0 +1,121 @@
#include "QueryString.h"
#include <string.h>
/**
* Create a buffer long enough to hold all the string data,
* parse the query, filling the buffer, and creating the fields
*/
QueryString::QueryString(const char* query) : n(0)
{
if (query[0] == '\0') return;
// create buffer
buffer_length = urlenc::decoded_length(query) + 1;
buffer = (char*) malloc(buffer_length);
// create fields
n = QueryString::str_count_fields(query);
fields = (QueryStringField*) malloc(sizeof(QueryStringField) * n);
size_t i = 0; // index of character in query
size_t j = 0; // index of character in buffer
size_t k = 0; // index of field in fields
char c;
fields[0].name = buffer;
fields[0].value = nullptr;
do {
c = query[i];
if (c == '=') {
c = '\0';
fields[k].value = buffer + j + 1;
} else if (c == '&') {
c = '\0';
fields[++k].name = buffer + j + 1;
fields[k].value = nullptr;
} else if (c == '%') {
c = urlenc::enctochar(query+i);
i += 2;
}
buffer[j++] = c;
} while (query[i++] != '\0');
buffer[j] = '\0';
}
size_t QueryString::count() const
{
return n;
}
size_t QueryString::count(const char* key) const
{
size_t count = 0;
size_t i = 0;
while (i < n) {
if (strcmp(fields[i].name, key) == 0) count++;
i++;
}
return count;
}
const char* QueryString::key(const char* name) const
{
return key(name, 0);
}
const char* QueryString::key(const char* key, size_t n) const
{
long i = find(key, n);
if (i == -1) return nullptr;
return fields[i].value;
}
const char* QueryString::keyAt(size_t index) const
{
if (index >= n) return nullptr;
return fields[index].name;
}
const char* QueryString::valueAt(size_t index) const
{
if (index >= n) return nullptr;
return fields[index].value;
}
bool QueryString::has(const char* key) const
{
return (find(key, 0) != -1);
}
long QueryString::find(const char* key, size_t nth) const
{
size_t matches = 0;
size_t i = 0;
while (i < n) {
if (strcmp(fields[i].name, key) == 0) matches++;
if (matches > nth) return i;
i++;
}
return -1;
}
size_t QueryString::str_count_fields(const char* query)
{
if (query[0] == '\0') return 0;
size_t n = 1;
size_t i = 0;
char c;
do {
c = query[i];
n += (c == '&');
i++;
} while (c != '\0');
return n;
}
QueryString::~QueryString()
{
free(buffer);
free(fields);
}

36
src/QueryString.h 100644
View File

@ -0,0 +1,36 @@
#ifndef _QUERYSTRING_H
#define _QUERYSTRING_H
#include <stdlib.h>
#include "urlenc.h"
typedef struct QueryStringField {
char * name;
char * value;
} QueryStringField;
class QueryString {
public:
QueryString(const char* );
~QueryString();
size_t count() const;
size_t count(const char*) const;
const char* key(const char*) const;
const char* key(const char*, size_t) const;
const char* keyAt(size_t) const;
const char* valueAt(size_t) const;
bool has(const char*) const;
static size_t str_count_fields(const char*);
private:
char* buffer;
size_t buffer_length;
QueryStringField* fields;
size_t n;
long find(const char*, size_t) const;
};
#endif // _QUERYSTRING_H include guard

View File

@ -0,0 +1,93 @@
#include "catch.hpp"
#include "QueryString.h"
#include "string.h"
using Catch::Matchers::Equals;
TEST_CASE("Construct", "[QueryString]")
{
const char* query = "foo=bar&baz=qux";
QueryString qs(query);
CHECK(qs.count() == 2);
}
TEST_CASE("Count keys", "[QueryString]")
{
QueryString qs("foo=bar&baz=qux");
CHECK(qs.count("foo") == 1);
CHECK(qs.count("baz") == 1);
}
TEST_CASE("Encoded characters", "[QueryString]")
{
QueryString qs("Foo%20Bar=Baz%3DQux&apple=Orange%26Apple");
CHECK(qs.count() == 2);
CHECK_THAT(qs.keyAt(0), Equals("Foo Bar"));
CHECK_THAT(qs.valueAt(0), Equals("Baz=Qux"));
CHECK_THAT(qs.keyAt(1), Equals("apple"));
CHECK_THAT(qs.valueAt(1), Equals("Orange&Apple"));
}
TEST_CASE("Find keys", "[QueryString]")
{
QueryString qs("foo=bar&baz=qux");
CHECK_THAT(qs.key("foo"), Equals("bar"));
CHECK_THAT(qs.key("baz"), Equals("qux"));
CHECK_THAT(qs.keyAt(0), Equals("foo"));
CHECK_THAT(qs.valueAt(0), Equals("bar"));
CHECK_THAT(qs.keyAt(1), Equals("baz"));
CHECK_THAT(qs.valueAt(1), Equals("qux"));
}
TEST_CASE("Repeated Keys", "[QueryString]")
{
QueryString qs("foo=1234&foo=5678&foo=bar");
CHECK(qs.count() == 3);
CHECK(qs.count("foo") == 3);
CHECK_THAT(qs.key("foo", 0), Equals("1234"));
CHECK_THAT(qs.key("foo", 1), Equals("5678"));
CHECK_THAT(qs.key("foo", 2), Equals("bar"));
}
TEST_CASE("Null values", "[QueryString]")
{
QueryString qs("foo&bar&baz");
CHECK(qs.count() == 3);
CHECK_THAT(qs.keyAt(0), Equals("foo"));
CHECK_THAT(qs.keyAt(1), Equals("bar"));
CHECK_THAT(qs.keyAt(2), Equals("baz"));
CHECK(qs.valueAt(0) == nullptr);
CHECK(qs.valueAt(1) == nullptr);
CHECK(qs.valueAt(2) == nullptr);
}
TEST_CASE("Null Empty", "[QueryString]")
{
QueryString qs("foo=&bar=&baz=");
CHECK(qs.count() == 3);
CHECK_THAT(qs.keyAt(0), Equals("foo"));
CHECK_THAT(qs.keyAt(1), Equals("bar"));
CHECK_THAT(qs.keyAt(2), Equals("baz"));
CHECK_THAT(qs.valueAt(0), Equals(""));
CHECK_THAT(qs.valueAt(1), Equals(""));
CHECK_THAT(qs.valueAt(2), Equals(""));
}
TEST_CASE("Has", "[QueryString]")
{
QueryString qs("foo=&bar&baz=qux");
CHECK(qs.has("foo"));
CHECK(qs.has("bar"));
CHECK(qs.has("baz"));
CHECK(!qs.has("qux"));
}
TEST_CASE("str_count_fields", "[QueryString][str_count_fields]")
{
CHECK(QueryString::str_count_fields("") == 0);
CHECK(QueryString::str_count_fields("foo") == 1);
CHECK(QueryString::str_count_fields("foo&bar") == 2);
CHECK(QueryString::str_count_fields("foo=bar&baz=qux") == 2);
CHECK(QueryString::str_count_fields("foo&bar&foo&bar") == 4);
}