stub functions and documentation
authorTero Marttila <terom@fixme.fi>
Thu, 28 Aug 2008 01:34:14 +0300
changeset 44 03a7e064f833
parent 43 e5b714190dee
child 45 10d514029c64
stub functions and documentation
memcache.h
memcache/connection.c
memcache/memcache.c
memcache/request.c
memcache/request.h
memcache_test.c
--- a/memcache.h	Thu Aug 28 00:29:39 2008 +0300
+++ b/memcache.h	Thu Aug 28 01:34:14 2008 +0300
@@ -21,7 +21,24 @@
  * Keys used
  */
 struct memcache_key {
+    /*
+     * Pointer to a char buffer containing the key itself.
+     *
+     * A key is a text tring which should uniquely identify a cache entry.
+     *
+     * The length limit for a key is usually 250 characters, but this is imposed by the server side (a longer key will
+     * presumeably cause a CLIENT_ERROR reply).
+     *
+     * The key should presumeably also not contain any spaces, carriage returns or newlines, as these are used to
+     * delimit tokens in the protocol itself.
+     *
+     * The buffer does not need to be zero-terminated.
+     */
     char *buf;
+
+    /*
+     * The length of the key buffer in bytes. This does not include a NUL byte.
+     */
     size_t len;
 };
 
@@ -29,9 +46,27 @@
  * Object attributes
  */
 struct memcache_obj {
+    /*
+     * An arbitrary 16-bit (32-bit for >1.2.1) that the server stores transparently.
+     */
     unsigned int flags;
+
+    /*
+     * Expiration time. If non-zero, either an offset in seconds from current time (up to 60*60*24*30 seconds, or 30d),
+     * or an absolute 32-bit unix timestamp.
+     */
     time_t exptime;
+
+    /*
+     * Number of bytes in the entry. This may be zero, in which case there will be no data.
+     */
     size_t bytes;
+
+    /*
+     * Used for the CMD_STORE_CAS command. An unique 64-bit value that changes if the entry is modified.
+     *
+     * Not needed for other commands, and may or may not be provided by CMD_FETCH_GET (will be set to zero).
+     */
     unsigned long long cas;
 };
 
@@ -39,8 +74,21 @@
  * Object data
  */
 struct memcache_buf {
+    /*
+     * The char buffer containing the data.
+     */
     char *data;
+
+    /*
+     * The total length of the char buffer, and thence the cache entry.
+     */
     size_t len;
+
+    /*
+     * The amount of data currently available in the buffer. This is used to provide streaming fetches.
+     *
+     * This field is *IMPORTANT*! Don't disregard it, or you *will* get incomplete data.
+     */
     size_t offset;
 };
 
@@ -49,33 +97,124 @@
  */
 enum memcache_command {
     MEMCACHE_CMD_INVALID,
+    
+    /*
+     * Retrieve the value of a key from the memcached server. 
+     *
+     * If the key exists, you will get a RPL_VALUE reply followed by a RPL_END reply. 
+     * If it does not exist, you will just get a RPL_END reply.
+     */
+    MEMCACHE_CMD_FETCH_GET,
 
-    MEMCACHE_CMD_FETCH_GET,
+    /*
+     * Store this data.
+     *
+     * Returns either RPL_STORED or RPL_NOT_STORED.
+     */
     MEMCACHE_CMD_STORE_SET,
+
+    /*
+     * Store this data, but only if the server doesn't already hold data for this key.
+     *
+     * Returns either RPL_STORED or RPL_NOT_STORED.
+     */
     MEMCACHE_CMD_STORE_ADD,
+
+    /*
+     * Store this data, but only if the server does already hold data for this key.
+     *
+     * Returns either RPL_STORED or RPL_NOT_STORED.
+     */
     MEMCACHE_CMD_STORE_REPLACE,
+
+    /*
+     * Add this data to an existing key after existing data.
+     *
+     * obj.flags and obj.exptime are ignored.
+     *
+     * Returns ???
+     */
     MEMCACHE_CMD_STORE_APPEND,
+
+    /*
+     * Add this data to an existing key before existing data.
+     *
+     * obj.flags and obj.exptime are ignored.
+     *
+     * Returns ???
+     */
     MEMCACHE_CMD_STORE_PREPEND,
+
+    /*
+     * Check and Set - store this data but only if no one else had updated it since I last fetched it.
+     *
+     * obj.cas is required.
+     *
+     * Returns RPL_STORED, RPL_NOT_STORED, RPL_EXISTS (data has been modified), or RPL_NOT_FOUND (the item does not
+     * exist or has been deleted).
+     */
     MEMCACHE_CMD_STORE_CAS,
 
     MEMCACHE_CMD_MAX,
 };
 
