Merge remote-tracking branch 'origin/pycurl' into planetlab-4_0-branch
[plcapi.git] / trunk / psycopg2 / psycopg / typecast_binary.c
1 /* typecast_binary.c - binary typecasting functions to python types
2  *
3  * Copyright (C) 2001-2003 Federico Di Gregorio <fog@debian.org>
4  *
5  * This file is part of the psycopg module.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2,
10  * or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20  */
21
22 #include "typecast_binary.h"
23
24 #include <libpq-fe.h>
25 #include <stdlib.h>
26
27
28 /* Python object holding a memory chunk. The memory is deallocated when
29    the object is destroyed. This type is used to let users directly access
30    memory chunks holding unescaped binary data through the buffer interface.
31  */
32
33 static void
34 chunk_dealloc(chunkObject *self)
35 {
36     Dprintf("chunk_dealloc: deallocating memory at %p, size %d",
37             self->base, self->len);
38     free(self->base);
39     self->ob_type->tp_free((PyObject *) self);
40 }
41
42 static PyObject *
43 chunk_repr(chunkObject *self)
44 {
45     return PyString_FromFormat("<memory chunk at %p size %d>",
46                                self->base, self->len);
47 }
48
49 static int
50 chunk_getreadbuffer(chunkObject *self, int segment, void **ptr)
51 {
52     if (segment != 0)
53     {
54         PyErr_SetString(PyExc_SystemError,
55                         "acessing non-existant buffer segment");
56         return -1;
57     }
58     *ptr = self->base;
59     return self->len;
60 }
61
62 static int
63 chunk_getsegcount(chunkObject *self, int *lenp)
64 {
65     if (lenp != NULL)
66         *lenp = self->len;
67     return 1;
68 }
69
70 static PyBufferProcs chunk_as_buffer =
71 {
72     (getreadbufferproc) chunk_getreadbuffer,
73     (getwritebufferproc) NULL,
74     (getsegcountproc) chunk_getsegcount,
75     (getcharbufferproc) NULL
76 };
77
78 #define chunk_doc "memory chunk"
79
80 PyTypeObject chunkType = {
81     PyObject_HEAD_INIT(NULL)
82     0,                          /* ob_size */
83     "psycopg2._psycopg.chunk",   /* tp_name */
84     sizeof(chunkObject),        /* tp_basicsize */
85     0,                          /* tp_itemsize */
86     (destructor) chunk_dealloc, /* tp_dealloc*/
87     0,                          /* tp_print */
88     0,                          /* tp_getattr */
89     0,                          /* tp_setattr */
90     0,                          /* tp_compare */
91     (reprfunc) chunk_repr,      /* tp_repr */
92     0,                          /* tp_as_number */
93     0,                          /* tp_as_sequence */
94     0,                          /* tp_as_mapping */
95     0,                          /* tp_hash */
96     0,                          /* tp_call */
97     0,                          /* tp_str */
98     0,                          /* tp_getattro */
99     0,                          /* tp_setattro */
100     &chunk_as_buffer,           /* tp_as_buffer */
101     Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /* tp_flags */
102     chunk_doc                   /* tp_doc */
103 };
104
105 /* the function typecast_BINARY_cast_unescape is used when libpq does not
106    provide PQunescapeBytea: it convert all the \xxx octal sequences to the
107    proper byte value */
108
109 #ifdef PSYCOPG_OWN_QUOTING
110 static unsigned char *
111 typecast_BINARY_cast_unescape(unsigned char *str, size_t *to_length)
112 {
113     char *dstptr, *dststr;
114     int len, i;
115
116     len = strlen(str);
117     dststr = (char*)calloc(len, sizeof(char));
118     dstptr = dststr;
119
120     if (dststr == NULL) return NULL;
121
122     Py_BEGIN_ALLOW_THREADS;
123    
124     for (i = 0; i < len; i++) {
125         if (str[i] == '\\') {
126             if ( ++i < len) {
127                 if (str[i] == '\\') {
128                     *dstptr = '\\';
129                 }
130                 else {
131                     *dstptr = 0;
132                     *dstptr |= (str[i++] & 7) << 6;
133                     *dstptr |= (str[i++] & 7) << 3;
134                     *dstptr |= (str[i] & 7);
135                 }
136             }
137         }
138         else {
139             *dstptr = str[i];
140         }
141         dstptr++;
142     }
143     
144     Py_END_ALLOW_THREADS;
145
146     *to_length = (size_t)(dstptr-dststr);
147     
148     return dststr;
149 }
150
151 #define PQunescapeBytea typecast_BINARY_cast_unescape
152 #endif
153     
154 static PyObject *
155 typecast_BINARY_cast(char *s, int l, PyObject *curs)
156 {
157     chunkObject *chunk;
158     PyObject *res;
159     char *str, *buffer = NULL;
160     size_t len;
161
162     if (s == NULL) {Py_INCREF(Py_None); return Py_None;}
163
164     /* PQunescapeBytea absolutely wants a 0-terminated string and we don't
165        want to copy the whole buffer, right? Wrong, but there isn't any other
166        way <g> */
167     if (s[l] != '\0') {
168         if ((buffer = PyMem_Malloc(l+1)) == NULL)
169             PyErr_NoMemory();
170         strncpy(buffer, s, l);
171         buffer[l] = '\0';
172         s = buffer;
173     }
174     str = (char*)PQunescapeBytea((unsigned char*)s, &len);
175     Dprintf("typecast_BINARY_cast: unescaped %d bytes", len);
176     if (buffer) PyMem_Free(buffer);
177
178     chunk = (chunkObject *) PyObject_New(chunkObject, &chunkType);
179     if (chunk == NULL) return NULL;
180     
181     chunk->base = str;
182     chunk->len = len;
183     if ((res = PyBuffer_FromObject((PyObject *)chunk, 0, len)) == NULL)
184         return NULL;
185
186     /* PyBuffer_FromObject() created a new reference. Release our reference so
187        that the memory can be freed once the buffer is garbage collected. */
188     Py_DECREF(chunk);
189
190     return res;
191 }