1#include "pyobjc.h"
2#import "OC_PythonUnicode.h"
3
4@implementation OC_PythonUnicode 
5
6+ newWithPythonObject:(PyObject*)v;
7{
8	OC_PythonUnicode* res;
9
10	res = [[OC_PythonUnicode alloc] initWithPythonObject:v];
11	[res autorelease];
12	return res;
13}
14
15- initWithPythonObject:(PyObject*)v;
16{
17	Py_INCREF(v);
18	Py_XDECREF(value);
19	value = v;
20	return self;
21}
22
23
24-(PyObject*)__pyobjc_PythonObject__
25{
26	Py_INCREF(value);
27	return value;
28}
29
30-(PyObject*)__pyobjc_PythonTransient__:(int*)cookie
31{
32	*cookie = 0;
33	Py_INCREF(value);
34	return value;
35}
36
37
38
39-(void)release
40{
41	/* There is small race condition when an object is almost deallocated
42	 * in one thread and fetched from the registration mapping in another
43	 * thread. If we don't get the GIL this object might get a -dealloc
44	 * message just as the other thread is fetching us from the mapping.
45	 * That's why we need to grab the GIL here (getting it in dealloc is
46	 * too late, we'd already be dead).
47	 */
48	/* FIXME: it should be possible to grab the lock only when really
49	 * needed, but the test below isn't good enough. Be heavy handed to
50	 * make sure we're right, rather than crashing sometimes anyway.
51	 */
52	/* FIXME2: in rare occasions we're trying to acquire the GIL during 
53	 * shutdown and if we're very unlucky this can happen after the 
54	 * GILState machinery has shut down...
55	 */
56#if 0
57	if ([self retainCount] == 1) {
58#endif
59		PyObjC_BEGIN_WITH_GIL
60			[super release];
61		PyObjC_END_WITH_GIL
62#if 0
63	} else {
64		[super release];
65	}
66#endif
67}
68
69-(void)dealloc
70{
71	PyObjC_BEGIN_WITH_GIL
72		PyObjC_UnregisterObjCProxy(value, self);
73#ifndef PyObjC_UNICODE_FAST_PATH
74		[realObject release];
75#endif /* !PyObjC_UNICODE_FAST_PATH */
76		Py_XDECREF(value);
77	PyObjC_END_WITH_GIL
78
79	[super dealloc];
80}
81
82#ifdef PyObjC_UNICODE_FAST_PATH
83
84-(NSUInteger)length
85{
86	return (NSUInteger)PyUnicode_GET_SIZE(value);
87}
88
89-(unichar)characterAtIndex:(NSUInteger)anIndex
90{
91	if (anIndex > PY_SSIZE_T_MAX) {
92		[NSException raise:@"NSRangeException" format:@"Range or index out of bounds"];
93	}
94	if (anIndex >= (NSUInteger)PyUnicode_GET_SIZE(value)) {
95		[NSException raise:@"NSRangeException" format:@"Range or index out of bounds"];
96	}
97	return PyUnicode_AS_UNICODE(value)[anIndex];
98}
99
100-(void)getCharacters:(unichar *)buffer range:(NSRange)aRange
101{
102	if (aRange.location + aRange.length > (NSUInteger)PyUnicode_GET_SIZE(value)) {
103		[NSException raise:@"NSRangeException" format:@"Range or index out of bounds"];
104	}
105	memcpy(buffer, 
106	       PyUnicode_AS_UNICODE(value) + aRange.location, 
107	       sizeof(unichar) * aRange.length);
108}
109
110#else /* !PyObjC_UNICODE_FAST_PATH */
111
112-(id)__realObject__
113{
114	if (!realObject) {
115		PyObjC_BEGIN_WITH_GIL
116			PyObject* utf8 = PyUnicode_AsUTF8String(value);
117			if (!utf8) {
118				NSLog(@"failed to encode unicode string to UTF8");
119				PyErr_Clear();
120			} else {
121				realObject = [[NSString alloc]
122					initWithBytes:PyString_AS_STRING(utf8)
123					       length:(NSUInteger)PyString_GET_SIZE(value)
124					     encoding:NSUTF8StringEncoding];
125				Py_DECREF(utf8);
126			}
127		PyObjC_END_WITH_GIL
128	}
129	return realObject;
130}
131	
132-(NSUInteger)length
133{
134	return [((NSString *)[self __realObject__]) length];
135}
136
137-(unichar)characterAtIndex:(NSUInteger)anIndex
138{
139	return [((NSString *)[self __realObject__]) characterAtIndex:anIndex];
140}
141
142-(void)getCharacters:(unichar *)buffer range:(NSRange)aRange
143{
144	[((NSString *)[self __realObject__]) getCharacters:buffer range:aRange];
145}
146
147
148#endif /* PyObjC_UNICODE_FAST_PATH */
149
150/*
151 * NSCoding support 
152 *
153 * We need explicit NSCoding support to get full fidelity, otherwise we'll
154 * get archived as generic NSStrings.
155 */
156- (id)initWithCharactersNoCopy:(unichar *)characters 
157			length:(NSUInteger)length 
158		  freeWhenDone:(BOOL)flag
159{
160#ifndef PyObjC_UNICODE_FAST_PATH
161# error "Wide UNICODE builds are not supported at the moment"
162#endif
163	PyObjC_BEGIN_WITH_GIL
164		value = PyUnicode_FromUnicode((Py_UNICODE*)characters, length);
165		if (value == NULL) {
166			PyObjC_GIL_FORWARD_EXC();
167		}
168
169	PyObjC_END_WITH_GIL;
170	if (flag) {
171		free(characters);
172	}
173	return self;
174}
175
176
177/* 
178 * Helper method for initWithCoder, needed to deal with
179 * recursive objects (e.g. o.value = o)
180 */
181-(void)pyobjcSetValue:(NSObject*)other
182{
183	PyObject* v = PyObjC_IdToPython(other);
184	Py_XDECREF(value);
185	value = v;
186}
187
188- initWithCoder:(NSCoder*)coder
189{
190	int ver;
191	if ([coder allowsKeyedCoding]) {
192		ver = [coder decodeInt32ForKey:@"pytype"];
193	} else {
194		[coder decodeValueOfObjCType:@encode(int) at:&ver];
195	}
196	if (ver == 1) {
197		self = [super initWithCoder:coder];
198		return self;
199	} else if (ver == 2) {
200
201		if (PyObjC_Decoder != NULL) {
202			PyObjC_BEGIN_WITH_GIL
203				PyObject* cdr = PyObjC_IdToPython(coder);
204				if (cdr == NULL) {
205					PyObjC_GIL_FORWARD_EXC();
206				}
207
208				PyObject* setValue;
209				PyObject* selfAsPython = PyObjCObject_New(self, 0, YES);
210				setValue = PyObject_GetAttrString(selfAsPython, "pyobjcSetValue_");
211
212				PyObject* v = PyObject_CallFunction(PyObjC_Decoder, "OO", cdr, setValue);
213				Py_DECREF(cdr);
214				Py_DECREF(setValue);
215				Py_DECREF(selfAsPython);
216
217				if (v == NULL) {
218					PyObjC_GIL_FORWARD_EXC();
219				}
220
221				Py_XDECREF(value);
222				value = v;
223
224				NSObject* proxy = PyObjC_FindObjCProxy(value);
225				if (proxy == NULL) {
226					PyObjC_RegisterObjCProxy(value, self);
227				} else {
228					[self release];
229					[proxy retain];
230					self = (OC_PythonUnicode*)proxy;
231				}
232
233
234			PyObjC_END_WITH_GIL
235
236			return self;
237
238		} else {
239			[NSException raise:NSInvalidArgumentException
240					format:@"decoding Python objects is not supported"];
241			return nil;
242
243		}
244	} else {
245		[NSException raise:NSInvalidArgumentException
246			format:@"encoding Python objects is not supported"];
247		return nil;
248	}
249}
250
251-(void)encodeWithCoder:(NSCoder*)coder
252{
253	if (PyUnicode_CheckExact(value)) {
254		if ([coder allowsKeyedCoding]) {
255			[coder encodeInt32:1 forKey:@"pytype"];
256		} else {
257			int v = 1;
258			[coder encodeValueOfObjCType:@encode(int) at:&v];
259		}
260		[super encodeWithCoder:coder];
261	} else {
262		if ([coder allowsKeyedCoding]) {
263			[coder encodeInt32:2 forKey:@"pytype"];
264		} else {
265			int v = 2;
266			[coder encodeValueOfObjCType:@encode(int) at:&v];
267		}
268
269		PyObjC_encodeWithCoder(value, coder);
270	}
271}
272
273#if 1
274
275
276-(NSObject*)replacementObjectForArchiver:(NSArchiver*)archiver 
277{
278	(void)(archiver);
279	return self;
280}
281
282-(NSObject*)replacementObjectForKeyedArchiver:(NSKeyedArchiver*)archiver
283{
284	(void)(archiver);
285	return self;
286}
287
288-(NSObject*)replacementObjectForCoder:(NSCoder*)archiver
289{
290	(void)(archiver);
291	return self;
292}
293
294-(NSObject*)replacementObjectForPortCoder:(NSPortCoder*)archiver
295{
296	(void)(archiver);
297	return self;
298}
299
300-(Class)classForArchiver
301{
302	return [OC_PythonUnicode class];
303}
304
305-(Class)classForKeyedArchiver
306{
307	return [OC_PythonUnicode class];
308}
309
310-(Class)classForCoder
311{
312	return [OC_PythonUnicode class];
313}
314
315-(Class)classForPortCoder
316{
317	return [OC_PythonUnicode class];
318}
319
320/* Ensure that we can be unarchived as a generic string by pure ObjC
321 * code.
322 */
323+classFallbacksForKeyedArchiver
324{
325	return [NSArray arrayWithObject:@"NSString"];
326}
327
328#endif
329
330@end /* implementation OC_PythonUnicode */
331