Skip to content

Instantly share code, notes, and snippets.

@dougm
Created June 4, 2009 00:40
Show Gist options
  • Save dougm/123360 to your computer and use it in GitHub Desktop.
Save dougm/123360 to your computer and use it in GitHub Desktop.
/*
* tinkering w/ the idea of a couchdb C api, shelved in favor of http://bit.ly/ccprC
* but the jist is to use map keys to lookup structure offsets.
* did not give any thought to POST, PUT, etc.
* gcc -Wall -g -o ycouchdb ycouchdb.c -I/usr/local/include/yajl -L/usr/local/lib -lyajl
* curl -s http://localhost:5984/_stats/httpd/requests | ./ycouchdb /_stats/httpd/requests
*/
#include <yajl/yajl_parse.h>
#include <yajl/yajl_gen.h>
#define _GNU_SOURCE /* strndup */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define couch_offsetof(type, field) ((size_t)(&((type *)0)->field))
#define couch_map_ent(s, n) { #n, couch_offsetof(s, n) }
typedef struct {
char *ptr;
size_t offset;
} couch_map_ent_t;
typedef struct {
long doc_count;
long doc_del_count;
long update_seq;
long disk_size;
} database_t;
static couch_map_ent_t database_stats[] = {
couch_map_ent(database_t, doc_count),
couch_map_ent(database_t, doc_del_count),
couch_map_ent(database_t, update_seq),
couch_map_ent(database_t, disk_size),
{ NULL }
};
typedef struct {
long current;
long max;
long min;
long count;
double mean;
double stddev;
} httpd_requests_t;
static couch_map_ent_t httpd_requests[] = {
couch_map_ent(httpd_requests_t, current),
couch_map_ent(httpd_requests_t, max),
couch_map_ent(httpd_requests_t, min),
couch_map_ent(httpd_requests_t, count),
couch_map_ent(httpd_requests_t, mean),
couch_map_ent(httpd_requests_t, stddev),
{ NULL }
};
typedef struct {
int num;
int size;
char **values;
} couch_array_t;
typedef struct {
enum {
LIST,
HTTPD_REQUESTS,
DATABASE
} type;
union {
couch_array_t dbs;
database_t db;
httpd_requests_t hr;
} data;
couch_map_ent_t *map;
couch_map_ent_t cur;
} couchdb_t;
static int couchcb_null(void *ctx)
{
return 1;
}
static int couchcb_boolean(void *ctx, int boolean)
{
return 1;
}
static int couchcb_integer(void *ctx, long val)
{
couchdb_t *couch = (couchdb_t *)ctx;
if (couch->cur.offset >= 0) {
*(long *)(couch->cur.ptr + couch->cur.offset) = val;
}
return 1;
}
static int couchcb_double(void *ctx, double val)
{
couchdb_t *couch = (couchdb_t *)ctx;
if (couch->cur.offset >= 0) {
*(double *)(couch->cur.ptr + couch->cur.offset) = val;
}
return 1;
}
static int couchcb_string(void *ctx, const unsigned char *val,
unsigned int len)
{
couchdb_t *couch = (couchdb_t *)ctx;
if (couch->cur.offset == LIST) {
couch_array_t *list = (couch_array_t *)couch->cur.ptr;
char *copy = strndup((const char *)val, len);
if (list->num >= list->size) {
list->size += 2;
list->values =
realloc(list->values, list->size * sizeof(list->values[0]));
}
list->values[list->num++] = copy;
}
return 1;
}
static int couchcb_map_key(void *ctx, const unsigned char *key,
unsigned int len)
{
couchdb_t *couch = (couchdb_t *)ctx;
int i;
couch->cur.offset = -1;
if (couch->map == NULL) {
return 1;
}
for (i=0; couch->map[i].ptr != NULL; i++) {
if (strncmp((char *)key, couch->map[i].ptr, len) == 0) {
couch->cur.offset = couch->map[i].offset;
return 1;
}
}
return 1;
}
static int couchcb_start_map(void *ctx)
{
return 1;
}
static int couchcb_end_map(void *ctx)
{
return 1;
}
static int couchcb_start_array(void *ctx)
{
return 1;
}
static int couchcb_end_array(void *ctx)
{
return 1;
}
static yajl_callbacks callbacks = {
couchcb_null,
couchcb_boolean,
couchcb_integer,
couchcb_double,
NULL,
couchcb_string,
couchcb_start_map,
couchcb_map_key,
couchcb_end_map,
couchcb_start_array,
couchcb_end_array
};
int main(int argc, char ** argv)
{
yajl_handle hand;
unsigned char buffer[65536];
yajl_status stat;
size_t rd;
int done = 0;
couchdb_t couch;
char *path = argv[1];
if (strcmp(path, "/chef") == 0) {
couch.type = DATABASE;
couch.map = &database_stats[0];
memset(&couch.data.db, '\0', sizeof(couch.data.db));
couch.cur.ptr = (char *)&couch.data.db;
}
else if (strcmp(path, "/_stats/httpd/requests") == 0) {
couch.type = HTTPD_REQUESTS;
couch.map = &httpd_requests[0];
memset(&couch.data.hr, '\0', sizeof(couch.data.hr));
couch.cur.ptr = (char *)&couch.data.hr;
}
else if (strcmp(path, "/_all_dbs") == 0) {
couch.type = LIST;
couch.data.dbs.num = 0;
couch.data.dbs.size = 2;
couch.data.dbs.values =
malloc(couch.data.dbs.size * sizeof(couch.data.dbs.values[0]));
couch.map = NULL;
couch.cur.ptr = (char *)&couch.data.dbs;
couch.cur.offset = couch.type;
}
else {
printf("fail\n");
exit(0);
}
hand = yajl_alloc(&callbacks, NULL, NULL, (void *)&couch);
while (!done) {
rd = fread((void *)buffer, 1, sizeof(buffer)-1, stdin);
if (rd == 0) {
done = 1;
}
buffer[rd] = 0;
if (done)
stat = yajl_parse_complete(hand); /* flush */
else
stat = yajl_parse(hand, buffer, rd);
if (stat != yajl_status_ok &&
stat != yajl_status_insufficient_data)
{
unsigned char *str = yajl_get_error(hand, 1, buffer, rd);
fprintf(stderr, (const char *) str);
yajl_free_error(hand, str);
}
}
if (couch.type == DATABASE) {
printf("doc_count:%ld,doc_del_count:%ld,update_seq:%ld,disk_size:%ld\n",
couch.data.db.doc_count,
couch.data.db.doc_del_count,
couch.data.db.update_seq,
couch.data.db.disk_size);
}
else if (couch.type == HTTPD_REQUESTS) {
printf("current:%ld,max:%ld,min:%ld,count:%ld\n",
couch.data.hr.current,
couch.data.hr.max,
couch.data.hr.min,
couch.data.hr.count);
}
else if (couch.type == LIST) {
int i;
for (i=0; i<couch.data.dbs.num; i++) {
printf("%s\n", couch.data.dbs.values[i]);
}
}
yajl_free(hand);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment