Browse Source

make http request and response childs of a common parent http message

master
Georg Hopp 14 years ago
parent
commit
fa58a6cc81
  1. 6
      ChangeLog
  2. 50
      include/class.h
  3. 28
      include/http/message.h
  4. 10
      include/http/request.h
  5. 5
      include/http/request/parser.h
  6. 10
      include/http/response.h
  7. 13
      include/http/worker.h
  8. 5
      src/Makefile.am
  9. 47
      src/http/header/add.c
  10. 61
      src/http/message.c
  11. 30
      src/http/message/helper.c
  12. 36
      src/http/request.c
  13. 8
      src/http/request/has_keep_alive.c
  14. 38
      src/http/request/parser/get_body.c
  15. 4
      src/http/request/parser/get_header.c
  16. 5
      src/http/request/parser/get_request_line.c
  17. 32
      src/http/request/parser/parse.c
  18. 44
      src/http/request/parser/read.c
  19. 39
      src/http/response.c
  20. 20
      src/http/response/404.c
  21. 12
      src/http/response/size_get.c
  22. 11
      src/http/response/to_string.c
  23. 4
      src/server/run.c
  24. 333
      tests/tst-tsearch.c

6
ChangeLog

@ -1,6 +1,10 @@
2012-02-12 12:43:56 +0100 Georg Hopp
* make http request and response childs of a common parent http message (HEAD, master)
2012-02-12 04:13:54 +0100 Georg Hopp
* remove now obsoleted header_sort (HEAD, master)
* remove now obsoleted header_sort
2012-02-12 04:05:38 +0100 Georg Hopp

50
include/class.h

