1/*
2 * Custom subclass of PyUnicode_Type, to allow for transparent bridging of
3 * strings
4 */
5
6#include "pyobjc.h"
7
8#include <stddef.h>
9#include <Foundation/NSString.h>
10
11typedef struct {
12	PyUnicodeObject	base;
13	PyObject*	weakrefs;
14	id		nsstr;
15	PyObject* py_nsstr;
16} PyObjCUnicodeObject;
17
18PyDoc_STRVAR(class_doc,
19	"objc.pyobjc_unicode\n"
20	"\n"
21	"Subclass of unicode for representing NSString values. Use \n"
22	"the method nsstring to access the NSString. \n"
23	"Note that instances are immutable and won't be updated when\n"
24	"the value of the NSString changes."
25);
26
27static void
28class_dealloc(PyObject* obj)
29{
30	PyObjCUnicodeObject* uobj = (PyObjCUnicodeObject*)obj;
31	id nsstr = uobj->nsstr;
32	PyObject* weakrefs = uobj->weakrefs;
33	PyObject* py_nsstr = uobj->py_nsstr;
34
35	PyObjC_UnregisterPythonProxy(nsstr, obj);
36
37	Py_XDECREF(py_nsstr);
38	if (nsstr) {
39		CFRelease(nsstr);
40	}
41
42	if (weakrefs) {
43		PyObject_ClearWeakRefs(obj);
44	}
45
46	PyUnicode_Type.tp_dealloc(obj);
47}
48
49static PyObject*
50meth_nsstring(PyObject* self)
51{
52	PyObjCUnicodeObject* uobj = (PyObjCUnicodeObject*)self;
53	if (uobj->py_nsstr == NULL) {
54		uobj->py_nsstr = PyObjCObject_New(uobj->nsstr,
55				PyObjCObject_kDEFAULT, YES);
56	}
57	Py_INCREF(uobj->py_nsstr);
58	return uobj->py_nsstr;
59}
60
61
62static PyObject*
63meth_getattro(PyObject *o, PyObject *attr_name)
64{
65	PyObject *res;
66	res = PyObject_GenericGetAttr(o, attr_name);
67	if (res == NULL) {
68		PyErr_Clear();
69		PyObject *py_nsstr = meth_nsstring(o);
70		res = PyObject_GenericGetAttr(py_nsstr, attr_name);
71		Py_XDECREF(py_nsstr);
72	}
73	return res;
74}
75
76static PyObject*
77meth_reduce(PyObject* self)
78{
79	PyObject* retVal = NULL;
80	PyObject *v = NULL;
81	PyObject *v2 = NULL;
82
83	retVal = PyTuple_New(2);
84	if (retVal == NULL) goto error;
85
86	v = (PyObject*)&PyUnicode_Type;
87	Py_INCREF(v);
88	PyTuple_SET_ITEM(retVal, 0, v);
89
90	v = PyUnicode_FromObject(self);
91	if (v == NULL ) goto error;
92
93	v2 = PyTuple_New(1);
94	if (v2 == NULL) goto error;
95	PyTuple_SET_ITEM(v2, 0, v);
96	PyTuple_SET_ITEM(retVal, 1, v2);
97
98	return retVal;
99
100error:
101	Py_XDECREF(retVal);
102	Py_XDECREF(v);
103	return NULL;
104}
105
106static PyMethodDef class_methods[] = {
107	{
108	  "nsstring",
109	  (PyCFunction)meth_nsstring,
110	  METH_NOARGS,
111	  "directly access NSString instance"
112	},
113	{
114	  "__reduce__",
115	  (PyCFunction)meth_reduce,
116	  METH_NOARGS,
117	  "Used for pickling"
118	},
119	{ 0, 0, 0, 0 } /* sentinel */
120};
121
122static PyObject*
123nsstring_get__pyobjc_object__(PyObject *self, void *closure __attribute__((__unused__))) {
124	return meth_nsstring(self);
125}
126
127static PyGetSetDef nsstring_getsetters[] = {
128	{
129		"__pyobjc_object__",
130		(getter)nsstring_get__pyobjc_object__, NULL,
131		"raw NSString instance",
132		NULL
133	},
134	{
135		NULL,
136		NULL, NULL,
137		NULL,
138		NULL
139	}
140};
141
142static PyObject*
143class_new(
144	PyTypeObject* type __attribute__((__unused__)),
145	PyObject* args __attribute__((__unused__)),
146	PyObject* kwds __attribute__((__unused__)))
147{
148	PyErr_SetString(PyExc_TypeError,
149			"Cannot create instances of 'objc.unicode' in Python");
150	return NULL;
151}
152
153PyTypeObject PyObjCUnicode_Type = {
154	PyObject_HEAD_INIT(&PyType_Type)
155	0,					/* ob_size */
156	"objc.pyobjc_unicode",			/* tp_name */
157	sizeof(PyObjCUnicodeObject),		/* tp_basicsize */
158	0,			 		/* tp_itemsize */
159	/* methods */
160	class_dealloc,	 			/* tp_dealloc */
161	0,					/* tp_print */
162	0,					/* tp_getattr */
163	0,					/* tp_setattr */
164	0,					/* tp_compare */
165	0,					/* tp_repr */
166	0,					/* tp_as_number */
167	0,					/* tp_as_sequence */
168	0,		       			/* tp_as_mapping */
169	0,					/* tp_hash */
170	0,					/* tp_call */
171	0,					/* tp_str */
172	meth_getattro,		/* tp_getattro */
173	0,					/* tp_setattro */
174	0,					/* tp_as_buffer */
175	Py_TPFLAGS_DEFAULT,			/* tp_flags */
176 	class_doc,				/* tp_doc */
177 	0,					/* tp_traverse */
178 	0,					/* tp_clear */
179	0,					/* tp_richcompare */
180	offsetof(PyObjCUnicodeObject, weakrefs),	/* tp_weaklistoffset */
181	0,					/* tp_iter */
182	0,					/* tp_iternext */
183	class_methods,				/* tp_methods */
184	0,					/* tp_members */
185	nsstring_getsetters,			/* tp_getset */
186	&PyUnicode_Type,			/* tp_base */
187	0,					/* tp_dict */
188	0,					/* tp_descr_get */
189	0,					/* tp_descr_set */
190	0,					/* tp_dictoffset */
191	0,					/* tp_init */
192	0,					/* tp_alloc */
193	class_new,				/* tp_new */
194	0,		        		/* tp_free */
195	0,					/* tp_is_gc */
196	0,                                      /* tp_bases */
197	0,                                      /* tp_mro */
198	0,                                      /* tp_cache */
199	0,                                      /* tp_subclasses */
200	0,                                      /* tp_weaklist */
201	0                                       /* tp_del */
202#if PY_VERSION_HEX >= 0x02060000
203	, 0                                     /* tp_version_tag */
204#endif
205
206};
207
208PyObject*
209PyObjCUnicode_New(NSString* value)
210{
211	/* Conversion to PyUnicode without creating an autoreleased object.
212	 *
213	 * NOTE: A final optimization is removing the copy of 'characters', but
214	 * that can only be done when sizeof(unichar) == Py_UNICODE_SIZE.
215	 *
216	 * The reason for doing this: NSThread
217	 *     +detachNewThreadSelector:toTarget:withObject:, with a string
218	 *     as one of the arguments.
219	 *
220	 * Another reason is that the following loop 'leaks' memory when using
221	 * -UTF8String:
222	 *  	while True:
223	 *  		NSString.alloc().init()
224	 *
225	 *  while the following doesn't:
226	 *
227	 *  	while True:
228	 *  		NSArray.alloc().init()
229	 */
230	PyObjCUnicodeObject* result;
231// XXX - I don't know how to get gcc to let me use sizeof(unichar)
232#ifdef PyObjC_UNICODE_FAST_PATH
233	Py_ssize_t length = [value length];
234
235	if (length < 0) {
236		PyErr_SetString(PyExc_SystemError, "string with negative length");
237		return NULL;
238	}
239	result = PyObject_New(PyObjCUnicodeObject, &PyObjCUnicode_Type);
240	Py_UNICODE* tptr = PyMem_NEW(Py_UNICODE, length);
241	PyUnicode_AS_UNICODE(result) = tptr;
242	tptr = NULL;
243
244	if (PyUnicode_AS_UNICODE(result) == NULL) {
245		Py_DECREF((PyObject*)result);
246		PyErr_NoMemory();
247		return NULL;
248	}
249	[value getCharacters:(unichar *)PyUnicode_AS_UNICODE(result)];
250	PyUnicode_GET_SIZE(result) = length;
251#else
252	int i, length;
253	unichar* volatile characters = NULL;
254	NSRange range;
255
256	PyObjC_DURING
257		length = [value length];
258		characters = PyMem_Malloc(sizeof(unichar) * length);
259		if (characters == NULL) {
260			PyErr_NoMemory();
261			NS_VALUERETURN(NULL, PyObject*);
262		}
263
264		range = NSMakeRange(0, length);
265
266		[value getCharacters: characters range: range];
267
268	PyObjC_HANDLER
269		if (characters) {
270			PyMem_Free(characters);
271			characters = NULL;
272		}
273		PyObjCErr_FromObjC(localException);
274		NS_VALUERETURN(NULL, PyObject*);
275	PyObjC_ENDHANDLER
276
277	result = PyObject_New(PyObjCUnicodeObject, &PyObjCUnicode_Type);
278	PyUnicode_AS_UNICODE(result) = PyMem_NEW(Py_UNICODE, length);
279	if (PyUnicode_AS_UNICODE(result) == NULL) {
280		Py_DECREF((PyObject*)result);
281		PyMem_Free(characters); characters = NULL;
282		PyErr_NoMemory();
283		return NULL;
284	}
285	PyUnicode_GET_SIZE(result) = length;
286	for (i = 0; i < length; i++) {
287		PyUnicode_AS_UNICODE(result)[i] = (Py_UNICODE)(characters[i]);
288	}
289	PyMem_Free(characters); characters = NULL;
290#endif
291
292	result->base.defenc = NULL;
293
294	result->base.hash = -1;
295
296	if (PyUnicode_GET_SIZE(result) == 0) {
297		result->base.hash = 0;
298	}
299
300	result->weakrefs = NULL;
301	result->py_nsstr = NULL;
302	result->nsstr = value;
303	CFRetain(value);
304
305	return (PyObject*)result;
306}
307
308NSString*
309PyObjCUnicode_Extract(PyObject* value)
310{
311	if (!PyObjCUnicode_Check(value)) {
312		PyErr_BadInternalCall();
313		return NULL;
314	}
315
316	return ((PyObjCUnicodeObject*)value)->nsstr;
317}
318