]> granicus.if.org Git - python/commitdiff
Support for buffer protocol for socket and struct.
authorMartin Blais <blais@furius.ca>
Fri, 26 May 2006 12:03:27 +0000 (12:03 +0000)
committerMartin Blais <blais@furius.ca>
Fri, 26 May 2006 12:03:27 +0000 (12:03 +0000)
* Added socket.recv_buf() and socket.recvfrom_buf() methods, that use the buffer
  protocol (send and sendto already did).

* Added struct.pack_to(), that is the corresponding buffer compatible method to
  unpack_from().

* Fixed minor typos in arraymodule.

Lib/socket.py
Lib/test/test_socket.py
Lib/test/test_struct.py
Modules/_struct.c
Modules/arraymodule.c
Modules/socketmodule.c

index 32a92b4535452570a0218127cefc6fe91932c534..cc5e65e166bf1d1d250275062c71b269a7d0e190 100644 (file)
@@ -140,7 +140,9 @@ class _socketobject(object):
 
     __doc__ = _realsocket.__doc__
 
-    __slots__ = ["_sock", "send", "recv", "sendto", "recvfrom",
+    __slots__ = ["_sock",
+                 "recv", "recv_buf", "recvfrom_buf",
+                 "send", "sendto", "recvfrom",
                  "__weakref__"]
 
     def __init__(self, family=AF_INET, type=SOCK_STREAM, proto=0, _sock=None):
@@ -149,8 +151,10 @@ class _socketobject(object):
         self._sock = _sock
         self.send = self._sock.send
         self.recv = self._sock.recv
+        self.recv_buf = self._sock.recv_buf
         self.sendto = self._sock.sendto
         self.recvfrom = self._sock.recvfrom
+        self.recvfrom_buf = self._sock.recvfrom_buf
 
     def close(self):
         self._sock = _closedsocket()
index 6943080a71c00abf9dfb624add3deabcc1e4e5a1..2246fb6e4ce5c6d4fba7b35515fc0d6e8496b771 100644 (file)
@@ -9,6 +9,7 @@ import time
 import thread, threading
 import Queue
 import sys
+import array
 from weakref import proxy
 
 PORT = 50007
@@ -852,8 +853,38 @@ class TestLinuxAbstractNamespace(unittest.TestCase):
         self.assertRaises(socket.error, s.bind, address)
 
 
+class BufferIOTest(SocketConnectedTest):
+    """
+    Test the buffer versions of socket.recv() and socket.send().
+    """
+    def __init__(self, methodName='runTest'):
+        SocketConnectedTest.__init__(self, methodName=methodName)
+
+    def testRecvBuf(self):
+        buf = array.array('c', ' '*1024)
+        nbytes = self.cli_conn.recv_buf(buf)
+        self.assertEqual(nbytes, len(MSG))
+        msg = buf.tostring()[:len(MSG)]
+        self.assertEqual(msg, MSG)
+
+    def _testRecvBuf(self):
+        buf = buffer(MSG)
+        self.serv_conn.send(buf)
+
+    def testRecvFromBuf(self):
+        buf = array.array('c', ' '*1024)
+        nbytes, addr = self.cli_conn.recvfrom_buf(buf)
+        self.assertEqual(nbytes, len(MSG))
+        msg = buf.tostring()[:len(MSG)]
+        self.assertEqual(msg, MSG)
+
+    def _testRecvFromBuf(self):
+        buf = buffer(MSG)
+        self.serv_conn.send(buf)
+
 def test_main():
-    tests = [GeneralModuleTests, BasicTCPTest, TCPTimeoutTest, TestExceptions]
+    tests = [GeneralModuleTests, BasicTCPTest, TCPTimeoutTest, TestExceptions,
+             BufferIOTest]
     if sys.platform != 'mac':
         tests.extend([ BasicUDPTest, UDPTimeoutTest ])
 
index 40fbde1129af506c38f3bfbaf8bbfbdf16ac7803..1420a08ce2d363bc20c9ea23daaea8d1321cdc7d 100644 (file)
@@ -1,5 +1,8 @@
 from test.test_support import TestFailed, verbose, verify
+import test.test_support
 import struct
+import array
+import unittest
 
 import sys
 ISBIGENDIAN = sys.byteorder == "big"
@@ -438,31 +441,6 @@ def test_705836():
 
 test_705836()
 
-def test_unpack_from():
-    test_string = 'abcd01234'
-    fmt = '4s'
-    s = struct.Struct(fmt)
-    for cls in (str, buffer):
-        data = cls(test_string)
-        assert s.unpack_from(data) == ('abcd',)
-        assert s.unpack_from(data, 2) == ('cd01',)
-        assert s.unpack_from(data, 4) == ('0123',)
-        for i in xrange(6):
-            assert s.unpack_from(data, i) == (data[i:i+4],)
-        for i in xrange(6, len(test_string) + 1):
-            simple_err(s.unpack_from, data, i)
-    for cls in (str, buffer):
-        data = cls(test_string)
-        assert struct.unpack_from(fmt, data) == ('abcd',)
-        assert struct.unpack_from(fmt, data, 2) == ('cd01',)
-        assert struct.unpack_from(fmt, data, 4) == ('0123',)
-        for i in xrange(6):
-            assert struct.unpack_from(fmt, data, i) == (data[i:i+4],)
-        for i in xrange(6, len(test_string) + 1):
-            simple_err(struct.unpack_from, fmt, data, i)
-
-test_unpack_from()
-
 def test_1229380():
     for endian in ('', '>', '<'):
         for cls in (int, long):
@@ -478,3 +456,60 @@ def test_1229380():
 if 0:
     # TODO: bug #1229380
     test_1229380()
+
+class PackBufferTestCase(unittest.TestCase):
+    """
+    Test the packing methods that work on buffers.
+    """
+
+    def test_unpack_from( self ):
+        test_string = 'abcd01234'
+        fmt = '4s'
+        s = struct.Struct(fmt)
+        for cls in (str, buffer):
+            data = cls(test_string)
+            self.assertEquals(s.unpack_from(data), ('abcd',))
+            self.assertEquals(s.unpack_from(data, 2), ('cd01',))
+            self.assertEquals(s.unpack_from(data, 4), ('0123',))
+            for i in xrange(6):
+                self.assertEquals(s.unpack_from(data, i), (data[i:i+4],))
+            for i in xrange(6, len(test_string) + 1):
+                simple_err(s.unpack_from, data, i)
+        for cls in (str, buffer):
+            data = cls(test_string)
+            self.assertEquals(struct.unpack_from(fmt, data), ('abcd',))
+            self.assertEquals(struct.unpack_from(fmt, data, 2), ('cd01',))
+            self.assertEquals(struct.unpack_from(fmt, data, 4), ('0123',))
+            for i in xrange(6):
+                self.assertEquals(struct.unpack_from(fmt, data, i),
+                                  (data[i:i+4],))
+            for i in xrange(6, len(test_string) + 1):
+                simple_err(struct.unpack_from, fmt, data, i)
+
+    def test_pack_to( self ):
+        test_string = 'Reykjavik rocks, eow!'
+        writable_buf = array.array('c', ' '*100)
+        fmt = '21s'
+        s = struct.Struct(fmt)
+
+        # Test without offset
+        s.pack_to(writable_buf, 0, test_string)
+        from_buf = writable_buf.tostring()[:len(test_string)]
+        self.assertEquals(from_buf, test_string)
+
+        # Test with offset.
+        s.pack_to(writable_buf, 10, test_string)
+        from_buf = writable_buf.tostring()[:len(test_string)+10]
+        self.assertEquals(from_buf, (test_string[:10] + test_string))
+
+        # Go beyond boundaries.
+        small_buf = array.array('c', ' '*10)
+        self.assertRaises(struct.error, s.pack_to, small_buf, 0, test_string)
+        self.assertRaises(struct.error, s.pack_to, small_buf, 2, test_string)
+
+def test_main():
+    test.test_support.run_unittest(PackBufferTestCase)
+    
+if __name__ == "__main__":
+    test_main()
+
index fb3e497af27f8010cf202934b9a798925338aba5..1ae2320a5ef7043c39fe1783497cdb38de4bd150 100644 (file)
@@ -15,6 +15,7 @@ static PyTypeObject PyStructType;
 typedef int Py_ssize_t;
 #endif
 
+
 /* PY_USE_INT_WHEN_POSSIBLE is an experimental flag that changes the 
    struct API to return int instead of long when possible. This is
    often a significant performance improvement. */
@@ -24,7 +25,6 @@ typedef int Py_ssize_t;
 
 
 /* The translation function for each format character is table driven */
-
 typedef struct _formatdef {
        char format;
        int size;
@@ -1315,50 +1315,36 @@ s_unpack_from(PyObject *self, PyObject *args, PyObject *kwds)
        return s_unpack_internal(soself, buffer + offset);
 }
 
-PyDoc_STRVAR(s_pack__doc__,
-"pack(v1, v2, ...) -> string\n\
-\n\
-Return a string containing values v1, v2, ... packed according to this\n\
-Struct's format. See struct.__doc__ for more on format strings.");
 
-static PyObject *
-s_pack(PyObject *self, PyObject *args)
+/*
+ * Guts of the pack function.
+ *
+ * Takes a struct object, a tuple of arguments, and offset in that tuple of
+ * argument for where to start processing the arguments for packing, and a
+ * character buffer for writing the packed string.  The caller must insure
+ * that the buffer may contain the required length for packing the arguments.
+ * 0 is returned on success, 1 is returned if there is an error.
+ *
+ */
+static int
+s_pack_internal(PyStructObject *soself, PyObject *args, int offset, char* buf)
 {
-       PyStructObject *soself;
-       PyObject *result;
-       char *restart;
        formatcode *code;
        Py_ssize_t i;
-       
-       soself = (PyStructObject *)self;
-       assert(PyStruct_Check(self));
-       assert(soself->s_codes != NULL);
-       if (args == NULL || !PyTuple_Check(args) ||
-           PyTuple_GET_SIZE(args) != soself->s_len)
-       {
-               PyErr_Format(StructError,
-                       "pack requires exactly %d arguments", soself->s_len);
-               return NULL;
-       }
-       
-       result = PyString_FromStringAndSize((char *)NULL, soself->s_size);
-       if (result == NULL)
-               return NULL;
-       
-       restart = PyString_AS_STRING(result);
-       memset(restart, '\0', soself->s_size);
-       i = 0;
+
+       memset(buf, '\0', soself->s_size);
+       i = offset;
        for (code = soself->s_codes; code->fmtdef != NULL; code++) {
                Py_ssize_t n;
                PyObject *v;
                const formatdef *e = code->fmtdef;
-               char *res = restart + code->offset;
+               char *res = buf + code->offset;
                if (e->format == 's') {
                        v = PyTuple_GET_ITEM(args, i++);
                        if (!PyString_Check(v)) {
                                PyErr_SetString(StructError,
                                                "argument for 's' must be a string");
-                               goto fail;
+                               return -1;
                        }
                        n = PyString_GET_SIZE(v);
                        if (n > code->size)
@@ -1370,7 +1356,7 @@ s_pack(PyObject *self, PyObject *args)
                        if (!PyString_Check(v)) {
                                PyErr_SetString(StructError,
                                                "argument for 'p' must be a string");
-                               goto fail;
+                               return -1;
                        }
                        n = PyString_GET_SIZE(v);
                        if (n > (code->size - 1))
@@ -1383,16 +1369,109 @@ s_pack(PyObject *self, PyObject *args)
                } else {
                        v = PyTuple_GET_ITEM(args, i++);
                        if (e->pack(res, v, e) < 0)
-                               goto fail;
+                               return -1;
                }
        }
        
+       /* Success */
+       return 0;
+}
+
+
+PyDoc_STRVAR(s_pack__doc__,
+"pack(v1, v2, ...) -> string\n\
+\n\
+Return a string containing values v1, v2, ... packed according to this\n\
+Struct's format. See struct.__doc__ for more on format strings.");
+
+static PyObject *
+s_pack(PyObject *self, PyObject *args)
+{
+       PyStructObject *soself;
+       PyObject *result;
+
+       /* Validate arguments. */
+       soself = (PyStructObject *)self;
+       assert(PyStruct_Check(self));
+       assert(soself->s_codes != NULL);
+       if (args == NULL || !PyTuple_Check(args) ||
+           PyTuple_GET_SIZE(args) != soself->s_len)
+       {
+               PyErr_Format(StructError,
+                       "pack requires exactly %d arguments", soself->s_len);
+               return NULL;
+       }
+       
+       /* Allocate a new string */
+       result = PyString_FromStringAndSize((char *)NULL, soself->s_size);
+       if (result == NULL)
+               return NULL;
+       
+       /* Call the guts */
+       if ( s_pack_internal(soself, args, 0, PyString_AS_STRING(result)) != 0 ) {
+               Py_DECREF(result);
+               return NULL;
+       }
+
        return result;
+}
 
-fail:
-       Py_DECREF(result);
-       return NULL;
+PyDoc_STRVAR(s_pack_to__doc__,
+"pack_to(buffer, offset, v1, v2, ...)\n\
+\n\
+Pack the values v2, v2, ... according to this Struct's format, write \n\
+the packed bytes into the given buffer at the given offset.  Note that \n\
+the offset is not an optional argument.  See struct.__doc__ for \n\
+more on format strings.");
+
+static PyObject *
+s_pack_to(PyObject *self, PyObject *args)
+{
+       PyStructObject *soself;
+       char *buffer;
+       Py_ssize_t buffer_len, offset;
+
+       /* Validate arguments.  +1 is for the first arg as buffer. */
+       soself = (PyStructObject *)self;
+       assert(PyStruct_Check(self));
+       assert(soself->s_codes != NULL);
+       if (args == NULL || !PyTuple_Check(args) ||
+           PyTuple_GET_SIZE(args) != (soself->s_len + 2))
+       {
+               PyErr_Format(StructError,
+                            "pack_to requires exactly %d arguments", 
+                            (soself->s_len + 2));
+               return NULL;
+       }
+
+       /* Extract a writable memory buffer from the first argument */
+        if ( PyObject_AsWriteBuffer(PyTuple_GET_ITEM(args, 0), 
+                                    (void**)&buffer, &buffer_len) == -1 ) { 
+               return NULL;
+        }
+        assert( buffer_len >= 0 );
+
+       /* Extract the offset from the first argument */
+       offset = PyInt_AsLong(PyTuple_GET_ITEM(args, 1));
+
+       /* Support negative offsets. */
+       if (offset < 0)
+               offset += buffer_len;
+
+       /* Check boundaries */
+       if (offset < 0 || (buffer_len - offset) < soself->s_size) {
+               PyErr_Format(StructError,
+                            "pack_to requires a buffer of at least %d bytes",
+                            soself->s_size);
+               return NULL;
+       }
        
+       /* Call the guts */
+       if ( s_pack_internal(soself, args, 2, buffer + offset) != 0 ) {
+               return NULL;
+       }
+
+       return Py_None;
 }
 
 
@@ -1400,6 +1479,7 @@ fail:
 
 static struct PyMethodDef s_methods[] = {
        {"pack",        (PyCFunction)s_pack,            METH_VARARGS, s_pack__doc__},
+       {"pack_to",     (PyCFunction)s_pack_to,         METH_VARARGS, s_pack_to__doc__}, 
        {"unpack",      (PyCFunction)s_unpack,          METH_O, s_unpack__doc__},
        {"unpack_from", (PyCFunction)s_unpack_from,     METH_KEYWORDS, s_unpack_from__doc__},
        {NULL,   NULL}          /* sentinel */
index 52a7f5ee052022c9c50c50c94cce5ddea1d6537d..af1276949c8434da9f408e7ea62a41d5a77cfc26 100644 (file)
@@ -1975,9 +1975,9 @@ static PyTypeObject Arraytype = {
        0,                                      /* tp_setattr */
        0,                                      /* tp_compare */
        (reprfunc)array_repr,                   /* tp_repr */
-       0,                                      /* tp_as _number*/
-       &array_as_sequence,                     /* tp_as _sequence*/
-       &array_as_mapping,                      /* tp_as _mapping*/
+       0,                                      /* tp_as_number*/
+       &array_as_sequence,                     /* tp_as_sequence*/
+       &array_as_mapping,                      /* tp_as_mapping*/
        0,                                      /* tp_hash */
        0,                                      /* tp_call */
        0,                                      /* tp_str */
index b77f41e9b248158688e849d7e34e968898b262ea..164a5d12c546f52a9f21907abec45b06a5e2efb5 100644 (file)
@@ -104,7 +104,10 @@ gettimeout() -- return timeout or None\n\
 listen(n) -- start listening for incoming connections\n\
 makefile([mode, [bufsize]]) -- return a file object for the socket [*]\n\
 recv(buflen[, flags]) -- receive data\n\
-recvfrom(buflen[, flags]) -- receive data and sender's address\n\
+recv_buf(buffer[, nbytes[, flags]]) -- receive data (into a buffer)\n\
+recvfrom(buflen[, flags]) -- receive data and sender\'s address\n\
+recvfrom_buf(buffer[, nbytes, [, flags])\n\
+  -- receive data and sender\'s address (into a buffer)\n\
 sendall(data[, flags]) -- send all data\n\
 send(data[, flags]) -- send data, may not send all of it\n\
 sendto(data[, flags], addr) -- send data to a given address\n\
@@ -205,7 +208,7 @@ shutdown(how) -- shut down traffic in one or both directions\n\
    functions are declared correctly if compiling with
    MIPSPro 7.x in ANSI C mode (default) */
 
-/* XXX Using _SGIAPI is the wrong thing, 
+/* XXX Using _SGIAPI is the wrong thing,
    but I don't know what the right thing is. */
 #undef _SGIAPI /* to avoid warning */
 #define _SGIAPI 1
@@ -223,8 +226,8 @@ shutdown(how) -- shut down traffic in one or both directions\n\
 #include <netdb.h>
 #endif
 
-/* Irix 6.5 fails to define this variable at all. This is needed 
-   for both GCC and SGI's compiler. I'd say that the SGI headers 
+/* Irix 6.5 fails to define this variable at all. This is needed
+   for both GCC and SGI's compiler. I'd say that the SGI headers
    are just busted. Same thing for Solaris. */
 #if (defined(__sgi) || defined(sun)) && !defined(INET_ADDRSTRLEN)
 #define INET_ADDRSTRLEN 16
@@ -1194,10 +1197,10 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args,
                                args->ob_type->tp_name);
                        return 0;
                }
-               if (!PyArg_ParseTuple(args, "eti:getsockaddrarg", 
+               if (!PyArg_ParseTuple(args, "eti:getsockaddrarg",
                                      "idna", &host, &port))
                        return 0;
-                result = setipaddr(host, (struct sockaddr *)addr, 
+                result = setipaddr(host, (struct sockaddr *)addr,
                                    sizeof(*addr),  AF_INET);
                 PyMem_Free(host);
                 if (result < 0)
@@ -1225,12 +1228,12 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args,
                                args->ob_type->tp_name);
                        return 0;
                }
-               if (!PyArg_ParseTuple(args, "eti|ii", 
+               if (!PyArg_ParseTuple(args, "eti|ii",
                                      "idna", &host, &port, &flowinfo,
                                      &scope_id)) {
                        return 0;
                }
-                result = setipaddr(host, (struct sockaddr *)addr,  
+                result = setipaddr(host, (struct sockaddr *)addr,
                                    sizeof(*addr), AF_INET6);
                 PyMem_Free(host);
                 if (result < 0)
@@ -1839,7 +1842,7 @@ internal_connect(PySocketSockObject *s, struct sockaddr *addr, int addrlen,
                                        int res_size = sizeof res;
                                        /* It must be in the exception set */
                                        assert(FD_ISSET(s->sock_fd, &fds_exc));
-                                       if (0 == getsockopt(s->sock_fd, SOL_SOCKET, SO_ERROR, 
+                                       if (0 == getsockopt(s->sock_fd, SOL_SOCKET, SO_ERROR,
                                                            (char *)&res, &res_size))
                                                /* getsockopt also clears WSAGetLastError,
                                                   so reset it back. */
@@ -2135,95 +2138,126 @@ The mode and buffersize arguments are as for the built-in open() function.");
 
 #endif /* NO_DUP */
 
-
-/* s.recv(nbytes [,flags]) method */
-
-static PyObject *
-sock_recv(PySocketSockObject *s, PyObject *args)
+/*
+ * This is the guts of the recv() and recv_buf() methods, which reads into a
+ * char buffer.  If you have any inc/def ref to do to the objects that contain
+ * the buffer, do it in the caller.  This function returns the number of bytes
+ * succesfully read.  If there was an error, it returns -1.  Note that it is
+ * also possible that we return a number of bytes smaller than the request
+ * bytes.
+ */
+static int
+sock_recv_guts(PySocketSockObject *s, char* cbuf, int len, int flags)
 {
-       int len, n = 0, flags = 0, timeout;
-       PyObject *buf;
+        int timeout, outlen = 0;
 #ifdef __VMS
-       int read_length;
+       int remaining, nread;
        char *read_buf;
 #endif
 
-       if (!PyArg_ParseTuple(args, "i|i:recv", &len, &flags))
-               return NULL;
-
-       if (len < 0) {
-               PyErr_SetString(PyExc_ValueError,
-                               "negative buffersize in recv");
-               return NULL;
+       if (!IS_SELECTABLE(s)) {
+               select_error();
+               return -1;
        }
 
-       buf = PyString_FromStringAndSize((char *) 0, len);
-       if (buf == NULL)
-               return NULL;
-
-       if (!IS_SELECTABLE(s))
-               return select_error();
-
 #ifndef __VMS
        Py_BEGIN_ALLOW_THREADS
        timeout = internal_select(s, 0);
        if (!timeout)
-               n = recv(s->sock_fd, PyString_AS_STRING(buf), len, flags);
+               outlen = recv(s->sock_fd, cbuf, len, flags);
        Py_END_ALLOW_THREADS
 
        if (timeout) {
-               Py_DECREF(buf);
                PyErr_SetString(socket_timeout, "timed out");
-               return NULL;
+               return -1;
        }
-       if (n < 0) {
-               Py_DECREF(buf);
-               return s->errorhandler();
+       if (outlen < 0) {
+               /* Note: the call to errorhandler() ALWAYS indirectly returned
+                  NULL, so ignore its return value */
+               s->errorhandler();
+               return -1;
        }
-       if (n != len)
-               _PyString_Resize(&buf, n);
 #else
-       read_buf = PyString_AsString(buf);
-       read_length = len;
-       while (read_length != 0) {
+       read_buf = cbuf;
+       remaining = len;
+       while (remaining != 0) {
                unsigned int segment;
 
-               segment = read_length /SEGMENT_SIZE;
+               segment = remaining /SEGMENT_SIZE;
                if (segment != 0) {
                        segment = SEGMENT_SIZE;
                }
                else {
-                       segment = read_length;
+                       segment = remaining;
                }
 
                Py_BEGIN_ALLOW_THREADS
                timeout = internal_select(s, 0);
                if (!timeout)
-                       n = recv(s->sock_fd, read_buf, segment, flags);
+                       nread = recv(s->sock_fd, read_buf, segment, flags);
                Py_END_ALLOW_THREADS
 
                if (timeout) {
-                       Py_DECREF(buf);
                        PyErr_SetString(socket_timeout, "timed out");
-                       return NULL;
+                       return -1;
                }
-               if (n < 0) {
-                       Py_DECREF(buf);
-                       return s->errorhandler();
+               if (nread < 0) {
+                       s->errorhandler();
+                       return -1;
                }
-               if (n != read_length) {
-                       read_buf += n;
+               if (nread != remaining) {
+                       read_buf += nread;
                        break;
                }
 
-               read_length -= segment;
+               remaining -= segment;
                read_buf += segment;
        }
-       if (_PyString_Resize(&buf, (read_buf - PyString_AsString(buf))) < 0)
-       {
-           return NULL;
-       }
+       outlen = read_buf - cbuf;
 #endif /* !__VMS */
+
+       return outlen;
+}
+
+
+/* s.recv(nbytes [,flags]) method */
+
+static PyObject *
+sock_recv(PySocketSockObject *s, PyObject *args)
+{
+       int recvlen, flags = 0, outlen;
+       PyObject *buf;
+
+       if (!PyArg_ParseTuple(args, "i|i:recv", &recvlen, &flags))
+               return NULL;
+
+       if (recvlen < 0) {
+               PyErr_SetString(PyExc_ValueError,
+                               "negative buffersize in recv");
+               return NULL;
+       }
+
+       /* Allocate a new string. */
+       buf = PyString_FromStringAndSize((char *) 0, recvlen);
+       if (buf == NULL)
+               return NULL;
+
+       /* Call the guts */
+       outlen = sock_recv_guts(s, PyString_AsString(buf), recvlen, flags);
+       if (outlen < 0) {
+               /* An error occured, release the string and return an
+                  error. */
+               Py_DECREF(buf);
+               return NULL;
+       }
+       if (outlen != recvlen) {
+               /* We did not read as many bytes as we anticipated, resize the
+                  string if possible and be succesful. */
+               if (_PyString_Resize(&buf, outlen) < 0)
+                       /* Oopsy, not so succesful after all. */
+                       return NULL;
+       }
+
        return buf;
 }
 
@@ -2236,29 +2270,90 @@ at least one byte is available or until the remote end is closed.  When\n\
 the remote end is closed and all data is read, return the empty string.");
 
 
-/* s.recvfrom(nbytes [,flags]) method */
+/* s.recv_buf(buffer, [nbytes [,flags]]) method */
 
-static PyObject *
-sock_recvfrom(PySocketSockObject *s, PyObject *args)
+static PyObject*
+sock_recv_buf(PySocketSockObject *s, PyObject *args, PyObject *kwds)
 {
-       sock_addr_t addrbuf;
-       PyObject *buf = NULL;
-       PyObject *addr = NULL;
-       PyObject *ret = NULL;
-       int len, n = 0, flags = 0, timeout;
-       socklen_t addrlen;
+       static char *kwlist[] = {"buffer", "nbytes", "flags", 0};
 
-       if (!PyArg_ParseTuple(args, "i|i:recvfrom", &len, &flags))
+       int recvlen = 0, flags = 0, readlen;
+       char *buf;
+       int buflen;
+
+       /* Get the buffer's memory */
+       if (!PyArg_ParseTupleAndKeywords(args, kwds, "s#|ii:recv", kwlist,
+                                        &buf, &buflen, &recvlen, &flags))
                return NULL;
+       assert(buf != 0 && buflen > 0);
 
-       if (!getsockaddrlen(s, &addrlen))
+       if (recvlen < 0) {
+               PyErr_SetString(PyExc_ValueError,
+                               "negative buffersize in recv");
                return NULL;
-       buf = PyString_FromStringAndSize((char *) 0, len);
-       if (buf == NULL)
+       }
+       if (recvlen == 0) {
+            /* If nbytes was not specified, use the buffer's length */
+            recvlen = buflen;
+       }
+
+       /* Check if the buffer is large enough */
+       if (buflen < recvlen) {
+               PyErr_SetString(PyExc_ValueError,
+                               "buffer too small for requested bytes");
                return NULL;
+       }
 
-       if (!IS_SELECTABLE(s))
-               return select_error();
+       /* Call the guts */
+       readlen = sock_recv_guts(s, buf, recvlen, flags);
+       if (readlen < 0) {
+               /* Return an error. */
+               return NULL;
+       }
+
+       /* Return the number of bytes read.  Note that we do not do anything
+          special here in the case that readlen < recvlen. */
+       return PyInt_FromLong(readlen);
+}
+
+PyDoc_STRVAR(recv_buf_doc,
+"recv_buf(buffer, [nbytes[, flags]]) -> nbytes_read\n\
+\n\
+A version of recv() that stores its data into a buffer rather than creating \n\
+a new string.  Receive up to buffersize bytes from the socket.  If buffersize \n\
+is not specified (or 0), receive up to the size available in the given buffer.\n\
+\n\
+See recv() for documentation about the flags.");
+
+
+/*
+ * This is the guts of the recv() and recv_buf() methods, which reads into a
+ * char buffer.  If you have any inc/def ref to do to the objects that contain
+ * the buffer, do it in the caller.  This function returns the number of bytes
+ * succesfully read.  If there was an error, it returns -1.  Note that it is
+ * also possible that we return a number of bytes smaller than the request
+ * bytes.
+ *
+ * 'addr' is a return value for the address object.  Note that you must decref
+ * it yourself.
+ */
+static int
+sock_recvfrom_guts(PySocketSockObject *s, char* cbuf, int len, int flags,
+                  PyObject** addr)
+{
+       sock_addr_t addrbuf;
+       int n = 0, timeout;
+       socklen_t addrlen;
+
+       *addr = NULL;
+
+       if (!getsockaddrlen(s, &addrlen))
+               return -1;
+
+       if (!IS_SELECTABLE(s)) {
+               select_error();
+               return -1;
+       }
 
        Py_BEGIN_ALLOW_THREADS
        memset(&addrbuf, 0, addrlen);
@@ -2266,41 +2361,71 @@ sock_recvfrom(PySocketSockObject *s, PyObject *args)
        if (!timeout) {
 #ifndef MS_WINDOWS
 #if defined(PYOS_OS2) && !defined(PYCC_GCC)
-               n = recvfrom(s->sock_fd, PyString_AS_STRING(buf), len, flags,
+               n = recvfrom(s->sock_fd, cbuf, len, flags,
                             (struct sockaddr *) &addrbuf, &addrlen);
 #else
-               n = recvfrom(s->sock_fd, PyString_AS_STRING(buf), len, flags,
+               n = recvfrom(s->sock_fd, cbuf, len, flags,
                             (void *) &addrbuf, &addrlen);
 #endif
 #else
-               n = recvfrom(s->sock_fd, PyString_AS_STRING(buf), len, flags,
+               n = recvfrom(s->sock_fd, cbuf, len, flags,
                             (struct sockaddr *) &addrbuf, &addrlen);
 #endif
        }
        Py_END_ALLOW_THREADS
 
        if (timeout) {
-               Py_DECREF(buf);
                PyErr_SetString(socket_timeout, "timed out");
-               return NULL;
+               return -1;
        }
        if (n < 0) {
-               Py_DECREF(buf);
-               return s->errorhandler();
+               s->errorhandler();
+                return -1;
        }
 
-       if (n != len && _PyString_Resize(&buf, n) < 0)
+       if (!(*addr = makesockaddr(s->sock_fd, (struct sockaddr *) &addrbuf,
+                                  addrlen, s->sock_proto)))
+               return -1;
+
+       return n;
+}
+
+/* s.recvfrom(nbytes [,flags]) method */
+
+static PyObject *
+sock_recvfrom(PySocketSockObject *s, PyObject *args)
+{
+       PyObject *buf = NULL;
+       PyObject *addr = NULL;
+       PyObject *ret = NULL;
+       int recvlen, outlen, flags = 0;
+
+       if (!PyArg_ParseTuple(args, "i|i:recvfrom", &recvlen, &flags))
+               return NULL;
+
+       buf = PyString_FromStringAndSize((char *) 0, recvlen);
+       if (buf == NULL)
                return NULL;
 
-       if (!(addr = makesockaddr(s->sock_fd, (struct sockaddr *) &addrbuf,
-                                 addrlen, s->sock_proto)))
+       outlen = sock_recvfrom_guts(s, PyString_AS_STRING(buf),
+                                   recvlen, flags, &addr);
+       if (outlen < 0) {
                goto finally;
+       }
+
+       if (outlen != recvlen) {
+               /* We did not read as many bytes as we anticipated, resize the
+                  string if possible and be succesful. */
+               if (_PyString_Resize(&buf, outlen) < 0)
+                       /* Oopsy, not so succesful after all. */
+                       goto finally;
+       }
 
        ret = PyTuple_Pack(2, buf, addr);
 
 finally:
-       Py_XDECREF(addr);
        Py_XDECREF(buf);
+       Py_XDECREF(addr);
        return ret;
 }
 
@@ -2309,6 +2434,57 @@ PyDoc_STRVAR(recvfrom_doc,
 \n\
 Like recv(buffersize, flags) but also return the sender's address info.");
 
+
+/* s.recvfrom_buf(buffer[, nbytes [,flags]]) method */
+
+static PyObject *
+sock_recvfrom_buf(PySocketSockObject *s, PyObject *args, PyObject* kwds)
+{
+       static char *kwlist[] = {"buffer", "nbytes", "flags", 0};
+
+       int recvlen = 0, flags = 0, readlen;
+       char *buf;
+       int buflen;
+
+       PyObject *addr = NULL;
+       PyObject *ret = NULL;
+
+       if (!PyArg_ParseTupleAndKeywords(args, kwds, "s#|ii:recvfrom", kwlist,
+                                        &buf, &buflen, &recvlen, &flags))
+               return NULL;
+       assert(buf != 0 && buflen > 0);
+
+       if (recvlen < 0) {
+               PyErr_SetString(PyExc_ValueError,
+                               "negative buffersize in recv");
+               return NULL;
+       }
+       if (recvlen == 0) {
+            /* If nbytes was not specified, use the buffer's length */
+            recvlen = buflen;
+       }
+
+       readlen = sock_recvfrom_guts(s, buf, recvlen, flags, &addr);
+       if (readlen < 0) {
+               /* Return an error */
+               goto finally;
+       }
+
+       /* Return the number of bytes read and the address.  Note that we do
+          not do anything special here in the case that readlen < recvlen. */
+       ret = PyTuple_Pack(2, PyInt_FromLong(readlen), addr);
+       
+finally:
+       Py_XDECREF(addr);
+       return ret;
+}
+
+PyDoc_STRVAR(recvfrom_buf_doc,
+"recvfrom_buf(buffer[, nbytes[, flags]]) -> (nbytes, address info)\n\
+\n\
+Like recv_buf(buffer[, nbytes[, flags]]) but also return the sender's address info.");
+
+
 /* s.send(data [,flags]) method */
 
 static PyObject *
@@ -2503,59 +2679,63 @@ of the socket (flag == SHUT_WR), or both ends (flag == SHUT_RDWR).");
 /* List of methods for socket objects */
 
 static PyMethodDef sock_methods[] = {
-       {"accept",      (PyCFunction)sock_accept, METH_NOARGS,
-                       accept_doc},
-       {"bind",        (PyCFunction)sock_bind, METH_O,
-                       bind_doc},
-       {"close",       (PyCFunction)sock_close, METH_NOARGS,
-                       close_doc},
-       {"connect",     (PyCFunction)sock_connect, METH_O,
-                       connect_doc},
-       {"connect_ex",  (PyCFunction)sock_connect_ex, METH_O,
-                       connect_ex_doc},
+       {"accept",        (PyCFunction)sock_accept, METH_NOARGS,
+                         accept_doc},
+       {"bind",          (PyCFunction)sock_bind, METH_O,
+                         bind_doc},
+       {"close",         (PyCFunction)sock_close, METH_NOARGS,
+                         close_doc},
+       {"connect",       (PyCFunction)sock_connect, METH_O,
+                         connect_doc},
+       {"connect_ex",    (PyCFunction)sock_connect_ex, METH_O,
+                         connect_ex_doc},
 #ifndef NO_DUP
-       {"dup",         (PyCFunction)sock_dup, METH_NOARGS,
-                       dup_doc},
+       {"dup",           (PyCFunction)sock_dup, METH_NOARGS,
+                         dup_doc},
 #endif
-       {"fileno",      (PyCFunction)sock_fileno, METH_NOARGS,
-                       fileno_doc},
+       {"fileno",        (PyCFunction)sock_fileno, METH_NOARGS,
+                         fileno_doc},
 #ifdef HAVE_GETPEERNAME
-       {"getpeername", (PyCFunction)sock_getpeername,
-                       METH_NOARGS, getpeername_doc},
-#endif
-       {"getsockname", (PyCFunction)sock_getsockname,
-                       METH_NOARGS, getsockname_doc},
-       {"getsockopt",  (PyCFunction)sock_getsockopt, METH_VARARGS,
-                       getsockopt_doc},
-       {"listen",      (PyCFunction)sock_listen, METH_O,
-                       listen_doc},
+       {"getpeername",   (PyCFunction)sock_getpeername,
+                         METH_NOARGS, getpeername_doc},
+#endif
+       {"getsockname",   (PyCFunction)sock_getsockname,
+                         METH_NOARGS, getsockname_doc},
+       {"getsockopt",    (PyCFunction)sock_getsockopt, METH_VARARGS,
+                         getsockopt_doc},
+       {"listen",        (PyCFunction)sock_listen, METH_O,
+                         listen_doc},
 #ifndef NO_DUP
-       {"makefile",    (PyCFunction)sock_makefile, METH_VARARGS,
-                       makefile_doc},
-#endif
-       {"recv",        (PyCFunction)sock_recv, METH_VARARGS,
-                       recv_doc},
-       {"recvfrom",    (PyCFunction)sock_recvfrom, METH_VARARGS,
-                       recvfrom_doc},
-       {"send",        (PyCFunction)sock_send, METH_VARARGS,
-                       send_doc},
-       {"sendall",     (PyCFunction)sock_sendall, METH_VARARGS,
-                       sendall_doc},
-       {"sendto",      (PyCFunction)sock_sendto, METH_VARARGS,
-                       sendto_doc},
-       {"setblocking", (PyCFunction)sock_setblocking, METH_O,
-                       setblocking_doc},
-       {"settimeout", (PyCFunction)sock_settimeout, METH_O,
-                       settimeout_doc},
-       {"gettimeout", (PyCFunction)sock_gettimeout, METH_NOARGS,
-                       gettimeout_doc},
-       {"setsockopt",  (PyCFunction)sock_setsockopt, METH_VARARGS,
-                       setsockopt_doc},
-       {"shutdown",    (PyCFunction)sock_shutdown, METH_O,
-                       shutdown_doc},
+       {"makefile",      (PyCFunction)sock_makefile, METH_VARARGS,
+                         makefile_doc},
+#endif
+       {"recv",          (PyCFunction)sock_recv, METH_VARARGS,
+                         recv_doc},
+       {"recv_buf",      (PyCFunction)sock_recv_buf, METH_VARARGS | METH_KEYWORDS,
+                         recv_buf_doc},
+       {"recvfrom",      (PyCFunction)sock_recvfrom, METH_VARARGS,
+                         recvfrom_doc},
+       {"recvfrom_buf",  (PyCFunction)sock_recvfrom_buf, METH_VARARGS | METH_KEYWORDS,
+                         recvfrom_buf_doc},
+       {"send",          (PyCFunction)sock_send, METH_VARARGS,
+                         send_doc},
+       {"sendall",       (PyCFunction)sock_sendall, METH_VARARGS,
+                         sendall_doc},
+       {"sendto",        (PyCFunction)sock_sendto, METH_VARARGS,
+                         sendto_doc},
+       {"setblocking",   (PyCFunction)sock_setblocking, METH_O,
+                         setblocking_doc},
+       {"settimeout",    (PyCFunction)sock_settimeout, METH_O,
+                         settimeout_doc},
+       {"gettimeout",    (PyCFunction)sock_gettimeout, METH_NOARGS,
+                         gettimeout_doc},
+       {"setsockopt",    (PyCFunction)sock_setsockopt, METH_VARARGS,
+                         setsockopt_doc},
+       {"shutdown",      (PyCFunction)sock_shutdown, METH_O,
+                         shutdown_doc},
 #ifdef RISCOS
-       {"sleeptaskw",  (PyCFunction)sock_sleeptaskw, METH_O,
-                       sleeptaskw_doc},
+       {"sleeptaskw",    (PyCFunction)sock_sleeptaskw, METH_O,
+                         sleeptaskw_doc},
 #endif
        {NULL,                  NULL}           /* sentinel */
 };
@@ -3401,7 +3581,7 @@ socket_inet_aton(PyObject *self, PyObject *args)
        if (strcmp(ip_addr, "255.255.255.255") == 0) {
                packed_addr = 0xFFFFFFFF;
        } else {
-       
+
                packed_addr = inet_addr(ip_addr);
 
                if (packed_addr == INADDR_NONE) {       /* invalid address */
@@ -3476,7 +3656,7 @@ socket_inet_pton(PyObject *self, PyObject *args)
                                "can't use AF_INET6, IPv6 is disabled");
                return NULL;
        }
-#endif 
+#endif
 
        retval = inet_pton(af, ip, packed);
        if (retval < 0) {
@@ -3499,7 +3679,7 @@ socket_inet_pton(PyObject *self, PyObject *args)
                return NULL;
        }
 }
-       
+
 PyDoc_STRVAR(inet_ntop_doc,
 "inet_ntop(af, packed_ip) -> string formatted IP address\n\
 \n\
@@ -3517,7 +3697,7 @@ socket_inet_ntop(PyObject *self, PyObject *args)
 #else
        char ip[INET_ADDRSTRLEN + 1];
 #endif
-       
+
        /* Guarantee NUL-termination for PyString_FromString() below */
        memset((void *) &ip[0], '\0', sizeof(ip));
 
@@ -3595,7 +3775,7 @@ socket_getaddrinfo(PyObject *self, PyObject *args)
        } else if (PyString_Check(hobj)) {
                hptr = PyString_AsString(hobj);
        } else {
-               PyErr_SetString(PyExc_TypeError, 
+               PyErr_SetString(PyExc_TypeError,
                                "getaddrinfo() argument 1 must be string or None");
                return NULL;
        }