@ -42,28 +42,44 @@
* @TODO: actually i use gcc feature ## for variadoc... think about
* a way to make this standard.
*/
#define _CALL(object,_iface,method,...) \
class_ptr class = class_getClass((object)); \
struct i_##_iface * iface; \
if (class->init) class->init(); \
iface = (struct i_##_iface *)class_getInterface(&class, &i_##_iface); \
while ((NULL == iface || NULL == iface->method) && HAS_PARENT(class)) { \
class = class->parent; \
if (class->init) class->init(); \
iface = (struct i_##_iface *)class_getInterface(&class, &i_##_iface); \
}; \
assert(NULL != iface->method);
#define _CALL(object,_iface,method,...) \
do { \
class_ptr class = class_getClass((object)); \
if (class->init) class->init(); \
iface = (struct i_##_iface *)class_getInterface(&class, &i_##_iface); \
while ((NULL == iface || NULL == iface->method) && HAS_PARENT(class)) { \
class = class->parent; \
if (class->init) class->init(); \
iface = (struct i_##_iface *)class_getInterface(&class, &i_##_iface); \
}; \
assert(NULL != iface->method); \
} while(0)
#define CALL(object,_iface,method,...) \
do { \
#define CALL(object,_iface,method,...) \
do { \
struct i_##_iface * iface; \
_CALL(object, _iface, method, ##__VA_ARGS__); \
iface->method(object, ##__VA_ARGS__); \
iface->method(object, ##__VA_ARGS__); \
} while(0)
#define RETCALL(object,_iface,method,ret,...) \
do { \
#define RETCALL(object,_iface,method,ret,...) \
do { \
struct i_##_iface * iface; \
_CALL(object, _iface, method, ##__VA_ARGS__); \
ret = iface->method(object, ##__VA_ARGS__); \
ret = iface->method(object, ##__VA_ARGS__); \
} while(0)
#define PARENTCALL(object,_iface,method,...) \
do { \
struct i_##_iface * iface; \
class_ptr class = class_getClass((object)); \
if (class->init) class->init(); \
assert(HAS_PARENT(class)); \
class = class->parent; \
if (class->init) class->init(); \
iface = (struct i_##_iface *)class_getInterface(&class, &i_##_iface); \
assert(NULL != iface->method); \
iface->method(object, ##__VA_ARGS__); \
} while(0)

28
include/http/message.h

@ -0,0 +1,28 @@
#ifndef __HTTP_MESSAGE__
#define __HTTP_MESSAGE__
#include "class.h"
#include "http/header.h"
typedef enum e_HttpMessageType {
HTTP_MESSAGE_BUFFERED=0,
HTTP_MESSAGE_PIPED
} HttpMessageType;
CLASS(HttpMessage) {
char * version;
HttpHeader header;
HttpMessageType type;
union {
char * buffer;
int handle;
} body;
int nbody;
};
#endif // __HTTP_MESSAGE__
// vim: set ts=4 sw=4:

10
include/http/request.h

@ -2,17 +2,13 @@
#define __HTTP_REQUEST_H__
#include "class.h"
#include "http/header.h"
#include "http/message.h"
CLASS(HttpRequest) {
EXTENDS(HttpMessage);
char * method;
char * uri;
char * version;
HttpHeader header;
char * body;
int nbody;
};
char httpRequestHasKeepAlive(HttpRequest);

5
include/http/request/parser.h

@ -7,6 +7,9 @@
#define HTTP_REQUEST_PARSER_READ_CHUNK 1024
#define REMAINS(pars) \
((pars)->buffer_used - ((pars)->cur_data - (pars)->buffer))
typedef enum e_HttpRequestState {
HTTP_REQUEST_GARBAGE=0,
@ -32,6 +35,8 @@ CLASS(HttpRequestParser) {
size_t httpRequestParserRead(HttpRequestParser, int);
void httpRequestParserParse(HttpRequestParser);
void httpRequestParserGetBody(HttpRequestParser);
void httpRequestParserGetRequestLine(HttpRequest, char *);
void httpRequestParserGetHeader(HttpRequest, char *);

10
include/http/response.h

@ -4,18 +4,14 @@
#include <sys/types.h>
#include "class.h"
#include "http/header.h"
#include "http/message.h"
CLASS(HttpResponse) {
char * version;
EXTENDS(HttpMessage);
unsigned int status;
char * reason;
HttpHeader header;
char * body;
int nbody;
};
HttpResponse httpResponse404();

13
include/http/worker.h

@ -0,0 +1,13 @@
#ifndef __HTTP_WORKER_H__
#define __HTTP_WORKER_H__
#include "class.h"
#include "http/request/parser.h"
CLASS(HttpWorker) {
HttpRequestParser parser;
};
#endif // __HTTP_WORKER_H__
// vim: set ts=4 sw=4:

5
src/Makefile.am

@ -6,6 +6,7 @@ CLASS = class.c interface.c
SOCKET = socket.c socket/accept.c socket/connect.c socket/listen.c
SERVER = server.c server/run.c server/close_conn.c
LOGGER = logger.c logger/stderr.c logger/syslog.c
MSG = http/message.c
REQ = http/request.c http/request/queue.c http/request/has_keep_alive.c
RESP = http/response.c http/response/404.c http/response/size_get.c \
http/response/to_string.c
@ -13,7 +14,7 @@ HEADER = http/header.c http/header/get.c http/header/add.c \
http/header/size_get.c http/header/to_string.c
PARSER = http/request/parser.c http/request/parser/get_header.c \
http/request/parser/parse.c http/request/parser/get_request_line.c \
http/request/parser/read.c
http/request/parser/read.c http/request/parser/get_body.c
AM_CFLAGS = -Wall -I ../include/
@ -21,6 +22,6 @@ AM_CFLAGS = -Wall -I ../include/
bin_PROGRAMS = testserver
testserver_SOURCES = testserver.c \
$(IFACE) $(CLASS) $(SOCKET) $(SERVER) $(LOGGER) $(REQ) \
$(IFACE) $(CLASS) $(SOCKET) $(SERVER) $(LOGGER) $(MSG) $(REQ) \
$(RESP) $(HEADER) $(PARSER) signalHandling.c daemonize.c
testserver_CFLAGS = -Wall -I ../include/

47
src/http/header/add.c

@ -0,0 +1,47 @@
#include <search.h>
#include <stdlib.h>
#include <ctype.h>
#include <stdio.h>
#include "class.h"
#include "interface/class.h"
#include "http/header.h"
static
inline
unsigned long
sdbm(const unsigned char * str)
{
unsigned long hash = 0;
int c;
while ((c = tolower(*str++)))
hash = c + (hash << 6) + (hash << 16) - hash;
return hash;
}
static
inline
int
comp(const void * _a, const void * _b)
{
HttpHeader a = (HttpHeader)_a;
HttpHeader b = (HttpHeader)_b;
return (a->hash < b->hash)? -1 : (a->hash > b->hash)? 1 : 0;
}
void
httpHeaderAdd(const HttpHeader * root, HttpHeader header)
{
HttpHeader * found = tsearch(header, (void **)root, comp);
if (*found != header) {
puts("uhh, duplicate header set. "
"This is not implemented right now. "
"Keep the first one found.");
delete(&header);
}
}
// vim: set ts=4 sw=4:

61
src/http/message.c

@ -0,0 +1,61 @@
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <search.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <unistd.h>
#include "class.h"
#include "interface/class.h"
#include "http/message.h"
#include "message/helper.c"
static
void
ctor(void * _this, va_list * params)
{
HttpMessage this = _this;
char * version = va_arg(* params, char *);
this->version = calloc(1, strlen(version)+1);
strcpy(this->version, version);
}
static
void
dtor(void * _this)
{
HttpMessage this = _this;
_free((void **)&(this->version));
/**
* this is a GNU extension...anyway on most non
* GNUish systems i would not use tsearch anyway
* as the trees will be unbalanced.
*/
tdestroy(this->header, tDelete);
switch (this->type) {
case HTTP_MESSAGE_BUFFERED:
_free((void **)&((this->body).buffer));
break;
case HTTP_MESSAGE_PIPED:
close((this->body).handle);
break;
default:
break;
}
}
INIT_IFACE(Class, ctor, dtor, NULL);
CREATE_CLASS(HttpMessage, NULL, IFACE(Class));
// vim: set ts=4 sw=4:

30
src/http/message/helper.c

@ -0,0 +1,30 @@
#ifndef __HTTP_MESSAGE_HELPER_C__
#define __HTTP_MESSAGE_HELPER_C__
#include <stdlib.h>
#include "class.h"
#include "interface/class.h"
static
inline
void
_free(void ** data)
{
if (NULL != *data) {
free(*data);
}
}
static
inline
void
tDelete(void * node)
{
delete(&node);
}
#endif // __HTTP_MESSAGE_HELPER_C__
// vim: set ts=4 sw=4:

36
src/http/request.c

@ -1,35 +1,13 @@
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdlib.h>
#include <stdarg.h>
#include <search.h>
#include "class.h"
#include "interface/class.h"
#include "http/request.h"
#include "message/helper.c"
static
inline
void
_free(void ** data)
{
if (NULL != *data) {
free(*data);
}
}
static
inline
void
tDelete(void * node)
{
delete(&node);
}
static
void
ctor(void * _this, va_list * params) {}
@ -40,21 +18,13 @@ dtor(void * _this)
{
HttpRequest this = _this;
_free((void **)&(this->version));
_free((void **)&(this->uri));
_free((void **)&(this->method));
/**
* this is a GNU extension...anyway on most non
* GNUish systems i would not use tsearch anyway
* as the trees will be unbalanced.
*/
tdestroy(this->header, tDelete);
_free((void **)&(this->body));
PARENTCALL(_this, Class, dtor);
}
INIT_IFACE(Class, ctor, dtor, NULL);
CREATE_CLASS(HttpRequest, NULL, IFACE(Class));
CREATE_CLASS(HttpRequest, HttpMessage, IFACE(Class));
// vim: set ts=4 sw=4:

8
src/http/request/has_keep_alive.c

@ -1,6 +1,7 @@
#include <string.h>
#include <ctype.h>
#include "http/message.h"
#include "http/request.h"
#include "http/header.h"
@ -8,10 +9,11 @@
char
httpRequestHasKeepAlive(HttpRequest request)
{
char * header;
char * header_ptr;
HttpMessage message = (HttpMessage)request;
char * header;
char * header_ptr;
header = httpHeaderGet(&(request->header), "connection");
header = httpHeaderGet(&(message->header), "connection");
if (NULL == header) {
return 0;

38
src/http/request/parser/get_body.c

@ -0,0 +1,38 @@
#include <stdlib.h>
#include "http/header.h"
#include "http/message.h"
#include "http/request/parser.h"
void
httpRequestParserGetBody(HttpRequestParser this)
{
HttpMessage message = (HttpMessage)(this->cur_request);
char * nbody;
if (0 == message->nbody) {
nbody = httpHeaderGet(
&(message->header),
"Content-Length");
if (NULL == nbody) {
this->state = HTTP_REQUEST_DONE;
return;
}
else {
message->type = HTTP_MESSAGE_BUFFERED;
message->nbody = atoi(nbody);
}
}
if (REMAINS(this) >= message->nbody) {
(message->body).buffer = calloc(1, message->nbody + 1);
memcpy((message->body).buffer,
this->cur_data,
message->nbody);
this->cur_data += message->nbody;
this->state = HTTP_REQUEST_DONE;
}
}
// vim: set ts=4 sw=4:

4
src/http/request/parser/get_header.c

@ -5,10 +5,10 @@
#include "class.h"
#include "interface/class.h"
#include "http/header.h"
#include "http/request.h"
#include "http/message.h"
void
httpRequestParserGetHeader(HttpRequest request, char * line)
httpRequestParserGetHeader(HttpMessage request, char * line)
{
char * name = line;
char * value = strchr(line, ':');

5
src/http/request/parser/get_request_line.c

@ -7,6 +7,7 @@
void
httpRequestParserGetRequestLine(HttpRequest request, char * line)
{
HttpMessage message = (HttpMessage)request;
char * method, * uri, * version;
method = line;
@ -23,8 +24,8 @@ httpRequestParserGetRequestLine(HttpRequest request, char * line)
strcpy(request->method, method);
request->uri = malloc(strlen(uri) + 1);
strcpy(request->uri, uri);
request->version = malloc(strlen(version) + 1);
strcpy(request->version, method);
message->version = malloc(strlen(version) + 1);
strcpy(message->version, method);
}
// vim: set ts=4 sw=4:

32
src/http/request/parser/parse.c

@ -7,10 +7,6 @@
#include "interface/class.h"
#define REMAINS(pars) \
((pars)->buffer_used - ((pars)->cur_data - (pars)->buffer))
static
inline
char *
@ -79,33 +75,7 @@ httpRequestParserParse(HttpRequestParser this)
break;
case HTTP_REQUEST_HEADERS_DONE:
{
char * nbody;
if (0 == this->cur_request->nbody) {
nbody = httpHeaderGet(
&(this->cur_request->header),
"Content-Length");
if (NULL == nbody) {
this->state = HTTP_REQUEST_DONE;
break;
}
else {
this->cur_request->nbody = atoi(nbody);
}
}
if (REMAINS(this) >= this->cur_request->nbody) {
this->cur_request->body = calloc(1, this->cur_request->nbody + 1);
memcpy(this->cur_request->body,
this->cur_data,
this->cur_request->nbody);
this->cur_data += this->cur_request->nbody;
this->state = HTTP_REQUEST_DONE;
}
}
httpRequestParserGetBody(this);
break;
case HTTP_REQUEST_DONE:

44
src/http/request/parser/read.c

@ -0,0 +1,44 @@
#include <stdlib.h>
#include <unistd.h>
#include "http/request/parser.h"
size_t
httpRequestParserRead(HttpRequestParser this, int fd)
{
size_t remaining, chunks;
char buffer[1024];
ssize_t size = read(fd, buffer, 1024);
if (0 < size) {
remaining = this->buffer_used % HTTP_REQUEST_PARSER_READ_CHUNK;
chunks = this->buffer_used / HTTP_REQUEST_PARSER_READ_CHUNK;
/**
* because a division always rounds down
* chunks holds exactly the currently allocated chunks if
* remaining equals 0 but there is no space left.
* Else chunks holds the actually allocated amount of chunks
* minus 1.
* For this reason chunks always has to be increased by 1.
*/
chunks++;
if (size >= remaining) {
this->buffer =
realloc(this->buffer, chunks * HTTP_REQUEST_PARSER_READ_CHUNK);
}
memcpy(this->buffer + this->buffer_used, buffer, size);
this->buffer_used += size;
this->buffer[this->buffer_used] = 0;
httpRequestParserParse(this);
}
return size;
}
// vim: set ts=4 sw=4:

39
src/http/response.c

@ -1,8 +1,3 @@
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <search.h>
#include <stdlib.h>
#include <stdarg.h>
@ -10,60 +5,38 @@
#include "interface/class.h"
#include "http/response.h"
#include "message/helper.c"
static
void
_free(void ** data)
{
if (NULL != *data) {
free(*data);
}
}
static
void
ctor(void * _this, va_list * params)
{
char * version;
HttpResponse this = _this;
char * status;
char * reason;
HttpResponse this = _this;
PARENTCALL(_this, Class, ctor, params);
version = va_arg(* params, char *);
this->status = va_arg(* params, unsigned int);
reason = va_arg(* params, char *);
this->version = calloc(1, strlen(version)+1);
strcpy(this->version, version);
this->reason = calloc(1, strlen(reason)+1);
strcpy(this->reason, reason);
}
static
inline
void
tDelete(void * node)
{
delete(&node);
}
static
void
dtor(void * _this)
{
HttpResponse this = _this;
_free((void **)&(this->version));
_free((void **)&(this->reason));
tdestroy(this->header, tDelete);
_free((void **)&(this->body));
PARENTCALL(_this, Class, dtor);
}
INIT_IFACE(Class, ctor, dtor, NULL);
CREATE_CLASS(HttpResponse, NULL, IFACE(Class));
CREATE_CLASS(HttpResponse, HttpMessage, IFACE(Class));
// vim: set ts=4 sw=4:

20
src/http/response/404.c

@ -7,6 +7,7 @@
#include "interface/class.h"
#include "http/response.h"
#include "http/message.h"
#include "http/header.h"
@ -26,26 +27,29 @@ httpResponse404()
struct tm * tmp;
char buffer[200];
HttpResponse response;
HttpMessage message;
response = new(HttpResponse, "HTTP/1.1", 404, "Not Found");
message = (HttpMessage)response;
httpHeaderAdd(&(response->header),
httpHeaderAdd(&(message->header),
new(HttpHeader, "Content-Type", "text/html"));
httpHeaderAdd(&(response->header),
httpHeaderAdd(&(message->header),
new(HttpHeader, "Server", "testserver"));
response->nbody = sizeof(RESP_DATA) - 1;
response->body = calloc(1, sizeof(RESP_DATA));
strcpy(response->body, RESP_DATA);
message->type = HTTP_MESSAGE_BUFFERED;
message->nbody = sizeof(RESP_DATA) - 1;
(message->body).buffer = calloc(1, sizeof(RESP_DATA));
strcpy((message->body).buffer, RESP_DATA);
sprintf(buffer, "%d", response->nbody);
httpHeaderAdd(&(response->header),
sprintf(buffer, "%d", message->nbody);
httpHeaderAdd(&(message->header),
new(HttpHeader, "Content-Length", buffer));
t = time(NULL);
tmp = localtime(&t);
strftime(buffer, sizeof(buffer), "%a, %d %b %Y %T %Z", tmp);
httpHeaderAdd(&(response->header),
httpHeaderAdd(&(message->header),
new(HttpHeader, "Date", buffer));
return response;

12
src/http/response/size_get.c

@ -2,6 +2,7 @@
#include <string.h>
#include <sys/types.h>
#include "http/message.h"
#include "http/response.h"
#include "http/header.h"
@ -20,16 +21,21 @@ addHeaderSize(const void * node, const VISIT which, const int depth)
size_t
httpResponseSizeGet(HttpResponse response)
{
HttpMessage message = (HttpMessage)response;
size = 0;
size += strlen(response->version) + 1;
size += strlen(message->version) + 1;
size += 4; // for status
size += strlen(response->reason) + 2;
twalk(response->header, addHeaderSize);
twalk(message->header, addHeaderSize);
size += 2;
size += response->nbody;
if (HTTP_MESSAGE_BUFFERED == message->type) {
size += message->nbody;
}
return size;
}

11
src/http/response/to_string.c

@ -20,13 +20,14 @@ addHeaderString(const void * node, const VISIT which, const int depth)
char *
httpResponseToString(HttpResponse response, char * _string)
{
char status[4];
HttpMessage message = (HttpMessage)response;
char status[4];
string = _string;
snprintf(status, 4, "%d", response->status);
strcpy(string, response->version);
strcpy(string, message->version);
string += strlen(string);
*string++ = ' ';
@ -42,12 +43,14 @@ httpResponseToString(HttpResponse response, char * _string)
*string++ = '\r';
*string++ = '\n';
twalk(response->header, addHeaderString);
twalk(message->header, addHeaderString);
*string++ = '\r';
*string++ = '\n';
memcpy(string, response->body, response->nbody);
if (HTTP_MESSAGE_BUFFERED == message->type) {
memcpy(string, (message->body).buffer, message->nbody);
}
return string;
}

4
src/server/run.c

@ -117,13 +117,13 @@ serverRun(Server this)
if (httpRequestHasKeepAlive(queue->requests[j])) {
(this->conns)[fd].keep_alive = 1;
httpHeaderAdd(
&(response->header),
&(((HttpMessage)response)->header),
new(HttpHeader, "Connection", "Keep-Alive"));
}
else {
(this->conns)[fd].keep_alive = 0;
httpHeaderAdd(
&(response->header),
&(((HttpMessage)response)->header),
new(HttpHeader, "Connection", "Close"));
}

333
tests/tst-tsearch.c

@ -0,0 +1,333 @@
/* Test program for tsearch et al.
Copyright (C) 1997, 2000, 2001 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */
#ifndef _GNU_SOURCE
# define _GNU_SOURCE 1
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <search.h>
#define SEED 0
#define BALANCED 1
#define PASSES 100
#if BALANCED
#include <math.h>
#define SIZE 1000
#else
#define SIZE 100
#endif
enum order
{
ascending,
descending,
randomorder
};
enum action
{
build,
build_and_del,
delete,
find
};
/* Set to 1 if a test is flunked. */
static int error = 0;
/* The keys we add to the tree. */
static int x[SIZE];
/* Pointers into the key array, possibly permutated, to define an order
for insertion/removal. */
static int y[SIZE];
/* Flags set for each element visited during a tree walk. */
static int z[SIZE];
/* Depths for all the elements, to check that the depth is constant for
all three visits. */
static int depths[SIZE];
/* Maximum depth during a tree walk. */
static int max_depth;
/* Compare two keys. */
static int
cmp_fn (const void *a, const void *b)
{
return *(const int *) a - *(const int *) b;
}
/* Permute an array of integers. */
static void
memfry (int *string)
{
int i;
for (i = 0; i < SIZE; ++i)
{
int32_t j;
int c;
j = random () % SIZE;
c = string[i];
string[i] = string[j];
string[j] = c;
}
}
static void
walk_action (const void *nodep, const VISIT which, const int depth)
{
int key = **(int **) nodep;
if (depth > max_depth)
max_depth = depth;
if (which == leaf || which == preorder)
{
++z[key];
depths[key] = depth;
}
else
{
if (depths[key] != depth)
{
fputs ("Depth for one element is not constant during tree walk.\n",
stdout);
}
}
}
static void
walk_tree (void *root, int expected_count)
{
int i;
memset (z, 0, sizeof z);
max_depth = 0;
twalk (root, walk_action);
for (i = 0; i < expected_count; ++i)
if (z[i] != 1)
{
fputs ("Node was not visited.\n", stdout);
error = 1;
}
#if BALANCED
if (max_depth > log (expected_count) * 2 + 2)
#else
if (max_depth > expected_count)
#endif
{
fputs ("Depth too large during tree walk.\n", stdout);
error = 1;
}
}
/* Perform an operation on a tree. */
static void
mangle_tree (enum order how, enum action what, void **root, int lag)
{
int i;
if (how == randomorder)
{
for (i = 0; i < SIZE; ++i)
y[i] = i;
memfry (y);
}
for (i = 0; i < SIZE + lag; ++i)
{
void *elem;
int j, k;
switch (how)
{
case randomorder:
if (i >= lag)
k = y[i - lag];
else
/* Ensure that the array index is within bounds. */
k = y[(SIZE - i - 1 + lag) % SIZE];
j = y[i % SIZE];
break;
case ascending:
k = i - lag;
j = i;
break;
case descending:
k = SIZE - i - 1 + lag;
j = SIZE - i - 1;
break;
default:
/* This never should happen, but gcc isn't smart enough to
recognize it. */
abort ();
}
switch (what)
{
case build_and_del:
case build:
if (i < SIZE)
{
if (tfind (x + j, (void *const *) root, cmp_fn) != NULL)
{
fputs ("Found element which is not in tree yet.\n", stdout);
error = 1;
}
elem = tsearch (x + j, root, cmp_fn);
if (elem == 0
|| tfind (x + j, (void *const *) root, cmp_fn) == NULL)
{
fputs ("Couldn't find element after it was added.\n",
stdout);
error = 1;
}
}
if (what == build || i < lag)
break;
j = k;
/* fall through */
case delete:
elem = tfind (x + j, (void *const *) root, cmp_fn);
if (elem == NULL || tdelete (x + j, root, cmp_fn) == NULL)
{
fputs ("Error deleting element.\n", stdout);
error = 1;
}
break;
case find:
if (tfind (x + j, (void *const *) root, cmp_fn) == NULL)
{
fputs ("Couldn't find element after it was added.\n", stdout);
error = 1;
}
break;
}
}
}
int
main (int argc, char **argv)
{
int total_error = 0;
static char state[8] = { 1, 2, 3, 4, 5, 6, 7, 8 };
void *root = NULL;
int i, j;
initstate (SEED, state, 8);
for (i = 0; i < SIZE; ++i)
x[i] = i;
/* Do this loop several times to get different permutations for the
random case. */
fputs ("Series I\n", stdout);
for (i = 0; i < PASSES; ++i)
{
fprintf (stdout, "Pass %d... ", i + 1);
fflush (stdout);
error = 0;
mangle_tree (ascending, build, &root, 0);
mangle_tree (ascending, find, &root, 0);
mangle_tree (descending, find, &root, 0);
mangle_tree (randomorder, find, &root, 0);
walk_tree (root, SIZE);
mangle_tree (ascending, delete, &root, 0);
mangle_tree (ascending, build, &root, 0);
walk_tree (root, SIZE);
mangle_tree (descending, delete, &root, 0);
mangle_tree (ascending, build, &root, 0);
walk_tree (root, SIZE);
mangle_tree (randomorder, delete, &root, 0);
mangle_tree (descending, build, &root, 0);
mangle_tree (ascending, find, &root, 0);
mangle_tree (descending, find, &root, 0);
mangle_tree (randomorder, find, &root, 0);
walk_tree (root, SIZE);
mangle_tree (descending, delete, &root, 0);
mangle_tree (descending, build, &root, 0);
walk_tree (root, SIZE);
mangle_tree (descending, delete, &root, 0);
mangle_tree (descending, build, &root, 0);
walk_tree (root, SIZE);
mangle_tree (randomorder, delete, &root, 0);
mangle_tree (randomorder, build, &root, 0);
mangle_tree (ascending, find, &root, 0);
mangle_tree (descending, find, &root, 0);
mangle_tree (randomorder, find, &root, 0);
walk_tree (root, SIZE);
mangle_tree (randomorder, delete, &root, 0);
for (j = 1; j < SIZE; j *= 2)
{
mangle_tree (randomorder, build_and_del, &root, j);
}
fputs (error ? " failed!\n" : " ok.\n", stdout);
total_error |= error;
}
fputs ("Series II\n", stdout);
for (i = 1; i < SIZE; i *= 2)
{
fprintf (stdout, "For size %d... ", i);
fflush (stdout);
error = 0;
mangle_tree (ascending, build_and_del, &root, i);
mangle_tree (descending, build_and_del, &root, i);
mangle_tree (ascending, build_and_del, &root, i);
mangle_tree (descending, build_and_del, &root, i);
mangle_tree (ascending, build_and_del, &root, i);
mangle_tree (descending, build_and_del, &root, i);
mangle_tree (ascending, build_and_del, &root, i);
mangle_tree (descending, build_and_del, &root, i);
fputs (error ? " failed!\n" : " ok.\n", stdout);
total_error |= error;
}
return total_error;
}
Loading…
Cancel
Save