+/*
+ * Replies from the server
+ */
 enum memcache_reply {
     MEMCACHE_RPL_INVALID,
+    
+    /*
+     * The library sent an unknown command. This probably means the server is not compatible with said command.
+     */
+    MEMCACHE_RPL_ERROR,
 
-    MEMCACHE_RPL_ERROR,
+    /*
+     * There was an error in the request that the library sent. The error message is printed out via ERROR.
+     */
     MEMCACHE_RPL_CLIENT_ERROR,
+
+    /*
+     * There was an error with the server when processing the request. The error message is printed out via ERROR.
+     */
     MEMCACHE_RPL_SERVER_ERROR,
     
-    // MEMCACHE_CMD_FETCH_*
+    // CMD_FETCH_*
+
+    /*
+     * The given key was found in the cache. The obj attributes are now known, and the buf data is on the way.
+     */
     MEMCACHE_RPL_VALUE,
+
+    /*
+     * No more RPL_VALUE replies will be sent. This may be the only reply sent if the key was not found.
+     */
     MEMCACHE_RPL_END,
     
-    // MEMCACHE_CMD_STORE_*
+    // CMD_STORE_*
+
+    /*
+     * The object was succesfully stored in the cache.
+     */
     MEMCACHE_RPL_STORED,
+
+    /*
+     * The object was not stored in the cache.
+     *
+     * This could be for a number of reasons, perhaps the object is too large, the cache is full, the key is in the
+     * delete queue, or some condition imposed by the STORE_* command was not met.
+     */
     MEMCACHE_RPL_NOT_STORED,
+
+    /*
+     * The item you were trying to store with a STORE_CAS request has been modified since you last fetched it (i.e.
+     * obj.cas does not match).
+     */
     MEMCACHE_RPL_EXISTS,
+
+    /*
+     * The item you were trying to store with a STORE_CAS request did not exist (or has been deleted).
+     */
     MEMCACHE_RPL_NOT_FOUND,
 
     MEMCACHE_RPL_MAX,
@@ -83,15 +222,50 @@
 
 enum memcache_req_state {
     MEMCACHE_STATE_INVALID,
+    
+    /*
+     * The request is queued, and has not been sent yet
+     */
+    MEMCACHE_STATE_QUEUED,
 
-    MEMCACHE_STATE_QUEUED,
+    /*
+     * The request is being sent, and the reply has not yet been received
+     */
     MEMCACHE_STATE_SEND,
+
+    /*
+     * The reply has been received.
+     *
+     * req_reply and req_obj will return a non-NULL value, but there is no reply data yet.
+     */
     MEMCACHE_STATE_REPLY,
-    MEMCACHE_STATE_REPLY_DATA,
 
+    /*
+     * The reply and part of the reply data has been received.
+     *
+     * req_reply, req_obj and req_buf will all return non-NULL values, but buf.offset will be smaller than buf.len.
+     */
+    MEMCACHE_STATE_REPLY_DATA,
+    
+    /*
+     * The full reply has been received.
+     *
+     * req_reply, req_obj will return a non-NULL value, but there is no reply data.
+     */
     MEMCACHE_STATE_DONE,
+
+    /*
+     * The full reply and reply data has been received.
+     *
+     * req_reply, req_obj and req_buf will all return non-NULL values, and buf.offset will be equal to buf.len.
+     */
     MEMCACHE_STATE_DATA_DONE,
-
+    
+    /*
+     * An error has occurred.
+     *
+     * req_reply, req_obj and req_buf may or may not work.
+     */
     MEMCACHE_STATE_ERROR,
 };
 
@@ -112,27 +286,94 @@
 
 /*
  * Attempt to fetch a key from the cache.
+ *
+ * The state machine will work as follows:
+ *
+ *  req_state                  multi   req_reply
+ *  ---------------------------------------------
+ *  STATE_QUEUE                 ?
+ *  STATE_SEND
+ *  STATE_REPLY                         RPL_VALUE
+ *      STATE_REPLY_DATA        *       RPL_VALUE
+ *      STATE_DATA_DONE                 RPL_END
+ *
+ *      STATE_DONE                      RPL_END
+ *
+ *  STATE_ERROR                         RPL_{ERROR,CLIENT_ERROR,SERVER_ERROR}
+ *
+ * The item attributes/data can be accessed via req_obj/req_buf as described in `enum memcache_state`.
  */
 struct memcache_req *memcache_fetch (struct memcache *mc, const struct memcache_key *key, void *cb_arg);
 
 /*
- * Attempt to store a key into the cache
+ * Attempt to store an item into the cache.
+ *
+ * The cmd argument can be used to specify what CMD_STORE_* command to use.
+ *
+ * The given memcache_key is copied, including the char array pointed to by buf.
+ * Both obj and buf are also copied, but buf.data will not be copied - the pointer must remain valid until the request is done.
+ *
+ * The state machine will work as follows:
+ *
+ *  req_state               multi       req_reply
+ *  ---------------------------------------------
+ *  STATE_QUEUE             ?
+ *  STATE_SEND
+ *  STATE_REPLY                         RPL_{STORED,NOT_STORED,EXISTS,NOT_FOUND}
+ *  STATE_DONE                          RPL_{STORED,NOT_STORED,EXISTS,NOT_FOUND}
+ * 
+ *  STATE_ERROR                         RPL_{ERROR,CLIENT_ERROR,SERVER_ERROR}
+ *
  */
-struct memcache_req *memcache_store (struct memcache *mc, enum memcache_command cmd, const struct memcache_key *key, const struct memcache_obj *obj, void *cb_arg);
+struct memcache_req *memcache_store (struct memcache *mc, enum memcache_command cmd, const struct memcache_key *key, const struct memcache_obj *obj, const struct memcache_buf *buf, void *cb_arg);
 
 /*
- * Request state
+ * Request state.
+ *
+ * Should always return a valid value.
  */ 
 enum memcache_req_state memcache_req_state (struct memcache_req *req);
 
 /*
- * Request key
+ * Request command.
+ *
+ * Should always return a valid value.
  */
-int memcache_req_key (struct memcache_req *req, const struct memcache_key *key);
+enum memcache_command memcache_req_cmd (struct memcache_req *req);
 
 /*
- * Request data
+ * Request reply.
+ *
+ * Will return a valid value in the STATE_REPLY, STATE_REPLY_DATA, STATE_DONE and STATE_DATA_DONE states.
  */
-int memcache_req_obj (struct memcache_req *req, const struct memcache_obj *obj);
+enum memcache_reply memcache_req_reply (struct memcache_req *req);
+
+/*
+ * Request key.
+ *
+ * Will return a valid valuein all states
+ */
+const struct memcache_key *keymemcache_req_key (struct memcache_req *req);
+
+/*
+ * Request data.
+ *
+ * Will return a valid value in the STATE_REPLY, STATE_REPLY_DATA, STATE_DONE and STATE_DATA_DONE states.
+ */
+const struct memcache_obj *memcache_req_obj (struct memcache_req *req);
+
+/*
+ * Request buf.
+ *
+ * Will return a valid value in the STATE_REPLY_DATA and STATE_DATA_DONE states.
+ *
+ * Note that buf.offset may be less than buf.len in the STATE_REPLY_DATA state.
+ */
+const struct memcache_buf *memcache_req_buf (struct memcache_req *req);
+
+/*
+ * Free a req that is in the STATE_DONE, STATE_DATA_DONE or STATE_ERROR state.
+ */
+void memcache_req_free (struct memcache_req *req);
 
 #endif /* MEMCACHE_H */
--- a/memcache/connection.c	Thu Aug 28 00:29:39 2008 +0300
+++ b/memcache/connection.c	Thu Aug 28 01:34:14 2008 +0300
@@ -298,7 +298,7 @@
             ERROR("got reply with wrong key !?!");
 
         // notify the request (no reply data is ready for reading yet, though)
-        memcache_req_reply(conn->req, reply_type);
+        memcache_req_recv(conn->req, reply_type);
         
         // does the reply include data?
         if (has_data) {
--- a/memcache/memcache.c	Thu Aug 28 00:29:39 2008 +0300
+++ b/memcache/memcache.c	Thu Aug 28 01:34:14 2008 +0300
@@ -50,12 +50,12 @@
     return mc->server_list.lh_first;
 }
 
-struct memcache_req *memcache_fetch (struct memcache *mc, const struct memcache_key *key, void *cb_arg) {
+static struct memcache_req *_memcache_req (struct memcache *mc, enum memcache_command cmd, const struct memcache_key *key, const struct memcache_obj *obj, const struct memcache_buf *buf, void *cb_arg) {
     struct memcache_req *req = NULL;
     struct memcache_server *server = NULL;
     
     // alloc the request
-    if ((req = memcache_req_alloc(mc, MEMCACHE_CMD_FETCH_GET, key, cb_arg)) == NULL)
+    if ((req = memcache_req_alloc(mc, MEMCACHE_CMD_FETCH_GET, key, obj, buf, cb_arg)) == NULL)
         ERROR("failed to allocate request");
 
     // pick a server
@@ -76,3 +76,24 @@
     return NULL;
 }  
 
+struct memcache_req *memcache_fetch (struct memcache *mc, const struct memcache_key *key, void *cb_arg) {
+    return _memcache_req(mc, MEMCACHE_CMD_FETCH_GET, key, NULL, NULL, cb_arg);
+}
+
+struct memcache_req *memcache_store (struct memcache *mc, enum memcache_command cmd, const struct memcache_key *key, const struct memcache_obj *obj, const struct memcache_buf *buf, void *cb_arg) {
+    if (
+            (cmd != MEMCACHE_CMD_STORE_SET)
+        &&  (cmd != MEMCACHE_CMD_STORE_ADD)
+        &&  (cmd != MEMCACHE_CMD_STORE_REPLACE)
+        &&  (cmd != MEMCACHE_CMD_STORE_APPEND)
+        &&  (cmd != MEMCACHE_CMD_STORE_PREPEND)
+        &&  (cmd != MEMCACHE_CMD_STORE_CAS)
+    )
+        ERROR("invalid command for store");
+
+    return _memcache_req(mc, cmd, key, obj, buf, cb_arg);
+
+error:
+    return NULL;
+}
+
--- a/memcache/request.c	Thu Aug 28 00:29:39 2008 +0300
+++ b/memcache/request.c	Thu Aug 28 01:34:14 2008 +0300
@@ -6,8 +6,11 @@
 #include "memcache.h"
 #include "../common.h"
 
-struct memcache_req *memcache_req_alloc (struct memcache *mc, enum memcache_command cmd_type, const struct memcache_key *key, void *cb_arg) {
+struct memcache_req *memcache_req_alloc (struct memcache *mc, enum memcache_command cmd_type, const struct memcache_key *key, const struct memcache_obj *obj, const struct memcache_buf *buf, void *cb_arg) {
     struct memcache_req *req = NULL;
+
+    // ensure key is provided
+    assert(key != NULL);
     
     // allocate it
     if ((req = calloc(1, sizeof(*req))) == NULL)
@@ -20,9 +23,20 @@
     if ((req->key.buf = malloc(key->len)) == NULL)
         ERROR("malloc key buf");
     
-    // copy over the key
     memcpy(req->key.buf, key->buf, key->len);
     req->key.len = key->len;
+    
+    // copy the obj if provided
+    if (obj) {
+        memcpy(&req->obj, obj, sizeof(req->obj));
+        req->have_obj = 1;
+    }
+
+    // copy the buf if provided
+    if (buf) {
+        memcpy(&req->buf, buf, sizeof(req->buf));
+        req->have_buf = 1;
+    }
 
     // store the other data
     req->mc = mc;
@@ -41,6 +55,32 @@
     return NULL;
 }
 
+// accessors
+enum memcache_req_state memcache_req_state (struct memcache_req *req) {
+    return req->state;
+}
+
+enum memcache_command memcache_req_cmd (struct memcache_req *req) {
+    return req->cmd_type;
+}
+
+enum memcache_reply memcache_req_reply (struct memcache_req *req) {
+    return req->reply_type;
+}
+
+const struct memcache_key *keymemcache_req_key (struct memcache_req *req) {
+    return &req->key;
+}
+
+const struct memcache_obj *memcache_req_obj (struct memcache_req *req) {
+    return req->have_obj ? &req->obj : NULL;
+}
+
+const struct memcache_buf *memcache_req_buf (struct memcache_req *req) {
+    return req->have_buf ? &req->buf : NULL;
+}
+
+// events
 static void _memcache_req_notify (struct memcache_req *req) {
     req->mc->cb_fn(req, req->cb_arg);
 }
@@ -57,10 +97,13 @@
 //    _memcache_req_notify(req);
 }
 
-void memcache_req_reply (struct memcache_req *req, enum memcache_reply reply_type) {
+void memcache_req_recv (struct memcache_req *req, enum memcache_reply reply_type) {
     req->state = MEMCACHE_STATE_REPLY;
     req->reply_type = reply_type;
 
+    // we must surely have a valid obj now
+    req->have_obj = 1;
+
     _memcache_req_notify(req);
 }
 
@@ -68,6 +111,9 @@
     assert(req->state == MEMCACHE_STATE_REPLY || req->state == MEMCACHE_STATE_REPLY_DATA);
 
     req->state = MEMCACHE_STATE_REPLY_DATA;
+
+    // we must surely have a valid buf now
+    req->have_buf = 1;
     
     _memcache_req_notify(req);
 }
@@ -75,15 +121,27 @@
 void memcache_req_done (struct memcache_req *req) {
     // make sure we are in the REPLY/REPLY_DATA state
     assert(req->state == MEMCACHE_STATE_REPLY || req->state == MEMCACHE_STATE_REPLY_DATA);
+    
+    // are we supposed to have data?
+    if (req->buf.data) {
+        // make sure we really have the full data, if applicable
+        assert(req->buf.offset == req->buf.len);
+        
+        // yes...
+        req->have_buf = 1;
 
-    // make sure we really have the full data, if applicable
-    assert(req->buf.data == NULL || req->buf.offset == req->buf.len);
+        // have data
+        req->state = MEMCACHE_STATE_DATA_DONE;
+
+    } else {
+        // no data
+        req->state = MEMCACHE_STATE_DONE;
+    }
 
     // forget the connection
     req->conn = NULL;
     
-    // state depends on if we have data or not...
-    req->state = req->buf.data ? MEMCACHE_STATE_DATA_DONE : MEMCACHE_STATE_DONE;
+    _memcache_req_notify(req);
 }
 
 void memcache_req_error (struct memcache_req *req) {
--- a/memcache/request.h	Thu Aug 28 00:29:39 2008 +0300
+++ b/memcache/request.h	Thu Aug 28 01:34:14 2008 +0300
@@ -21,6 +21,9 @@
     struct memcache_obj obj;
     struct memcache_buf buf;
 
+    // flags to indicate if we have valid obj/buf data
+    char have_obj, have_buf;
+
     // our state
     enum memcache_req_state state;
 
@@ -37,7 +40,7 @@
 /*
  * Allocate and return a new req, or NULL if unsuccesfull.
  */
-struct memcache_req *memcache_req_alloc (struct memcache *mc, enum memcache_command cmd_type, const struct memcache_key *key, void *cb_arg);
+struct memcache_req *memcache_req_alloc (struct memcache *mc, enum memcache_command cmd_type, const struct memcache_key *key, const struct memcache_obj *obj, const struct memcache_buf *buf, void *cb_arg);
 
 /*
  * The request has been queued.
@@ -57,7 +60,7 @@
  *
  * Note that no req data will not be available when this is first called, only once req_data/req_done is called.
  */
-void memcache_req_reply (struct memcache_req *req, enum memcache_reply reply_type);
+void memcache_req_recv (struct memcache_req *req, enum memcache_reply reply_type);
 
 /*
  * Some amount of reply data has been received. This may be called between the req_reply/MEMCACHE_RPL_VALUE and
--- a/memcache_test.c	Thu Aug 28 00:29:39 2008 +0300
+++ b/memcache_test.c	Thu Aug 28 01:34:14 2008 +0300
@@ -18,15 +18,19 @@
     if ((mc = memcache_alloc(&_memcache_cb)) == NULL)
         ERROR("memcache_alloc");
     
+    // fix up the endpoint
     endpoint_init(&server_endpoint, 11211);
     
     if (endpoint_parse(&server_endpoint, "localhost"))
         ERROR("config_endpoint_parse");
-
+    
+    // add the server
     if (memcache_add_server(mc, &server_endpoint, 1))
         ERROR("memcache_add_server");
+   
+    // add a request or two
     
-    // XXX: we should have a connect() running now
+    
 
 error:
     return;