PageRenderTime 234ms CodeModel.GetById 81ms app.highlight 78ms RepoModel.GetById 62ms app.codeStats 8ms

/mysql_watcher/indra/base/cllsd.c

https://bitbucket.org/lindenlab/apiary/
C | 665 lines | 468 code | 139 blank | 58 comment | 116 complexity | 2d01b16fbac96365cd9a1254941f0bf8 MD5 | raw file
  1/*
  2 * @file cllsd.c
  3 * @brief Accelerated XML generation for LLSD
  4 * @author Bryan O'Sullivan
  5 * 
  6 * $LicenseInfo:firstyear=2008&license=mit$
  7 * 
  8 * Copyright (c) 2008-2009, Linden Research, Inc.
  9 * 
 10 * Permission is hereby granted, free of charge, to any person obtaining a copy
 11 * of this software and associated documentation files (the "Software"), to deal
 12 * in the Software without restriction, including without limitation the rights
 13 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 14 * copies of the Software, and to permit persons to whom the Software is
 15 * furnished to do so, subject to the following conditions:
 16 * 
 17 * The above copyright notice and this permission notice shall be included in
 18 * all copies or substantial portions of the Software.
 19 * 
 20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 21 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 23 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 25 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 26 * THE SOFTWARE.
 27 * $/LicenseInfo$
 28 *
 29 * Note: this module is not compatible with the type trickery played
 30 * by saranwrap.
 31 */
 32
 33#include <Python.h>
 34#include <stdlib.h>
 35#include <string.h>
 36
 37#if PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION < 5
 38typedef int Py_ssize_t;
 39#endif
 40
 41typedef struct 
 42{
 43	char *ptr;
 44	Py_ssize_t off;
 45	Py_ssize_t size;
 46} Buffer;
 47
 48// Not a fuzzy estimation method. If we still have core dumps
 49// due to realloc attempts, try reducing below number to 1.5
 50#define MEM_FACTOR 2.0
 51
 52static inline int buf_ensure(Buffer *buf, Py_ssize_t len)
 53{
 54    //DEV-14630 - Incorporate python module. Addressing core dump issue
 55    int asymptotic_len = fmax(buf->size * MEM_FACTOR, buf->size + len);
 56
 57	//fprintf(stderr, "\nBuffer : %s\nBuffer Size: %d\nBuffer Offset: %d\nLength: %d\nMax(%f, %d): %d\n", 
 58    //        buf->ptr, buf->size, buf->off, len, buf->size * MEM_FACTOR, buf->size + len, asymptotic_len);
 59	if (len > buf->size - buf->off) {
 60		char *nptr = realloc(buf->ptr, asymptotic_len);
 61		if (nptr == NULL) {
 62			PyErr_SetString(PyExc_MemoryError, "out of memory");
 63			return 0;
 64		}
 65		buf->ptr = nptr;
 66		buf->size = asymptotic_len;
 67	}
 68
 69	return 1;
 70}
 71
 72static inline void buf_append(Buffer *buf, const char *str, Py_ssize_t len)
 73{
 74	memcpy(buf->ptr + buf->off, str, len);
 75	buf->off += len;
 76}
 77
 78static inline void buf_char_append(Buffer *buf, char c)
 79{
 80	buf->ptr[buf->off++] = c;
 81}
 82
 83static inline int buf_extend(Buffer *buf, const char *str, Py_ssize_t len)
 84{
 85	if (!buf_ensure(buf, len))
 86		return 0;
 87
 88	buf_append(buf, str, len);
 89
 90	return 1;
 91}
 92
 93static int esc_extend(Buffer *buf, const char *str, Py_ssize_t len)
 94{
 95	Py_ssize_t i, excess;
 96
 97	if (!buf_ensure(buf, len))
 98		return 0;
 99
100	for (i = excess = 0; i < len; i++) {
101		switch (str[i]) {
102		case '&':
103			excess += 4;
104			if (!buf_ensure(buf, len - i + excess))
105				return 0;
106			buf_append(buf, "&amp;", 5);
107			break;
108		case '<':
109			excess += 3;
110			if (!buf_ensure(buf, len - i + excess))
111				return 0;
112			buf_append(buf, "&lt;", 4);
113			break;
114		case '>':
115			excess += 3;
116			if (!buf_ensure(buf, len - i + excess))
117				return 0;
118			buf_append(buf, "&gt;", 4);
119			break;
120		default:
121			buf_char_append(buf, str[i]); 
122			break;
123		}
124	}
125
126	return 1;
127}
128
129static inline PyObject *as_string(PyObject *obj)
130{
131	PyObject *strobj;
132
133	if (PyString_Check(obj)) {
134		strobj = obj;
135		Py_INCREF(strobj);
136	} else if (PyUnicode_Check(obj)) {
137		strobj = PyUnicode_AsUTF8String(obj);
138		if (!strobj)
139			goto bail;
140	} else {
141		strobj = PyObject_Str(obj);
142		if (!strobj)
143			goto bail;
144
145		if (!PyString_Check(strobj)) {
146			Py_DECREF(strobj);
147			strobj = NULL;
148			PyErr_SetString(PyExc_TypeError,
149					"str() did not return a string");
150		}
151	}
152
153bail:
154	return strobj;
155}
156
157static int obj_to_xml(Buffer *buf, const char *name, PyObject *obj)
158{
159	PyObject *strobj;
160	size_t namelen;
161	Py_ssize_t len;
162	int ret = 0;
163	char *str;
164
165	strobj = as_string(obj);
166	if (strobj == NULL)
167		goto bail;
168
169	len = PyString_GET_SIZE(strobj);
170	str = PyString_AS_STRING(strobj);
171
172	namelen = strlen(name);
173
174	if (!buf_ensure(buf, namelen * 2 + 5 + len))
175		goto bail;
176
177	buf_char_append(buf, '<');
178	buf_append(buf, name, namelen);
179	buf_char_append(buf, '>');
180	buf_append(buf, str, len);
181	buf_append(buf, "</", 2);
182	buf_append(buf, name, namelen);
183	buf_char_append(buf, '>');
184
185	ret = 1;
186
187bail:
188	Py_XDECREF(strobj);
189	return ret;
190}
191
192static int float_to_xml(Buffer *buf, PyObject *obj)
193{
194	double val = PyFloat_AS_DOUBLE(obj);
195
196	if (val == 0.0)
197		return buf_extend(buf, "<real/>", 7);
198
199	if (!buf_ensure(buf, 120))
200		return 0;
201		
202	buf_append(buf, "<real>", 6);
203	PyFloat_AsString(buf->ptr + buf->off,
204			 (PyFloatObject *) obj);
205	buf->off += strlen(buf->ptr + buf->off);
206	buf_append(buf, "</real>", 7);
207
208	return 1;
209}
210
211static int datetime_to_xml(Buffer *buf, PyObject *obj)
212{
213	int has_ms = 0;
214	PyObject* isoobj = NULL;
215	PyObject* strobj = NULL;
216	char* str;
217	Py_ssize_t len;
218	int ret = 0;
219
220	// Try to get out the microsecond value from obj. If that succeeds
221	// then we need to use isoformat to get fractional
222	// seconds. 2009-02-02 Phoenix
223	has_ms = PyObject_HasAttrString(obj, "microsecond");
224	if(has_ms)
225	{
226		isoobj = PyObject_CallMethod(obj, "isoformat", "()");
227	}
228	else
229	{
230		isoobj = PyObject_CallMethod(
231			obj,
232			"strftime",
233			"s",
234			"%Y-%m-%dT%H:%M:%S");
235	}
236	if (isoobj == NULL)
237		goto bail;
238
239	strobj = as_string(isoobj);
240	if (strobj == NULL)
241		goto bail;
242
243	// The magic number used here is the length of the minimal date
244	// string you can get from 'YYYY-MM-DDTHH:MM:SS' which is exactly
245	// 19 bytes. If we used isoformat above then this string is
246	// probably longer.
247	len = PyString_GET_SIZE(strobj);
248	str = PyString_AS_STRING(strobj);
249	if (len < 19)
250	{
251		ret = buf_extend(buf, "<date/>", 7);
252		goto bail;
253	}
254
255	buf_extend(buf, "<date>", 6);
256	buf_extend(buf, str, len);
257	buf_extend(buf, "Z</date>", 8);
258	ret = 1;
259
260bail:
261	Py_XDECREF(isoobj);
262	Py_XDECREF(strobj);
263	return ret;
264}
265
266static int string_to_xml(Buffer *buf, PyObject *obj)
267{
268	Py_ssize_t len;
269
270	len = PyString_GET_SIZE(obj);
271
272	if (len) {
273		if (!buf_extend(buf, "<string>", 8))
274			return 0;
275		if (!esc_extend(buf, PyString_AS_STRING(obj), len))
276			return 0;
277		return buf_extend(buf, "</string>", 9);
278	}
279
280	return buf_extend(buf, "<string/>", 9);
281}
282
283static int int_to_xml(Buffer *buf, PyObject *obj)
284{
285	long val = PyInt_AS_LONG(obj);
286
287	if (val == 0)
288		return buf_extend(buf, "<integer/>", 10);
289	
290	if (!buf_ensure(buf, 64))
291		return 0;
292
293	buf->off += PyOS_snprintf(buf->ptr + buf->off, 64,
294				  "<integer>%ld</integer>", val);
295
296	return 1;
297}
298
299static inline int is_module_type(PyObject *obj, const char *modulename,
300				const char *typename)
301{
302	PyObject *mod;
303	int ret = 0;
304
305	mod = PyImport_ImportModule(modulename);
306	if (mod != NULL) {
307		PyObject *type;
308
309		type = PyObject_GetAttrString(mod, typename);
310		if (type) {
311			ret = PyObject_IsInstance(obj, type);
312			Py_DECREF(type);
313		}
314	}
315	Py_XDECREF(mod);
316	return ret;
317}
318
319static int binary_to_xml(Buffer *buf, PyObject *obj)
320{
321	PyObject *mod;
322	PyObject *base64 = NULL;
323	int ret = 0;
324
325	mod = PyImport_ImportModule("binascii");
326	if (mod == NULL)
327		goto bail;
328	
329	base64 = PyObject_CallMethod(mod, "b2a_base64", "O", obj);
330	if (base64)
331		ret = obj_to_xml(buf, "binary", base64);
332
333bail:
334	Py_XDECREF(base64);
335	Py_XDECREF(mod);
336	return ret;
337}
338
339static int bool_to_xml(Buffer *buf, PyObject *obj)
340{
341	if (obj == Py_True)
342		return buf_extend(buf, "<boolean>true</boolean>", 23);
343	else if (obj == Py_False)
344		return buf_extend(buf, "<boolean>false</boolean>", 24);
345
346	PyErr_SetString(PyExc_TypeError, "impossible bool value");
347	return 0;
348}
349
350static int uri_to_xml(Buffer *buf, PyObject *obj)
351{
352	PyObject *strobj;
353	int ret = 0;
354
355	strobj = as_string(obj);
356	if (strobj == NULL)
357		goto bail;
358	
359	if (!buf_extend(buf, "<uri>", 5))
360		goto bail;
361
362	if (!esc_extend(buf, PyString_AS_STRING(strobj),
363			PyString_GET_SIZE(strobj)))
364		goto bail;
365	
366	if (!buf_extend(buf, "</uri>", 6))
367		goto bail;
368
369	ret = 1;
370bail:
371	Py_XDECREF(strobj);
372	return ret;
373}
374
375static int any_to_xml(Buffer *buf, PyObject *obj);
376
377static int seq_to_xml(Buffer *buf, PyObject *obj)
378{
379	Py_ssize_t len, i;
380
381	len = PySequence_Size(obj);
382
383	if (len == 0)
384		return buf_extend(buf, "<array/>", 8);
385		
386	if (!buf_extend(buf, "<array>", 7))
387		return 0;
388
389	for (i = 0; i < len; i++) {
390		if (!any_to_xml(buf, PySequence_Fast_GET_ITEM(obj, i)))
391			return 0;
392	}
393
394	return buf_extend(buf, "</array>", 8);
395}
396
397static int iter_to_xml(Buffer *buf, PyObject *obj)
398{
399	PyObject *iter, *cur = NULL;
400	int ret = 0;
401
402	iter = PyObject_GetIter(obj);
403	if (iter == NULL)
404		goto bail;
405
406	cur = PyIter_Next(iter);
407
408	if (cur == NULL) {
409		ret = buf_extend(buf, "<array/>", 8);
410		goto bail;
411	}
412	
413	if (!buf_extend(buf, "<array>", 7))
414		goto bail;
415
416	do {
417		ret = any_to_xml(buf, cur);
418		if (!ret)
419			goto bail;
420		Py_DECREF(cur);
421		cur = PyIter_Next(iter);
422	} while (cur != NULL);
423	ret = buf_extend(buf, "</array>", 8);
424
425bail:
426	Py_XDECREF(cur);
427	Py_XDECREF(iter);
428	return ret;
429}
430
431static int dict_to_xml(Buffer *buf, PyObject *obj)
432{
433	PyObject *key, *value;
434	Py_ssize_t pos = 0;
435
436	if (PyDict_Size(obj) == 0)
437		return buf_extend(buf, "<map/>", 6);
438	
439	if (!buf_extend(buf, "<map>", 5))
440		return 0;
441	
442	while (PyDict_Next(obj, &pos, &key, &value)) {
443		PyObject *strobj;
444
445		strobj = as_string(key);
446		if (strobj == NULL)
447			return 0;
448		
449		if (!buf_extend(buf, "<key>", 5))
450			return 0;
451
452		if (!esc_extend(buf, PyString_AS_STRING(strobj),
453				PyString_GET_SIZE(strobj)))
454			return 0;
455		Py_DECREF(strobj);
456
457		if (!buf_extend(buf, "</key>", 6))
458			return 0;
459
460		if (!any_to_xml(buf, value))
461			return 0;
462	}
463
464	if (!buf_extend(buf, "</map>", 6))
465		return 0;
466
467	return 1;
468}
469
470static int LLSD_to_xml(Buffer *buf, PyObject *obj)
471{
472	PyObject *thing;
473	int c = 0;
474
475	thing = PyObject_GetAttrString(obj, "thing");
476	if (thing == NULL)
477		goto bail;
478
479	c = any_to_xml(buf, thing);
480
481 bail:
482	Py_XDECREF(thing);
483	return c;
484}
485
486static int LLUUID_to_xml(Buffer *buf, PyObject *obj)
487{
488	PyObject *isNull = NULL, *strobj = NULL;
489	int ret = 0;
490
491	isNull = PyObject_CallMethod(obj, "isNull", "()");
492
493	if (isNull == Py_True) {
494		ret = buf_extend(buf, "<uuid/>", 7);
495		goto bail;
496	}
497
498	strobj = PyObject_CallMethod(obj, "toString", "()");
499	if (strobj == NULL)
500		goto bail;
501
502	ret = obj_to_xml(buf, "uuid", strobj);
503
504bail:
505	Py_XDECREF(isNull);
506	Py_XDECREF(strobj);
507	return ret;
508}
509
510static int unicode_to_xml(Buffer *buf, PyObject *obj)
511{
512	PyObject *strobj;
513	int ret = 0;
514	
515	strobj = PyUnicode_AsUTF8String(obj);
516
517	if (strobj == NULL)
518		goto bail;
519
520	ret = string_to_xml(buf, strobj);
521bail:
522	Py_XDECREF(strobj);
523	return ret;
524}
525
526static int long_to_xml(Buffer *buf, PyObject *obj)
527{
528	long val = PyLong_AsLong(obj);
529	PyObject *err = PyErr_Occurred();
530
531	if (err != NULL)
532		PyErr_Clear();
533
534	if (val != 0 || err != NULL)
535		return obj_to_xml(buf, "integer", obj);
536
537	return buf_extend(buf, "<integer/>", 10);
538}
539
540static int any_to_xml(Buffer *buf, PyObject *obj)
541{
542	if (PyDict_Check(obj))
543		return dict_to_xml(buf, obj);
544
545	/*
546	 * Do an exact string check first, deferring the more general
547	 * (and less likely) string check until after the expensive
548	 * and improbable checks for uri and binary.
549	 */
550	if (PyString_CheckExact(obj))
551		return string_to_xml(buf, obj);
552
553	if (PyUnicode_Check(obj))
554		return unicode_to_xml(buf, obj);
555
556	if (PyBool_Check(obj))
557		return bool_to_xml(buf, obj);
558
559	if (PyInt_Check(obj))
560		return int_to_xml(buf, obj);
561
562	if (PyLong_Check(obj))
563		return long_to_xml(buf, obj);
564
565	if (PyFloat_Check(obj))
566		return float_to_xml(buf, obj);
567
568	if (obj == Py_None)
569		return buf_extend(buf, "<undef/>", 8);
570
571	/* Don't use PySequence_Check here!  It's too general. */
572
573	if (PyList_Check(obj) || PyTuple_Check(obj))
574		return seq_to_xml(buf, obj);
575	
576	/*
577	 * These checks must occur before the more general check for
578	 * strings, or it will erroneously match them because they're
579	 * subclasses of str.
580	 */
581
582	if (is_module_type(obj, "indra.base.llsd", "uri"))
583		return uri_to_xml(buf, obj);
584
585	if (is_module_type(obj, "indra.base.llsd", "binary"))
586		return binary_to_xml(buf, obj);
587
588	if (PyString_Check(obj))
589		return string_to_xml(buf, obj);
590	
591	/*
592	 * Check for something iterable after we've exhausted all more
593	 * specific possibilities that support iteration.
594	 */
595	if (PyIter_Check(obj))
596		return iter_to_xml(buf, obj);
597
598	if (is_module_type(obj, "datetime", "datetime") || 
599		is_module_type(obj, "datetime", "date"))
600		return datetime_to_xml(buf, obj);
601	
602	if (is_module_type(obj, "indra.base.llsd", "LLSD"))
603		return LLSD_to_xml(buf, obj);
604
605	if (is_module_type(obj, "indra.base.lluuid", "UUID"))
606		return LLUUID_to_xml(buf, obj); 
607
608	PyErr_SetString(PyExc_TypeError, "invalid type");
609	return 0;
610}
611
612static PyObject *llsd_to_xml(PyObject *self, PyObject *args)
613{
614	PyObject *obj, *ret = NULL;
615	Buffer buf;
616
617	buf.ptr = NULL;
618
619	if (!PyArg_ParseTuple(args, "O:llsd_to_xml", &obj))
620		goto bail;
621
622	buf.size = 256;
623	buf.ptr = malloc(buf.size);
624
625	if (buf.ptr == NULL)
626	{
627		PyErr_SetString(PyExc_MemoryError, "out of memory");
628		goto bail;
629	}
630
631	buf.off = 0;
632
633	buf_extend(&buf, "<?xml version=\"1.0\" ?><llsd>", 28);
634	if (!any_to_xml(&buf, obj))
635		goto bail;
636	if (!buf_extend(&buf, "</llsd>", 7))
637		goto bail;
638
639	ret = PyString_FromStringAndSize(buf.ptr, buf.off);
640
641	goto done;
642
643bail:
644	Py_XDECREF(ret);
645	Py_XDECREF(obj);
646	ret = NULL;
647
648done:
649	if (buf.ptr)
650		free(buf.ptr);
651	return ret;
652}
653
654static char cllsd_doc[] = "Efficient LLSD parsing.";
655
656static PyMethodDef methods[] = {
657	{"llsd_to_xml", llsd_to_xml, METH_VARARGS,
658	 "Represent an LLSD value using XML encoding\n"},
659	{NULL, NULL}
660};
661
662PyMODINIT_FUNC initcllsd(void)
663{
664	Py_InitModule3("cllsd", methods, cllsd_doc);
665}