Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Adding files

  • Loading branch information...
commit 2338dee93b22f19e9ab3103a4b17ecdde059426e 1 parent c7fbdc6
@jskorpan jskorpan authored
View
577 Client.cpp
@@ -0,0 +1,577 @@
+#include "Client.h"
+#include <ctype.h>
+#include <stdio.h>
+#include <assert.h>
+
+//#define PRINTMARK() fprintf(stderr, "%s: MARK(%d)\n", __FILE__, __LINE__)
+#define PRINTMARK()
+
+Client::Client (SOCKETDESC *sockdesc)
+ : m_writer (1024 * 1200)
+ , m_reader (1024 * 1200)
+{
+ m_sock = sockdesc;
+ m_error = "Unspecified error";
+}
+
+Client::~Client (void)
+{
+ disconnect(NULL);
+}
+
+
+void Client::setError(const char *message)
+{
+ assert (m_error == NULL);
+ m_error = message;
+}
+
+const char *Client::getError(void)
+{
+ return m_error;
+}
+
+bool Client::connect(const char *address, int port)
+{
+ m_reader.reset();
+ m_writer.reset();
+
+ if (!m_sock->connect (m_sock, address, port))
+ {
+ setError("connect() failed or timed out");
+ return false;
+ }
+
+ return true;
+}
+
+bool Client::readLine(void)
+{
+ while (!m_reader.haveLine())
+ {
+ size_t bytesToRead = m_reader.getEndPtr () - m_reader.getWritePtr();
+
+ if (bytesToRead > 65536)
+ {
+ bytesToRead = 65536;
+ }
+
+ int result = m_sock->recv (m_sock, m_reader.getWritePtr(), bytesToRead);
+
+ if (result < 1)
+ {
+ disconnect(NULL);
+ return false;
+ }
+
+ m_reader.push(result);
+ }
+
+ return true;
+}
+
+bool Client::sendWriteBuffer(void)
+{
+ while (true)
+ {
+ size_t bytesToSend = m_writer.getWriteCursor() - m_writer.getReadCursor();
+
+ if (bytesToSend == 0)
+ {
+ return true;
+ }
+
+ int bytesSent = m_sock->send (m_sock, m_writer.getReadCursor(), bytesToSend);
+
+ if (bytesSent < 1)
+ {
+ disconnect(NULL);
+ return false;
+ }
+
+ m_writer.pull (bytesSent);
+ }
+}
+
+
+bool Client::getResult(char **pData, size_t *cbSize)
+{
+ if (pData != NULL)
+ {
+
+ *pData = m_reader.getReadPtr();
+
+ if (m_reader.getBytesLeft() < 2)
+ {
+ m_reader.skip();
+ return false;
+ }
+
+ *cbSize = m_reader.getBytesLeft() - 2;
+ }
+
+ m_reader.skip();
+ return true;
+}
+
+bool Client::cas(const char *key, size_t cbKey, UINT64 casUnique, void *data, size_t cbData, time_t expiration, int flags, bool async)
+{
+
+ if (cbData > 1000 * 1000)
+ {
+ setError("Data size can't be larger than 1000 000 bytes");
+ return false;
+ }
+
+ m_writer.writeChars("cas ", 4);
+ m_writer.writeChars(key, cbKey);
+ m_writer.writeChar(' ');
+ m_writer.writeNumeric(flags);
+ m_writer.writeChar(' ');
+ m_writer.writeNumeric(expiration);
+ m_writer.writeChar(' ');
+ m_writer.writeNumeric(cbData);
+ m_writer.writeChar(' ');
+ m_writer.writeNumeric(casUnique);
+
+ if (async)
+ {
+ m_writer.writeChars(" noreply", 8);
+ }
+ m_writer.writeChars("\r\n", 2);
+ m_writer.writeChars(data, cbData);
+ m_writer.writeChars("\r\n", 2);
+
+ if (!sendWriteBuffer())
+ {
+ return false;
+ }
+
+ if (async)
+ {
+ return true;
+ }
+
+ if (!readLine())
+ {
+ return false;
+ }
+
+ return true;
+
+}
+
+
+bool Client::command(const char *cmd, size_t cbCmd, const char *key, size_t cbKey, void *data, size_t cbData, time_t expiration, int flags, bool async)
+{
+ if (cbData > 1000 * 1000)
+ {
+ setError("Data size can't be larger than 1000 000 bytes");
+ return false;
+ }
+
+ m_writer.writeChars(cmd, cbCmd);
+ m_writer.writeChar(' ');
+ m_writer.writeChars(key, cbKey);
+ m_writer.writeChar(' ');
+ m_writer.writeNumeric(flags);
+ m_writer.writeChar(' ');
+ m_writer.writeNumeric(expiration);
+ m_writer.writeChar(' ');
+ m_writer.writeNumeric(cbData);
+
+ if (async)
+ {
+ m_writer.writeChars(" noreply", 8);
+ }
+ m_writer.writeChars("\r\n", 2);
+ m_writer.writeChars(data, cbData);
+ m_writer.writeChars("\r\n", 2);
+
+ if (!sendWriteBuffer())
+ {
+ PRINTMARK();
+ return false;
+ }
+
+ if (async)
+ {
+ return true;
+ }
+
+
+ if (!readLine())
+ {
+ PRINTMARK();
+ return false;
+ }
+
+ return true;
+}
+
+bool Client::set(const char *key, size_t cbKey, void *data, size_t cbData, time_t expiration, int flags, bool async)
+{
+ return command ("set", 3, key, cbKey, data, cbData, expiration, flags, async);
+}
+
+bool Client::add(const char *key, size_t cbKey, void *data, size_t cbData, time_t expiration, int flags, bool async)
+{
+ return command ("add", 3, key, cbKey, data, cbData, expiration, flags, async);
+}
+
+bool Client::replace(const char *key, size_t cbKey, void *data, size_t cbData, time_t expiration, int flags, bool async)
+{
+ return command ("replace", 7, key, cbKey, data, cbData, expiration, flags, async);
+}
+
+bool Client::append(const char *key, size_t cbKey, void *data, size_t cbData, time_t expiration, int flags, bool async)
+{
+ return command ("append", 6, key, cbKey, data, cbData, expiration, flags, async);
+}
+
+bool Client::prepend(const char *key, size_t cbKey, void *data, size_t cbData, time_t expiration, int flags, bool async)
+{
+ return command ("prepend", 7, key, cbKey, data, cbData, expiration, flags, async);
+}
+
+bool Client::del(const char *key, size_t cbKey, time_t *expiration, bool async)
+{
+ m_writer.writeChars("delete ", 7);
+ m_writer.writeChars(key, cbKey);
+
+ if (expiration)
+ {
+ m_writer.writeChar(' ');
+ m_writer.writeNumeric(*expiration);
+ }
+
+ if (async)
+ {
+ m_writer.writeChars(" noreply", 8);
+ }
+ m_writer.writeChars("\r\n", 2);
+
+ if (!sendWriteBuffer())
+ {
+ return false;
+ }
+
+ if (async)
+ {
+ return true;
+ }
+
+ if (!readLine())
+ {
+ return false;
+ }
+
+ return true;
+}
+
+
+bool Client::incr(const char *key, size_t cbKey, UINT64 increment, bool async)
+{
+ m_writer.writeChars("incr ", 5);
+ m_writer.writeChars(key, cbKey);
+ m_writer.writeChar(' ');
+ m_writer.writeNumeric(increment);
+
+ if (async)
+ {
+ m_writer.writeChars(" noreply", 8);
+ }
+ m_writer.writeChars("\r\n", 2);
+
+ if (!sendWriteBuffer())
+ {
+ return false;
+ }
+
+ if (async)
+ {
+ return true;
+ }
+
+ if (!readLine())
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool Client::decr(const char *key, size_t cbKey, UINT64 decrement, bool async)
+{
+ m_writer.writeChars("decr ", 5);
+ m_writer.writeChars(key, cbKey);
+ m_writer.writeChar(' ');
+ m_writer.writeNumeric(decrement);
+
+ if (async)
+ {
+ m_writer.writeChars(" noreply", 8);
+ }
+ m_writer.writeChars("\r\n", 2);
+
+ if (!sendWriteBuffer())
+ {
+ return false;
+ }
+
+ if (async)
+ {
+ return true;
+ }
+
+ if (!readLine())
+ {
+ return false;
+ }
+
+ return true;
+}
+
+
+void Client::getsBegin(void)
+{
+ m_writer.writeChars("gets", 4);
+}
+
+void Client::getBegin(void)
+{
+ m_writer.writeChars("get", 3);
+}
+
+void Client::getKeyWrite(const char *key, size_t cbKey)
+{
+ m_writer.writeChar(' ');
+ m_writer.writeChars(key, cbKey);
+}
+
+bool Client::getFlush(void)
+{
+ m_writer.writeChars("\r\n", 2);
+ if (!sendWriteBuffer())
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool Client::version(char **pVersion, size_t *cbVersion)
+{
+ m_writer.writeChars("version\r\n", 9);
+
+ if (!sendWriteBuffer())
+ {
+ return false;
+ }
+
+ if (!readLine())
+ {
+ return false;
+ }
+
+ if (m_reader.readBytes(8) == NULL)
+ {
+ return false;
+ }
+
+ *pVersion= (char *) m_reader.readUntil(cbVersion, '\r');
+
+ if (*pVersion == NULL)
+ {
+ return false;
+ }
+
+ m_reader.skip();
+
+ return true;
+}
+
+bool Client::stats(const char *arg, size_t cbArg)
+{
+ m_writer.writeChars("stats", 5);
+
+ if (arg)
+ {
+ m_writer.writeChar(' ');
+ m_writer.writeChars(arg, cbArg);
+ }
+
+ m_writer.writeChars("\r\n", 2);
+
+ if (!sendWriteBuffer())
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool Client::getStats(char **pName, size_t *cbName, char **pArg, size_t *cbArg)
+{
+ if (!readLine())
+ {
+ return false;
+ }
+
+ if (m_reader.readBytes(5) == NULL)
+ {
+ m_reader.skip();
+ return false;
+ }
+
+ *pName = (char *) m_reader.readUntil(cbName, ' ');
+
+ if (m_reader.readBytes(1) == NULL)
+ {
+ return false;
+ }
+
+ *pArg = (char *) m_reader.readUntil(cbArg, '\r');
+ m_reader.skip();
+
+ return true;
+
+}
+
+
+bool Client::getReadNext(char **key, size_t *cbKey, char **data, size_t *cbData, int *_flags, UINT64 *_cas, bool *bError)
+{
+ *bError = false;
+
+ if (!readLine())
+ {
+ *bError = true;
+ return false;
+ }
+
+ if (m_reader.readBytes(6) == NULL)
+ {
+ // "END\r\n" was recieved
+ m_reader.skip();
+ return false;
+ }
+
+ *key = (char *) m_reader.readUntil(cbKey, ' ');
+
+ if (*key == NULL)
+ {
+ *bError = true;
+ return false;
+ }
+
+ *(*key + *cbKey) = '\0';
+
+
+ if (m_reader.readBytes(1) == NULL)
+ {
+ *bError = true;
+ return false;
+ }
+
+ UINT64 flags;
+ UINT64 bytes;
+
+ if (!m_reader.readNumeric(&flags))
+ {
+ *bError = true;
+ return false;
+ }
+
+ *_flags = (int) flags;
+
+ if (m_reader.readBytes(1) == NULL)
+ {
+ *bError = true;
+ return false;
+ }
+
+ if (!m_reader.readNumeric(&bytes))
+ {
+ *bError = true;
+ return false;
+ }
+
+ if (m_reader.getBytesLeft () > 2)
+ {
+ UINT64 cas;
+
+ if (m_reader.readBytes(1) == NULL)
+ {
+ *bError = true;
+ return false;
+ }
+
+ if (!m_reader.readNumeric(&cas))
+ {
+ *bError = true;
+ return false;
+ }
+
+ *_cas = cas;
+ }
+
+ m_reader.skip();
+
+ size_t cbExpect = bytes + 2;
+
+
+ while (m_reader.getWritePtr() - m_reader.getReadPtr() < cbExpect)
+ {
+ size_t bytesToRead = cbExpect - (m_reader.getWritePtr() - m_reader.getReadPtr());
+
+ if (bytesToRead > 65536)
+ {
+ bytesToRead = 65536;
+ }
+
+ int result = m_sock->recv(m_sock, m_reader.getWritePtr(), bytesToRead);
+
+ if (result < 1)
+ {
+ *bError = true;
+ disconnect(NULL);
+ return false;
+ }
+
+ m_reader.push(result);
+ }
+
+ m_reader.setEndPtr(m_reader.getReadPtr() + bytes + 2);
+ *data = (char *) m_reader.readBytes(bytes);
+ *cbData = bytes;
+
+ *((*data) + *cbData) = '\0';
+
+
+ m_reader.skip();
+
+ return true;
+}
+
+
+bool Client::isConnected(void)
+{
+ return (m_sock->prv) ? true : false;
+}
+
+
+void Client::disconnect(const char *reason)
+{
+ if (reason)
+ {
+ setError(reason);
+ }
+
+ if (m_sock->prv == NULL)
+ {
+ return;
+ }
+
+ m_sock->destroy(m_sock);
+ m_sock->prv = NULL;
+}
View
75 Client.h
@@ -0,0 +1,75 @@
+#pragma once
+
+#include "mcdefs.h"
+#include "PacketWriter.h"
+#include "PacketReader.h"
+
+typedef struct SOCKETDESC
+{
+ void *prv;
+ int (*send)(SOCKETDESC *desc, void *data, size_t cbData);
+ int (*recv)(SOCKETDESC *desc, void *data, size_t cbMaxData);
+ void (*destroy)(SOCKETDESC *desc);
+ int (*connect)(SOCKETDESC *desc, const char *address, int port);
+} _SOCKETDESC;
+
+class Client
+{
+public:
+
+ enum RESULT
+ {
+ STORED,
+ NOT_STORED,
+ EXISTS,
+ NOT_FOUND,
+ };
+
+public:
+
+
+ Client (SOCKETDESC *sockdesc);
+ ~Client (void);
+ bool connect(const char *address, int port);
+ bool isConnected(void);
+ void disconnect(const char *reason);
+
+
+ void getsBegin(void);
+ void getBegin(void);
+ void getKeyWrite(const char *key, size_t cbKey);
+ bool getFlush(void);
+ bool getReadNext(char **key, size_t *cbKey, char **data, size_t *cbData, int *flags, UINT64 *cas, bool *bError);
+
+
+ bool set(const char *key, size_t cbKey, void *data, size_t cbData, time_t expiration, int flags, bool async);
+ bool del(const char *key, size_t cbKey, time_t *expiration, bool async);
+ bool add(const char *key, size_t cbKey, void *data, size_t cbData, time_t expiration, int flags, bool async);
+
+ bool replace(const char *key, size_t cbKey, void *data, size_t cbData, time_t expiration, int flags, bool async);
+ bool append(const char *key, size_t cbKey, void *data, size_t cbData, time_t expiration, int flags, bool async);
+ bool prepend(const char *key, size_t cbKey, void *data, size_t cbData, time_t expiration, int flags, bool async);
+
+ bool cas(const char *key, size_t cbKey, UINT64 casUnique, void *data, size_t cbData, time_t expiration, int flags, bool async);
+ bool incr(const char *key, size_t cbKey, UINT64 increment, bool async);
+ bool decr(const char *key, size_t cbKey, UINT64 decrement, bool async);
+ bool version(char **version, size_t *cbVersion);
+ bool stats(const char *arg, size_t cbArg);
+ bool getStats(char **pName, size_t *cbName, char **pArg, size_t *cbArg);
+ bool getResult(char **pData, size_t *cbSize);
+ const char *getError(void);
+
+private:
+ bool command(const char *cmd, size_t cbCmd, const char *key, size_t cbKey, void *data, size_t cbData, time_t expiration, int flags, bool async);
+ bool sendWriteBuffer(void);
+ bool readLine(void);
+
+ void setError(const char *message);
+
+private:
+ SOCKETDESC *m_sock;
+ PacketWriter m_writer;
+ PacketReader m_reader;
+
+ const char *m_error;
+};
View
202 PacketReader.cpp
@@ -0,0 +1,202 @@
+#include "PacketReader.h"
+#include "mcdefs.h"
+#include <assert.h>
+#include "socketdefs.h"
+
+#include <stdio.h>
+
+#define BYTEORDER_UINT16(_x) (_x)
+#define BYTEORDER_UINT32(_x) (_x)
+
+PacketReader::PacketReader (size_t _cbSize)
+{
+ m_buffStart = new char[_cbSize];
+ m_writeCursor = m_buffStart;
+ m_buffEnd = m_buffStart + _cbSize;
+ m_readCursor = m_buffStart;
+ m_packetEnd = NULL;
+}
+
+PacketReader::~PacketReader (void)
+{
+ delete m_buffStart;
+}
+
+void PacketReader::skip()
+{
+ assert (m_readCursor <= m_packetEnd);
+
+ m_readCursor = m_packetEnd;
+
+ if (m_readCursor == m_writeCursor)
+ {
+ m_readCursor = m_buffStart;
+ m_writeCursor = m_buffStart;
+ m_packetEnd = NULL;
+ }
+}
+
+void PacketReader::reset()
+{
+ m_readCursor = m_buffStart;
+ m_writeCursor = m_buffStart;
+ m_packetEnd = NULL;
+}
+
+
+void PacketReader::push(size_t _cbData)
+{
+ //fprintf (stderr, "%s: Pushing %u bytes\n", __FUNCTION__, _cbData);
+ m_writeCursor += _cbData;
+}
+
+
+void PacketReader::setEndPtr (char *ptr)
+{
+ m_packetEnd = ptr;
+}
+
+
+char *PacketReader::getReadPtr()
+{
+ return m_readCursor;
+}
+
+char *PacketReader::getWritePtr()
+{
+ return m_writeCursor;
+}
+
+char *PacketReader::getStartPtr()
+{
+ return m_buffStart;
+}
+
+char *PacketReader::getEndPtr()
+{
+ return m_buffEnd;
+}
+
+extern void PrintBuffer(FILE *file, void *_offset, size_t len, int perRow);
+
+
+bool PacketReader::readNumeric (UINT64 *value)
+{
+ // Scan integer part
+ UINT64 intValue = 0;
+
+ while (m_readCursor < m_packetEnd)
+ {
+ int chr = (int) (unsigned char) *(m_readCursor);
+
+ switch (chr)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ intValue = intValue * 10ULL + (UINT64) (chr - 48);
+ m_readCursor ++;
+ break;
+
+ default:
+ goto END_LOOP;
+ break;
+ }
+ }
+
+ END_LOOP:
+
+ if (m_readCursor == m_packetEnd)
+ {
+ return false;
+ }
+
+ *value = intValue;
+ return true;
+}
+
+
+bool PacketReader::haveLine()
+{
+ m_packetEnd = NULL;
+
+ char *ptr = m_readCursor;
+
+ while (ptr < m_writeCursor)
+ {
+ if (*ptr == '\n')
+ {
+ m_packetEnd = ptr + 1;
+ return true;
+ }
+
+ ptr ++;
+ }
+
+ return false;
+}
+
+
+
+UINT8 *PacketReader::readUntil(size_t *_outLen, char value)
+{
+ char *start = m_readCursor;
+ char *end = m_readCursor;
+
+ while (end < m_packetEnd && *end != value)
+ {
+ end ++;
+ }
+
+ if (end == m_packetEnd)
+ {
+ return NULL;
+ }
+
+ *_outLen = (end - start);
+
+ m_readCursor = end;
+
+ return (UINT8 *) start;
+}
+
+UINT8 *PacketReader::readBytes(size_t cbsize)
+{
+ if (m_readCursor + cbsize > m_packetEnd)
+ {
+ return NULL;
+ }
+
+ UINT8 *ret = (UINT8 *) m_readCursor;
+ m_readCursor += cbsize;
+
+ return ret;
+}
+
+size_t PacketReader::getBytesLeft()
+{
+ return (m_packetEnd - m_readCursor);
+}
+
+void PacketReader::rewind(size_t num)
+{
+ m_readCursor -= num;
+}
+
+size_t PacketReader::getSize()
+{
+ return m_buffEnd - m_buffStart;
+}
+
+
+
+
+
+
View
40 PacketReader.h
@@ -0,0 +1,40 @@
+#ifndef __AMPACKETREADER_H__
+#define __AMPACKETREADER_H__
+
+#include "mcdefs.h"
+
+class PacketReader
+{
+private:
+ char *m_buffStart;
+ char *m_buffEnd;
+ char *m_readCursor;
+ char *m_writeCursor;
+ char *m_packetEnd;
+
+public:
+
+ PacketReader (size_t cbSize);
+ ~PacketReader (void);
+
+ void reset();
+ void skip();
+ void push(size_t _cbData);
+ char *getReadPtr();
+ char *getWritePtr();
+ char *getStartPtr();
+ char *getEndPtr();
+ void setEndPtr (char *ptr);
+ size_t getSize();
+ bool haveLine();
+
+ UINT8 *readUntil(size_t *_outLen, char value);
+ UINT8 *readBytes(size_t cbsize);
+
+ bool readNumeric (UINT64 *value);
+
+ size_t getBytesLeft();
+ void rewind(size_t num);
+};
+
+#endif
View
184 PacketWriter.cpp
@@ -0,0 +1,184 @@
+#include "PacketWriter.h"
+#include <assert.h>
+
+#define BYTEORDER_UINT16(_x) (_x)
+#define BYTEORDER_UINT32(_x) (_x)
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+
+PacketWriter::PacketWriter(size_t _cbSize)
+{
+ m_buffStart = new char[_cbSize];
+ m_buffEnd = m_buffStart + _cbSize;
+ m_readCursor = m_buffStart;
+ m_writeCursor = m_buffStart;
+}
+
+PacketWriter::~PacketWriter(void)
+{
+ delete m_buffStart;
+}
+
+// Push/increment write cursor
+void PacketWriter::push(void *data, size_t cbData)
+{
+ assert (m_writeCursor + cbData < m_buffEnd);
+
+ memcpy (m_writeCursor, data, cbData);
+ m_writeCursor += cbData;
+}
+
+// Pull/Increment read cursor
+void PacketWriter::pull(size_t cbSize)
+{
+ assert (cbSize <= (m_writeCursor - m_readCursor));
+ m_readCursor += cbSize;
+
+ if (m_readCursor == m_writeCursor)
+ {
+ m_readCursor = m_buffStart;
+ m_writeCursor = m_buffStart;
+ }
+}
+
+char *PacketWriter::getStart()
+{
+ return m_buffStart;
+}
+
+char *PacketWriter::getEnd()
+{
+ return m_buffEnd;
+}
+
+char *PacketWriter::getReadCursor()
+{
+ return m_readCursor;
+}
+
+char *PacketWriter::getWriteCursor()
+{
+ return m_writeCursor;
+}
+
+bool PacketWriter::isDone()
+{
+ return (m_readCursor == m_writeCursor);
+}
+
+void PacketWriter::reset()
+{
+ m_readCursor = m_buffStart;
+ m_writeCursor = m_buffStart;
+}
+
+void strreverse(char* begin, char* end)
+{
+ char aux;
+ while (end > begin)
+ aux = *end, *end-- = *begin, *begin++ = aux;
+}
+
+void PacketWriter::writeChar (const char _value)
+{
+ *(m_writeCursor++) = _value;
+}
+
+void PacketWriter::writeChars (const void *data, size_t cbData)
+{
+ memcpy (m_writeCursor, data, cbData);
+ m_writeCursor += cbData;
+}
+
+void PacketWriter::writeNumeric (UINT64 value)
+{
+ char* wstr;
+ wstr = m_writeCursor;
+ // Conversion. Number is reversed.
+
+ do *wstr++ = (char)(48 + (value % 10ULL)); while(value /= 10ULL);
+ if (value < 0) *wstr++ = '-';
+
+ // Reverse string
+ strreverse(m_writeCursor,wstr - 1);
+ m_writeCursor += (wstr - (m_writeCursor));
+}
+
+
+void PrintBuffer(FILE *file, void *_offset, size_t len, int perRow)
+{
+ size_t cnt = 0;
+
+ char *offset = (char *) _offset;
+ char *end = offset + len;
+
+ int orgPerRow = perRow;
+
+ fprintf (file, "%u %p --------------\n", len, _offset);
+
+ while (offset != end)
+ {
+ fprintf (file, "%08x: ", cnt);
+
+ if (end - offset < perRow)
+ {
+ perRow = end - offset;
+ }
+
+ for (int index = 0; index < perRow; index ++)
+ {
+ int chr = (unsigned char) *offset;
+
+ if (isprint(chr))
+ {
+ fprintf (file, "%c", chr);
+ }
+ else
+ {
+ fprintf (file, ".");
+ }
+
+ offset ++;
+ }
+
+ offset -= perRow;
+
+ for (int index = perRow; index < orgPerRow; index ++)
+ {
+ fprintf (file, " ");
+ }
+
+ fprintf (file, " ");
+
+ for (int index = 0; index < perRow; index ++)
+ {
+ int chr = (unsigned char) *offset;
+
+ fprintf (file, "%02x ", chr);
+ offset ++;
+ }
+
+ fprintf (file, "\n");
+
+ cnt += perRow;
+ }
+}
+
+void PacketWriter::finalize(int packetNumber)
+{
+#ifdef __FIX_IT_OR_REMOVE__
+ size_t packetLen = (m_writeCursor - m_readCursor - MYSQL_PACKET_HEADER_SIZE);
+
+ *((UINT32 *)m_buffStart) = packetLen;
+ *((UINT8 *)m_buffStart + 3) = packetNumber;
+
+ //PrintBuffer (stdout, m_readCursor, (m_writeCursor - m_readCursor), 16);
+#endif
+}
+
+size_t PacketWriter::getSize(void)
+{
+ return (m_buffEnd - m_buffStart);
+}
View
44 PacketWriter.h
@@ -0,0 +1,44 @@
+#ifndef __AMPACKETWRITER_H__
+#define __AMPACKETWRITER_H__
+
+#include "socketdefs.h"
+#include "mcdefs.h"
+
+class PacketWriter
+{
+public:
+ PacketWriter(size_t _cbSize);
+ ~PacketWriter(void);
+
+ // Push/increment write cursor
+ void push(void *data, size_t cbData);
+
+ // Pull/Increment read cursor
+ void pull(size_t cbSize);
+
+ char *getStart();
+ char *getEnd();
+ char *getReadCursor();
+ char *getWriteCursor();
+ bool isDone();
+ void reset();
+
+ void writeChars (const void *data, size_t cbData);
+ void writeChar (const char _value);
+ void writeNumeric (UINT64 value);
+
+ void finalize(int packetNumber);
+
+ size_t getSize(void);
+
+
+private:
+ char *m_buffStart;
+ char *m_buffEnd;
+ char *m_readCursor;
+ char *m_writeCursor;
+
+
+};
+
+#endif
View
13 mcdefs.h
@@ -0,0 +1,13 @@
+#ifndef __MCDEFS_H__
+#define __MCDEFS_H__
+
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#else
+#include <sys/types.h>
+typedef u_int32_t UINT64;
+typedef u_int32_t UINT8;
+#endif
+
+#endif
View
36 python/setup.py
@@ -0,0 +1,36 @@
+from distutils.core import setup, Extension
+import shutil
+import sys
+
+
+
+libs = []
+
+if sys.platform != "win32":
+ libs.append("stdc++")
+
+if sys.platform == "win32":
+ libs.append("ws2_32")
+
+shutil.copy("../Client.h", "./")
+shutil.copy("../mcdefs.h", "./")
+shutil.copy("../PacketWriter.h", "./")
+shutil.copy("../PacketReader.h", "./")
+shutil.copy("../socketdefs.h", "./")
+shutil.copy("../PacketWriter.cpp", "./")
+shutil.copy("../PacketReader.cpp", "./")
+shutil.copy("../Client.cpp", "./")
+
+module1 = Extension('umemcached',
+ sources = ['umemcached.cpp', 'PacketReader.cpp', 'PacketWriter.cpp', 'Client.cpp'],
+ include_dirs = ['./'],
+ library_dirs = [],
+ libraries=libs,
+ define_macros=[('WIN32_LEAN_AND_MEAN', None)])
+
+setup (name = 'umemcached',
+ version = '1.0',
+ description = '',
+ ext_modules = [module1])
+
+
View
315 python/tests.py
@@ -0,0 +1,315 @@
+# -*- coding: utf-8 -*-
+from __future__ import with_statement
+
+import time
+import datetime
+import logging
+import unittest
+import gevent
+import random
+from umemcached import Client
+
+MEMCACHED_ADDRESS = "127.0.0.1:11211"
+
+class Testumemcached(unittest.TestCase):
+ log = logging.getLogger('umemcached')
+
+ def testRandomData(self):
+ def random_bytes(size):
+ return "".join(chr(random.randrange(0, 256)) for i in xrange(size))
+
+ def random_str(size):
+ return "".join(chr(random.randrange(33, 64)) for i in xrange(size))
+
+
+ c = Client(MEMCACHED_ADDRESS);
+ c.connect();
+
+ count = 0
+
+ for x in xrange(0, 10):
+ key = random_str(random.randrange(1, 33))
+ value = random_bytes(random.randrange(1, 5000))
+ c.set(key, value, 0, 0, True)
+ v2 = c.get(key)[0]
+
+ count += 1
+
+ if len(value) != len(v2):
+ print "%d %d != %d" % (count, len(value), len(v2))
+
+ #self.assertEquals(value, v2)
+
+ def testBigDataFail(self):
+ c = Client(MEMCACHED_ADDRESS);
+ c.connect();
+ data = "31337" * 10000
+
+ for x in xrange(0, 10):
+ v = c.get("kaka" + str(x))
+ c.set("fsdafbdsakjfjdkfjadklsafdsafdsaffdsafdasfdsafdasfsdafdsafdsafasdas" + str(x), data, 604830, 17, True)
+
+
+ def testIncrDecrString(self):
+ c = Client(MEMCACHED_ADDRESS);
+ c.connect();
+ c.set("test", "hej")
+ self.assertRaises(Exception, c.incr, "test", 1)
+ self.assertRaises(Exception, c.decr, "test", 5)
+
+
+ def testSetExpiration(self):
+ c = Client(MEMCACHED_ADDRESS);
+ c.connect();
+ c.set("test", "1", 60, 2)
+
+ def testSegfault(self):
+ try:
+ c = Client()
+ assert False
+ except(TypeError):
+ pass
+
+
+ def testConnect(self):
+ c = Client(MEMCACHED_ADDRESS);
+ c.connect();
+ self.assertEquals(True, c.is_connected());
+ pass
+
+
+ def testDisconnect(self):
+ c = Client(MEMCACHED_ADDRESS);
+ c.connect();
+ c.disconnect();
+ self.assertEquals(False, c.is_connected());
+ pass
+
+
+ def testClose(self):
+ c = Client(MEMCACHED_ADDRESS);
+ c.connect();
+ c.close();
+ self.assertEquals(False, c.is_connected());
+ pass
+
+ def testConnectTwice(self):
+ c = Client(MEMCACHED_ADDRESS);
+ c.connect();
+ c.disconnect();
+ self.assertEquals(False, c.is_connected());
+
+ try:
+ c.connect();
+ assert False
+ except (RuntimeError):
+ pass
+
+ pass
+
+ def testConnectCloseQuery(self):
+ c = Client(MEMCACHED_ADDRESS);
+ c.connect();
+ c.disconnect();
+ try:
+ r = c.set("jonas", "kaka", 0, False)
+ assert False
+ except:
+ pass
+
+
+ def testOversizedKey(self):
+ c = Client(MEMCACHED_ADDRESS);
+ c.connect();
+ data = "A" * (1000 * 1000 * 2)
+ try:
+ r = c.set("test", data)
+ assert False
+ except(RuntimeError):
+ pass
+
+
+ def testGet(self):
+ c = Client(MEMCACHED_ADDRESS);
+ c.connect();
+ c.set("key", "value")
+ self.assertEquals("value", c.get("key")[0])
+ self.assertEquals(None, c.get("key23123"))
+ pass
+
+ def testGets(self):
+ c = Client(MEMCACHED_ADDRESS);
+ c.connect();
+ c.set("key", "value")
+ self.assertEquals("value", c.gets("key")[0])
+ self.assertEquals(None, c.gets("key23123"))
+ r = c.gets("key")
+ self.assertEquals(3, len(r))
+ pass
+
+
+ def testSet(self):
+ c = Client(MEMCACHED_ADDRESS);
+ c.connect();
+ c.set("key", "my new value")
+ self.assertEquals("my new value", c.get("key")[0])
+ pass
+
+
+ def testGet_multi(self):
+ c = Client(MEMCACHED_ADDRESS);
+ c.connect();
+ c.set("key1", "value1")
+ c.set("key2", "value2")
+ c.set("key3", "value3")
+ r = c.get_multi(["key1", "key2", "key3", "key4"])
+
+ self.assertEquals(("value1", 0), r["key1"])
+ self.assertEquals(("value2", 0), r["key2"])
+ self.assertEquals(("value3", 0), r["key3"])
+ self.assertEquals(None, r.get("key4"))
+
+ pass
+
+
+
+ def testGets_multi(self):
+ c = Client(MEMCACHED_ADDRESS);
+ c.connect();
+ c.set("key1", "value1")
+ c.set("key2", "value2")
+ c.set("key3", "value3")
+ r = c.gets_multi(["key1", "key2", "key3", "key4"])
+
+ self.assertEquals(3, len(r["key1"]))
+ self.assertEquals(3, len(r["key2"]))
+ self.assertEquals(3, len(r["key3"]))
+
+ self.assertEquals("value1", r["key1"][0])
+ self.assertEquals("value2", r["key2"][0])
+ self.assertEquals("value3", r["key3"][0])
+ self.assertEquals(None, r.get("key4"))
+ pass
+
+ def testAdd(self):
+ c = Client(MEMCACHED_ADDRESS);
+ c.connect();
+ c.set("key1", "value1")
+ self.assertEquals("NOT_STORED", c.add("key1", "value"))
+ pass
+
+ def testReplace(self):
+ c = Client(MEMCACHED_ADDRESS);
+ c.connect();
+ c.set("key1", "value1")
+ self.assertEquals("STORED", c.replace("key1", "value"))
+ pass
+
+ pass
+ def testAppend(self):
+ c = Client(MEMCACHED_ADDRESS);
+ c.connect();
+ c.set("key1", "a")
+ self.assertEquals("STORED", c.append("key1", "b"))
+ self.assertEquals("ab", c.get("key1")[0])
+ pass
+
+ def testPrepend(self):
+ c = Client(MEMCACHED_ADDRESS);
+ c.connect();
+ c.set("key1", "a")
+ self.assertEquals("STORED", c.prepend("key1", "b"))
+ self.assertEquals("ba", c.get("key1")[0])
+ pass
+
+ def testDel(self):
+ c = Client(MEMCACHED_ADDRESS);
+ c.connect();
+ c.set("key1", "a")
+ c.delete("key1")
+ self.assertEquals(None, c.get("key1"))
+ pass
+
+
+ def testCas(self):
+ c = Client(MEMCACHED_ADDRESS);
+ c.connect();
+ c.set("key1", "a")
+ value, flags, cas = c.gets("key1")
+ self.assertEquals("STORED", c.cas("key1", "b", cas))
+ c.set("key1", "a")
+ self.assertEquals("EXISTS", c.cas("key1", "b", cas))
+
+ def testIncr(self):
+ c = Client(MEMCACHED_ADDRESS);
+ c.connect();
+ c.set("key1", "0")
+ c.incr("key1", 313370)
+ self.assertEquals("313370", c.get("key1")[0])
+
+ def testDecr(self):
+ c = Client(MEMCACHED_ADDRESS);
+ c.connect();
+ c.set("key1", "31337")
+ c.decr("key1", 31337)
+ self.assertEquals(0, long(c.get("key1")[0]))
+
+
+ def testVersion(self):
+ c = Client(MEMCACHED_ADDRESS);
+ c.connect();
+ v = c.version()
+ l = v.split('.');
+
+ self.assertEquals(3, len(l))
+
+ def testStats(self):
+ c = Client(MEMCACHED_ADDRESS);
+ c.connect();
+ d = c.stats()
+
+ self.assertTrue (d.has_key("uptime"))
+ self.assertTrue (d.has_key("bytes"))
+
+ def testConnectFails(self):
+ c = Client("130.244.1.1:31337");
+ try:
+ c.connect();
+ assert False
+ except:
+ pass
+
+ c.disconnect();
+ pass
+
+ def testConnectDNSOK(self):
+ c = Client("localhost:11211");
+ c.connect();
+ c.disconnect();
+ pass
+
+ def testConnectDNSFails(self):
+ c = Client("flensost:12111");
+ try:
+ c.connect();
+ assert False
+ except:
+ pass
+
+ c.disconnect();
+ pass
+
+if __name__ == '__main__':
+ unittest.main()
+
+"""
+if __name__ == '__main__':
+ from guppy import hpy
+ hp = hpy()
+ hp.setrelheap()
+ while True:
+ unittest.main()
+ heap = hp.heapu()
+ print heap
+"""
+
View
1,169 python/umemcached.cpp
@@ -0,0 +1,1169 @@
+#define PY_SSIZE_T_CLEAN
+#include <Python.h>
+#include "Client.h"
+#include <string.h>
+#include <stdio.h>
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef alloca
+#define alloca _alloca
+#endif
+
+//#define PRINTMARK() fprintf(stderr, "%s: MARK(%d)\n", __FILE__, __LINE__)
+#define PRINTMARK()
+
+int API_wouldBlock_gevent(void *sock, int fd, int ops, int timeout);
+
+//static PyTypeObject ClientType;
+
+typedef struct {
+ PyObject_HEAD
+ Client *client;
+ SOCKETDESC desc;
+ PyObject *sock;
+ PyObject *host;
+ int port;
+ SOCKET sockfd;
+} PyClient;
+
+int API_send(SOCKETDESC *desc, void *data, size_t cbData)
+{
+ PyClient *client = (PyClient *) desc->prv;
+ int result;
+
+ if (client == NULL)
+ {
+ PyErr_Format(PyExc_IOError, "No client object");
+ PRINTMARK();
+ return -1;
+ }
+
+ while (true)
+ {
+ PRINTMARK();
+ result = send (client->sockfd, (char *) data, cbData, MSG_NOSIGNAL);
+ PRINTMARK();
+
+ if (result == 0)
+ {
+ PyErr_Format(PyExc_IOError, "Connection reset by peer when sending on socket");
+ PRINTMARK();
+ return -1;
+ }
+
+ if (result > 0)
+ {
+ PRINTMARK();
+ break;
+ }
+
+ if (!SocketWouldBlock(client->sockfd))
+ {
+ PRINTMARK();
+
+ PyErr_Format(PyExc_IOError, "Socket error %d when sending", SocketGetLastError());
+ return -1;
+ }
+
+ if (API_wouldBlock_gevent(client->sock, client->sockfd, 2, 10) == 0)
+ {
+ PRINTMARK();
+
+ return -1;
+ }
+ }
+
+ return result;
+}
+
+int API_recv(SOCKETDESC *desc, void *data, size_t cbMaxData)
+{
+ PyClient *client = (PyClient *) desc->prv;
+
+ if (client == NULL)
+ {
+ PyErr_Format(PyExc_IOError, "No client object");
+
+ PRINTMARK();
+ return -1;
+ }
+
+ int result;
+
+ while (true)
+ {
+ result = recv ( client->sockfd, (char *) data, cbMaxData, MSG_NOSIGNAL);
+
+ if (result == 0)
+ {
+ PRINTMARK();
+ PyErr_Format(PyExc_IOError, "Connection reset by peer while reading on socket");
+ return -1;
+ }
+
+ if (result > 0)
+ {
+ PRINTMARK();
+ break;
+ }
+
+ PRINTMARK();
+ if (!SocketWouldBlock(client->sockfd))
+ {
+ PyErr_Format(PyExc_IOError, "Socket error %d when reading", SocketGetLastError());
+ return -1;
+ }
+
+ PRINTMARK();
+ if (API_wouldBlock_gevent(client->sock, client->sockfd, 1, 10) == 0)
+ {
+ PRINTMARK();
+ return -1;
+ }
+ PRINTMARK();
+
+ }
+
+ return result;
+}
+
+void API_destroy(SOCKETDESC *desc)
+{
+ PyClient *client = (PyClient *) desc->prv;
+ Py_DECREF(client->sock);
+ client->sock = NULL;
+
+}
+
+int API_connect(SOCKETDESC *desc, const char *address, int port)
+{
+ PyClient *client = (PyClient *) desc->prv;
+
+ char strTemp[256 + 1];
+ PRINTMARK();
+ snprintf (strTemp, 256, "%s:%d", address, port);
+ PRINTMARK();
+
+ PRINTMARK();
+
+ PyObject *args = PyTuple_New(2);
+ PyTuple_SET_ITEM(args, 0, client->host);
+ PyTuple_SET_ITEM(args, 1, PyInt_FromLong(client->port));
+ PyObject *method = PyString_FromString("connect");
+
+ PyObject *res = PyObject_CallMethodObjArgs(client->sock, method, args, NULL);
+
+ PRINTMARK();
+ Py_DECREF(PyTuple_GET_ITEM(args, 1));
+ Py_DECREF(args);
+ Py_DECREF(method);
+
+ if (res == NULL)
+ {
+ PRINTMARK();
+ return 0;
+ }
+
+ Py_DECREF(res);
+
+ PRINTMARK();
+ return 1;
+}
+
+void *API_createSocket(int family, int type, int proto)
+{
+ /* Create a normal socket */
+ PyObject *sockobj;
+ //FIXME: PyModule will leak
+ static PyObject *sockmodule = NULL;
+ static PyObject *sockclass = NULL;
+ static int once = 1;
+
+ if (once)
+ {
+ /*FIXME: References for module or class are never released */
+ sockmodule = PyImport_ImportModule ("gevent.socket");
+
+ if (sockmodule == NULL)
+ {
+ PRINTMARK();
+ return NULL;
+ }
+ sockclass = PyObject_GetAttrString(sockmodule, "socket");
+
+ if (sockclass == NULL)
+ {
+ PRINTMARK();
+ return NULL;
+ }
+
+ //FIXME: PyType will leak
+ if (!PyType_Check(sockclass))
+ {
+ PRINTMARK();
+ return NULL;
+ }
+
+ if (!PyCallable_Check(sockclass))
+ {
+ PRINTMARK();
+ return NULL;
+ }
+
+ once = 0;
+ }
+
+ PRINTMARK();
+ sockobj = PyObject_Call (sockclass, PyTuple_New(0), NULL);
+ PRINTMARK();
+
+ if (sockobj == NULL)
+ {
+ PRINTMARK();
+ return NULL;
+ }
+
+ PRINTMARK();
+ return sockobj;
+}
+
+int API_getSocketFD(void *sock)
+{
+ int ret;
+ PyObject *fdobj;
+ PRINTMARK();
+
+ fdobj = PyObject_CallMethod ((PyObject *) sock, "fileno", NULL);
+ PRINTMARK();
+
+ if (fdobj == NULL)
+ {
+ PRINTMARK();
+ return -1;
+ }
+
+ if (!PyInt_Check(fdobj))
+ {
+ Py_XDECREF(fdobj);
+ PRINTMARK();
+ return -1;
+ }
+
+ ret = PyInt_AS_LONG(fdobj);
+
+ Py_DECREF(fdobj);
+ return ret;
+}
+
+void API_closeSocket(void *sock)
+{
+ PyObject *res = PyObject_CallMethod( (PyObject *) sock, "close", NULL);
+
+ if (res == NULL)
+ {
+ PRINTMARK();
+ return;
+ }
+
+ Py_DECREF(res);
+}
+
+void API_deleteSocket(void *sock)
+{
+ Py_DECREF( (PyObject *) sock);
+}
+
+/*
+For gevent
+*/
+
+
+int API_wouldBlock_gevent(void *sock, int fd, int ops, int timeout)
+{
+ /* Setup gevent and yield to gevent hub */
+
+ static int once = 1;
+ static PyObject *sockmodule = NULL;
+ static PyObject *waitread = NULL;
+ static PyObject *waitwrite = NULL;
+
+ PyObject *resObject = NULL;
+ PyObject *argList;
+ PyObject *kwargList;
+
+ if (once)
+ {
+ /*FIXME: References for module, class or methods are never released */
+ sockmodule = PyImport_ImportModule ("gevent.socket");
+
+ if (sockmodule == NULL)
+ {
+ PRINTMARK();
+ return -1;
+ }
+
+ waitread = PyObject_GetAttrString(sockmodule, "wait_read");
+ waitwrite = PyObject_GetAttrString(sockmodule, "wait_write");
+
+ if (waitread == NULL || waitwrite == NULL)
+ {
+ PRINTMARK();
+ return -1;
+ }
+
+ if (!PyFunction_Check(waitread) || !PyFunction_Check(waitwrite))
+ {
+ PRINTMARK();
+ return -1;
+ }
+
+ PRINTMARK();
+ once = 0;
+ }
+
+
+ PRINTMARK();
+ //FIXME: do this once
+ argList = PyTuple_New(1);
+ PyTuple_SET_ITEM(argList, 0, PyInt_FromLong(fd));
+ kwargList = PyDict_New();
+ PyDict_SetItemString(kwargList, "timeout", PyInt_FromLong(timeout));
+
+ PRINTMARK();
+
+ switch (ops)
+ {
+ case 1:
+ PRINTMARK();
+
+ resObject = PyObject_Call (waitread, argList, kwargList);
+ PRINTMARK();
+ break;
+
+ case 2:
+ PRINTMARK();
+ resObject = PyObject_Call (waitwrite, argList, kwargList);
+ PRINTMARK();
+ break;
+ }
+
+ Py_DECREF(argList);
+ Py_DECREF(kwargList);
+
+ if (resObject == NULL)
+ {
+ if (!PyErr_Occurred())
+ {
+ PyErr_Format(PyExc_RuntimeError, "umemcached: Python exception not set for operation %d", ops);
+ }
+ PRINTMARK();
+ return 0;
+ }
+
+ PRINTMARK();
+ Py_DECREF(resObject);
+ PRINTMARK();
+
+ return 1;
+}
+
+int Client_init(PyClient *self, PyObject *args)
+{
+ /* Args:
+ def __init__(self, address, protocol = "text", codec = "default"):
+ */
+
+ self->client = NULL;
+ self->host = NULL;
+
+
+ char *address;
+ PRINTMARK();
+
+ if (!PyArg_ParseTuple (args, "s", &address))
+ {
+ PRINTMARK();
+ return -1;
+ }
+
+ PRINTMARK();
+ char *offset = strchr (address, ':');
+
+ if (offset == NULL)
+ {
+ PyErr_Format(PyExc_RuntimeError, "Invalid argument for address");
+ return -1;
+ }
+
+ char *port = address + (offset - address) + 1;
+
+
+ self->host = PyString_FromStringAndSize(address, (offset - address));
+ self->port = atoi(port);
+ Py_INCREF(self->host);
+ PRINTMARK();
+ self->sock = (PyObject *) API_createSocket(AF_INET, SOCK_STREAM, 0);
+ PRINTMARK();
+ self->sockfd = API_getSocketFD(self->sock);
+
+ self->desc.prv = self;
+ self->desc.connect = API_connect;
+ self->desc.destroy = API_destroy;
+ self->desc.recv = API_recv;
+ self->desc.send = API_send;
+ PRINTMARK();
+ self->client = new Client(&self->desc);
+ PRINTMARK();
+
+ return 0;
+}
+
+void Client_Destructor(PyClient *self)
+{
+ PRINTMARK();
+ if (self->client) delete self->client;
+ PRINTMARK();
+ Py_XDECREF(self->host);
+ PRINTMARK();
+ PyObject_Del(self);
+ PRINTMARK();
+}
+
+PyObject *Client_connect(PyClient *self, PyObject *args)
+{
+ if (self->desc.prv == NULL)
+ {
+ return PyErr_Format(PyExc_RuntimeError, "Client can not be reconnected");
+ }
+
+ if (!self->client->connect (PyString_AS_STRING(self->host), self->port))
+ {
+ PRINTMARK();
+ return NULL;
+ }
+
+ Py_RETURN_NONE;
+}
+
+
+PyObject *Client_is_connected(PyClient *self, PyObject *args)
+{
+ if (self->client->isConnected())
+ {
+ Py_RETURN_TRUE;
+ }
+
+ Py_RETURN_FALSE;
+}
+
+PyObject *Client_disconnect(PyClient *self, PyObject *args)
+{
+ self->client->disconnect(NULL);
+ Py_RETURN_NONE;
+}
+
+typedef bool (Client::*PFN_COMMAND) (const char *key, size_t cbKey, void *data, size_t cbData, time_t expiration, int flags, bool async);
+
+PyObject *Client_command(PyClient *self, PFN_COMMAND cmd, PyObject *args)
+{
+ char *pResult;
+ size_t cbResult;
+ char *pKey;
+ size_t cbKey;
+ char *pData;
+ size_t cbData;
+ int expire = 0;
+ int flags = 0;
+ int async = 0;
+
+ if (!PyArg_ParseTuple (args, "s#s#|iib", &pKey, &cbKey, &pData, &cbData, &expire, &flags, &async))
+ {
+ return NULL;
+ }
+
+ bool bAsync = async ? true : false;
+
+ if (!(self->client->*cmd)(pKey, cbKey, pData, cbData, expire, flags, async ? true : false))
+ {
+ if (!PyErr_Occurred())
+ {
+ return PyErr_Format(PyExc_RuntimeError, "Operation failed");
+ }
+
+ return NULL;
+ }
+
+ if (!async)
+ {
+ if (self->client->getResult(&pResult, &cbResult))
+ {
+ return PyString_FromStringAndSize(pResult, cbResult);
+ }
+ else
+ {
+ return PyErr_Format(PyExc_RuntimeError, "Could not retrieve result");
+ }
+ }
+
+ Py_RETURN_NONE;
+}
+
+PyObject *Client_set(PyClient *self, PyObject *args)
+{
+ return Client_command(self, &Client::set, args);
+}
+
+PyObject *Client_add(PyClient *self, PyObject *args)
+{
+ return Client_command(self, &Client::add, args);
+}
+
+PyObject *Client_replace(PyClient *self, PyObject *args)
+{
+ return Client_command(self, &Client::replace, args);
+}
+
+PyObject *Client_append(PyClient *self, PyObject *args)
+{
+ return Client_command(self, &Client::append, args);
+}
+
+PyObject *Client_prepend(PyClient *self, PyObject *args)
+{
+ return Client_command(self, &Client::prepend, args);
+}
+
+PyObject *Client_get(PyClient *self, PyObject *args)
+{
+ //[ ] def get(self, key):
+
+ char *pKey;
+ size_t cbKey;
+ char *pData;
+ size_t cbData;
+ UINT64 cas;
+ int flags;
+
+ if (!PyArg_ParseTuple (args, "s#", &pKey, &cbKey))
+ {
+ return NULL;
+ }
+
+ self->client->getBegin();
+
+ self->client->getKeyWrite(pKey, cbKey);
+ self->client->getFlush();
+
+ bool bError = false;
+
+ if (!self->client->getReadNext(&pKey, &cbKey, &pData, &cbData, &flags, &cas, &bError))
+ {
+ if (bError)
+ {
+ if (!PyErr_Occurred())
+ {
+ return PyErr_Format(PyExc_RuntimeError, "umemcached: %s", self->client->getError());
+ }
+ return NULL;
+ }
+
+ Py_RETURN_NONE;
+ }
+
+ PyObject *otuple = PyTuple_New(2);
+ PyObject *ovalue = PyString_FromStringAndSize(pData, cbData);
+ PyObject *oflags = PyInt_FromLong(flags);
+
+ PyTuple_SET_ITEM(otuple, 0, ovalue);
+ PyTuple_SET_ITEM(otuple, 1, oflags);
+
+ while (self->client->getReadNext(&pKey, &cbKey, &pData, &cbData, &flags, &cas, &bError));
+
+ if (bError)
+ {
+ Py_DECREF(otuple);
+
+ if (!PyErr_Occurred())
+ {
+ return PyErr_Format(PyExc_RuntimeError, "umemcached: %s", self->client->getError());
+ }
+
+ return NULL;
+ }
+
+ return otuple;
+}
+
+PyObject *Client_gets(PyClient *self, PyObject *args)
+{
+ //[ ] def gets(self, key, default = None):
+
+ char *pKey;
+ size_t cbKey;
+ char *pData;
+ size_t cbData;
+ UINT64 cas;
+ int flags;
+
+ if (!PyArg_ParseTuple (args, "s#", &pKey, &cbKey))
+ {
+ return NULL;
+ }
+
+ self->client->getsBegin();
+
+ self->client->getKeyWrite(pKey, cbKey);
+ self->client->getFlush();
+
+ bool bError = false;
+
+ if (!self->client->getReadNext(&pKey, &cbKey, &pData, &cbData, &flags, &cas, &bError))
+ {
+ if (bError)
+ {
+ if (!PyErr_Occurred())
+ {
+ return PyErr_Format(PyExc_RuntimeError, "umemcached: %s", self->client->getError());
+ }
+ return NULL;
+ }
+
+ Py_RETURN_NONE;
+ }
+
+
+
+ PyObject *otuple = PyTuple_New(3);
+ PyObject *ovalue = PyString_FromStringAndSize(pData, cbData);
+ PyObject *oflags = PyInt_FromLong(flags);
+ PyObject *ocas = PyLong_FromUnsignedLongLong(cas);
+
+ PyTuple_SET_ITEM(otuple, 0, ovalue);
+ PyTuple_SET_ITEM(otuple, 1, oflags);
+ PyTuple_SET_ITEM(otuple, 2, ocas);
+
+ while (self->client->getReadNext(&pKey, &cbKey, &pData, &cbData, &flags, &cas, &bError));
+
+ if (bError)
+ {
+ Py_DECREF(otuple);
+
+ if (!PyErr_Occurred())
+ {
+ return PyErr_Format(PyExc_RuntimeError, "umemcached: %s", self->client->getError());
+ }
+
+ return NULL;
+ }
+
+ return otuple;
+}
+
+PyObject *Client_get_multi(PyClient *self, PyObject *okeys)
+{
+ //[ ] def get_multi(self, keys):
+
+ char *pKey;
+ size_t cbKey;
+ char *pData;
+ size_t cbData;
+ UINT64 cas;
+ int flags;
+
+ self->client->getBegin();
+
+ PyObject *iterator = PyObject_GetIter(okeys);
+
+ if (iterator == NULL)
+ {
+ return NULL;
+ }
+
+ PyObject *arg;
+
+ while ( (arg = PyIter_Next(iterator)))
+ {
+ PyObject *ostr;
+
+ if (PyString_Check(arg))
+ {
+ ostr = arg;
+ }
+ else
+ {
+ ostr = PyObject_Str(arg);
+ }
+
+ self->client->getKeyWrite(PyString_AS_STRING(ostr), PyString_GET_SIZE(ostr));
+ if (ostr != arg)
+ {
+ Py_DECREF(ostr);
+ }
+
+ Py_DECREF(arg);
+ }
+
+ Py_DECREF(iterator);
+ self->client->getFlush();
+
+ PyObject *odict = PyDict_New();
+
+ bool bError = false;
+
+ while (self->client->getReadNext(&pKey, &cbKey, &pData, &cbData, &flags, &cas, &bError))
+ {
+ PyObject *okey = PyString_FromStringAndSize(pKey, cbKey);
+ PyObject *otuple = PyTuple_New(2);
+ PyObject *ovalue = PyString_FromStringAndSize(pData, cbData);
+ PyObject *oflags = PyInt_FromLong(flags);
+
+ PyTuple_SET_ITEM(otuple, 0, ovalue);
+ PyTuple_SET_ITEM(otuple, 1, oflags);
+ PyDict_SetItem (odict, okey, otuple);
+
+ Py_DECREF(otuple);
+ Py_DECREF(okey);
+ }
+
+ if (bError)
+ {
+ Py_DECREF(odict);
+
+ if (!PyErr_Occurred())
+ {
+ return PyErr_Format(PyExc_RuntimeError, "umemcached: %s", self->client->getError());
+ }
+
+ return NULL;
+ }
+
+ return odict;
+}
+
+PyObject *Client_gets_multi(PyClient *self, PyObject *okeys)
+{
+ //[ ] def gets_multi(self, keys):
+
+ char *pKey;
+ size_t cbKey;
+ char *pData;
+ size_t cbData;
+ UINT64 cas;
+ int flags;
+
+ self->client->getBegin();
+
+ PyObject *iterator = PyObject_GetIter(okeys);
+
+ if (iterator == NULL)
+ {
+ return NULL;
+ }
+
+ PyObject *arg;
+
+ while ( (arg = PyIter_Next(iterator)))
+ {
+ PyObject *ostr;
+
+ if (PyString_Check(arg))
+ {
+ ostr = arg;
+ }
+ else
+ {
+ ostr = PyObject_Str(arg);
+ }
+
+ self->client->getKeyWrite(PyString_AS_STRING(ostr), PyString_GET_SIZE(ostr));
+ if (ostr != arg)
+ {
+ Py_DECREF(ostr);
+ }
+
+ Py_DECREF(arg);
+ }
+
+ Py_DECREF(iterator);
+ self->client->getFlush();
+
+ PyObject *odict = PyDict_New();
+
+ bool bError = false;
+
+ while (self->client->getReadNext(&pKey, &cbKey, &pData, &cbData, &flags, &cas, &bError))
+ {
+ PyObject *okey = PyString_FromStringAndSize(pKey, cbKey);
+ PyObject *otuple = PyTuple_New(3);
+ PyObject *ovalue = PyString_FromStringAndSize(pData, cbData);
+ PyObject *oflags = PyInt_FromLong(flags);
+ PyObject *ocas = PyLong_FromUnsignedLongLong(cas);
+
+ PyTuple_SET_ITEM(otuple, 0, ovalue);
+ PyTuple_SET_ITEM(otuple, 1, oflags);
+ PyTuple_SET_ITEM(otuple, 2, ocas);
+ PyDict_SetItem (odict, okey, otuple);
+
+ Py_DECREF(otuple);
+ Py_DECREF(okey);
+ }
+
+ if (bError)
+ {
+ Py_DECREF(odict);
+
+ if (!PyErr_Occurred())
+ {
+ return PyErr_Format(PyExc_RuntimeError, "umemcached: %s", self->client->getError());
+ }
+
+ return NULL;
+ }
+
+ return odict;
+}
+
+PyObject *Client_delete(PyClient *self, PyObject *args)
+{
+ char *pResult;
+ size_t cbResult;
+ char *pKey;
+ size_t cbKey;
+ int expire = -1;
+ int flags = 0;
+ int async = 0;
+
+ if (!PyArg_ParseTuple (args, "s#|ib", &pKey, &cbKey, &expire, &async))
+ {
+ return NULL;
+ }
+
+ time_t tsExpire = expire;
+
+ if (!self->client->del(pKey, cbKey, (tsExpire == -1) ? NULL : (time_t *) &tsExpire, async ? true : false))
+ {
+ if (!PyErr_Occurred())
+ {
+ return PyErr_Format(PyExc_RuntimeError, "umemcached: %s", self->client->getError());
+ }
+
+ return NULL;
+ }
+
+ if (!async)
+ {
+ if (self->client->getResult(&pResult, &cbResult))
+ {
+ return PyString_FromStringAndSize(pResult, cbResult);
+ }
+ else
+ {
+ return PyErr_Format(PyExc_RuntimeError, "Could not retrieve result");
+ }
+ }
+
+ Py_RETURN_NONE;
+}
+
+
+PyObject *Client_cas(PyClient *self, PyObject *args)
+{
+ //[ ] def cas(self, key, data, cas_unique, expiration = 0, flags = 0, async = False):
+
+ char *pResult;
+ size_t cbResult;
+ char *pKey;
+ size_t cbKey;
+ char *pData;
+ size_t cbData;
+ int expire = 0;
+ int flags = 0;
+ int async = 0;
+
+ UINT64 cas;
+
+ if (!PyArg_ParseTuple (args, "s#s#K|iib", &pKey, &cbKey, &pData, &cbData, &cas, &expire, &flags, &async))
+ {
+ return NULL;
+ }
+
+ if (!self->client->cas(pKey, cbKey, cas, pData, cbData, expire, flags, async ? true : false))
+ {
+ if (!PyErr_Occurred())
+ {
+ return PyErr_Format(PyExc_RuntimeError, "umemcached: %s", self->client->getError());
+ }
+
+ return NULL;
+ }
+
+ if (!async)
+ {
+ if (self->client->getResult(&pResult, &cbResult))
+ {
+ return PyString_FromStringAndSize(pResult, cbResult);
+ }
+ else
+ {
+ return PyErr_Format(PyExc_RuntimeError, "Could not retrieve result");
+ }
+ }
+
+ Py_RETURN_NONE;
+}
+
+
+PyObject *Client_incr(PyClient *self, PyObject *args)
+{
+ // def incr(self, key, increment, async = False):
+ char *pResult;
+ size_t cbResult;
+ char *pKey;
+ size_t cbKey;
+ int async = 0;
+
+ UINT64 increment;
+
+ if (!PyArg_ParseTuple (args, "s#K|b", &pKey, &cbKey, &increment, &async))
+ {
+ return NULL;
+ }
+
+ if (!self->client->incr(pKey, cbKey, increment, async ? true : false))
+ {
+ if (!PyErr_Occurred())
+ {
+ return PyErr_Format(PyExc_RuntimeError, "umemcached: %s", self->client->getError());
+ }
+
+ return NULL;
+ }
+
+ if (!async)
+ {
+ if (self->client->getResult(&pResult, &cbResult))
+ {
+ pResult[cbResult] = '\0';
+
+ if (strncmp (pResult, "CLIENT_ERROR", 12) == 0)
+ {
+ return PyErr_Format(PyExc_RuntimeError, pResult);
+ }
+
+ return PyString_FromStringAndSize(pResult, cbResult);
+ }
+ else
+ {
+ return PyErr_Format(PyExc_RuntimeError, "Could not retrieve result");
+ }
+ }
+
+ Py_RETURN_NONE;
+}
+
+PyObject *Client_decr(PyClient *self, PyObject *args)
+{
+ // def incr(self, key, increment, async = False):
+ char *pResult;
+ size_t cbResult;
+ char *pKey;
+ size_t cbKey;
+ int async = 0;
+
+ UINT64 decrement;
+
+ if (!PyArg_ParseTuple (args, "s#K|b", &pKey, &cbKey, &decrement, &async))
+ {
+ return NULL;
+ }
+
+ if (!self->client->decr(pKey, cbKey, decrement, async ? true : false))
+ {
+ if (!PyErr_Occurred())
+ {
+ return PyErr_Format(PyExc_RuntimeError, "umemcached: %s", self->client->getError());
+ }
+
+ return NULL;
+ }
+
+ if (!async)
+ {
+ if (self->client->getResult(&pResult, &cbResult))
+ {
+ pResult[cbResult] = '\0';
+
+ if (strncmp (pResult, "CLIENT_ERROR", 12) == 0)
+ {
+ return PyErr_Format(PyExc_RuntimeError, pResult);
+ }
+
+ return PyString_FromStringAndSize(pResult, cbResult);
+ }
+ else
+ {
+ return PyErr_Format(PyExc_RuntimeError, "Could not retrieve result");
+ }
+ }
+
+ Py_RETURN_NONE;
+}
+
+PyObject *Client_version(PyClient *self, PyObject *args)
+{
+ char *pVersion;
+ size_t cbVersion;
+
+ if (!self->client->version(&pVersion, &cbVersion))
+ {
+ return PyErr_Format(PyExc_RuntimeError, "Could not retrieve version");
+ }
+
+ return PyString_FromStringAndSize(pVersion, cbVersion);
+}
+
+PyObject *Client_stats(PyClient *self, PyObject *args)
+{
+ char *pName;
+ char *pValue;
+ size_t cbName;
+ size_t cbValue;
+
+ if (!self->client->stats(NULL, 0))
+ {
+ return PyErr_Format(PyExc_RuntimeError, "Stats command failed");
+ }
+
+ PyObject *odict = PyDict_New();
+
+ while (self->client->getStats(&pName, &cbName, &pValue, &cbValue))
+ {
+ PyObject *oname = PyString_FromStringAndSize(pName, cbName);
+ PyObject *ovalue = PyString_FromStringAndSize(pValue, cbValue);
+
+ PyDict_SetItem (odict, oname, ovalue);
+ }
+
+ return odict;
+}
+
+
+
+
+
+/*
+[X] def disconnect(self):
+[X] def is_connected(self):
+[X] def close(self):
+[X] def set(self, key, data, expiration = 0, flags = 0, async = False):
+[X] def add(self, key, data, expiration = 0, flags = 0, async = False):
+[X] def replace(self, key, data, expiration = 0, flags = 0, async = False):
+[X] def append(self, key, data, expiration = 0, flags = 0, async = False):
+[X] def prepend(self, key, data, expiration = 0, flags = 0, async = False):
+[X] def delete(self, key, expiration = 0, async = False):
+[x] def get(self, key, default = None):
+[X] def gets(self, key, default = None):
+[x] def get_multi(self, keys):
+[X] def gets_multi(self, keys):
+[X] def cas(self, key, data, cas_unique, expiration = 0, flags = 0, async = False):
+[X] def incr(self, key, increment, async = False):
+[X] def decr(self, key, increment, async = False):
+[X] def getr(self, key, default = None):
+
+[X] def version(self):
+[X] def stats(self):
+
+*/
+
+
+static PyMethodDef Client_methods[] = {
+ {"connect", (PyCFunction) Client_connect, METH_NOARGS, ""},
+ {"is_connected", (PyCFunction) Client_is_connected, METH_NOARGS, ""},
+ {"disconnect", (PyCFunction) Client_disconnect, METH_NOARGS, ""},
+ {"close", (PyCFunction) Client_disconnect, METH_NOARGS, ""},
+ {"set", (PyCFunction) Client_set, METH_VARARGS, "def set(self, key, data, expiration = 0, flags = 0, async = False)"},
+ {"get", (PyCFunction) Client_get, METH_VARARGS, "def get(self, key, default = None)"},
+ {"gets", (PyCFunction) Client_gets, METH_VARARGS, "def gets(self, key, default = None)"},
+ {"get_multi", (PyCFunction) Client_get_multi, METH_O, "def get_multi(self, keys)"},
+ {"gets_multi", (PyCFunction) Client_gets_multi, METH_O, "def gets_multi(self, keys)"},
+ {"add", (PyCFunction) Client_add, METH_VARARGS, "def add(self, key, data, expiration = 0, flags = 0, async = False)"},
+ {"replace", (PyCFunction) Client_replace, METH_VARARGS, "def replace(self, key, data, expiration = 0, flags = 0, async = False)"},
+ {"append", (PyCFunction) Client_append, METH_VARARGS, "def append(self, key, data, expiration = 0, flags = 0, async = False)"},
+ {"prepend", (PyCFunction) Client_prepend, METH_VARARGS, "def prepend(self, key, data, expiration = 0, flags = 0, async = False)"},
+ {"delete", (PyCFunction) Client_delete, METH_VARARGS, "def delete(self, key, expiration = 0, async = False)"},
+ {"cas", (PyCFunction) Client_cas, METH_VARARGS, "def cas(self, key, data, cas_unique, expiration = 0, flags = 0, async = False)"},
+ {"incr", (PyCFunction) Client_incr, METH_VARARGS, "def incr(self, key, increment, async = False)"},
+ {"decr", (PyCFunction) Client_decr, METH_VARARGS, "def decr(self, key, decrement, async = False)"},
+ {"version", (PyCFunction) Client_version, METH_NOARGS, "def version(self)"},
+ {"stats", (PyCFunction) Client_stats, METH_NOARGS, "def stats(self)"},
+ {NULL}
+};
+
+static PyTypeObject ClientType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "umemcached.Client", /* tp_name */
+ sizeof(PyClient), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ (destructor) Client_Destructor, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ 0, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ "", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ Client_methods, /* tp_methods */
+ NULL, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)Client_init, /* tp_init */
+};
+
+static PyMethodDef methods[] = {
+ {NULL, NULL, 0, NULL} /* Sentinel */
+};
+
+
+PyMODINIT_FUNC
+initumemcached(void)
+{
+ PyObject* m;
+
+ m = Py_InitModule3("umemcached", methods,
+ "");
+ if (m == NULL)
+ return;
+
+ ClientType.tp_new = PyType_GenericNew;
+ if (PyType_Ready(&ClientType) < 0)
+ return;
+ Py_INCREF(&ClientType);
+ PyModule_AddObject(m, "Client", (PyObject *)&ClientType);
+}
View
61 socketdefs.h
@@ -0,0 +1,61 @@
+#ifndef __SOCKETDEFS_H__
+#define __SOCKETDEFS_H__
+
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <winsock2.h>
+#include <mswsock.h>
+#include <Ws2tcpip.h>
+typedef int socklen_t;
+#define SocketClose(_fd) closesocket((_fd))
+#define SocketWouldBlock(_fd) (WSAGetLastError () == WSAEWOULDBLOCK)
+#define SocketEINPROGRESS(_fd) (WSAGetLastError () == WSAEINPROGRESS)
+
+#define SocketGetLastError() ((int) WSAGetLastError ())
+
+#define SocketSetNonBlock(_fd, _state) \
+{ \
+ unsigned long flags = (_state) ? 1 : 0; \
+ ioctlsocket((_fd), FIONBIO, &flags); \
+} \
+
+#else
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/select.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <errno.h>
+typedef int SOCKET;
+#define SocketClose(_fd) close((_fd))
+#define SocketWouldBlock(_fd) (errno == EAGAIN)
+#define SocketEINPROGRESS(_fd) (errno == EINPROGRESS)
+#define SocketSetNonBlock(_fd, _state) \
+{ \
+ if ((_state) \
+ fcntl ((_fd), F_SETFL, O_NONBLOCK); \
+ else \
+ fcntl ((_fd), F_SETFL, 0); \
+} \
+
+#define SocketGetLastError() (errno)
+
+#endif
+
+#ifndef MSG_NOSIGNAL
+#define MSG_NOSIGNAL 0
+#endif
+
+
+
+#endif
Please sign in to comment.
Something went wrong with that request. Please try again.