QueryString class
parent
373d1d02ae
commit
ae9fa1a9e3
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue