PageRenderTime 103ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/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. #include <Python.h>
  33. #include <stdlib.h>
  34. #include <string.h>
  35. #if PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION < 5
  36. typedef int Py_ssize_t;
  37. #endif
  38. typedef struct
  39. {
  40. char *ptr;
  41. Py_ssize_t off;
  42. Py_ssize_t size;
  43. } Buffer;
  44. // Not a fuzzy estimation method. If we still have core dumps
  45. // due to realloc attempts, try reducing below number to 1.5
  46. #define MEM_FACTOR 2.0
  47. static inline int buf_ensure(Buffer *buf, Py_ssize_t len)
  48. {
  49. //DEV-14630 - Incorporate python module. Addressing core dump issue
  50. int asymptotic_len = fmax(buf->size * MEM_FACTOR, buf->size + len);
  51. //fprintf(stderr, "\nBuffer : %s\nBuffer Size: %d\nBuffer Offset: %d\nLength: %d\nMax(%f, %d): %d\n",
  52. // buf->ptr, buf->size, buf->off, len, buf->size * MEM_FACTOR, buf->size + len, asymptotic_len);
  53. if (len > buf->size - buf->off) {
  54. char *nptr = realloc(buf->ptr, asymptotic_len);
  55. if (nptr == NULL) {
  56. PyErr_SetString(PyExc_MemoryError, "out of memory");
  57. return 0;
  58. }
  59. buf->ptr = nptr;
  60. buf->size = asymptotic_len;
  61. }
  62. return 1;
  63. }
  64. static inline void buf_append(Buffer *buf, const char *str, Py_ssize_t len)
  65. {
  66. memcpy(buf->ptr + buf->off, str, len);
  67. buf->off += len;
  68. }
  69. static inline void buf_char_append(Buffer *buf, char c)
  70. {
  71. buf->ptr[buf->off++] = c;
  72. }
  73. static inline int buf_extend(Buffer *buf, const char *str, Py_ssize_t len)
  74. {
  75. if (!buf_ensure(buf, len))
  76. return 0;
  77. buf_append(buf, str, len);
  78. return 1;
  79. }
  80. static int esc_extend(Buffer *buf, const char *str, Py_ssize_t len)
  81. {
  82. Py_ssize_t i, excess;
  83. if (!buf_ensure(buf, len))
  84. return 0;
  85. for (i = excess = 0; i < len; i++) {
  86. switch (str[i]) {
  87. case '&':
  88. excess += 4;
  89. if (!buf_ensure(buf, len - i + excess))
  90. return 0;
  91. buf_append(buf, "&amp;", 5);
  92. break;
  93. case '<':
  94. excess += 3;
  95. if (!buf_ensure(buf, len - i + excess))
  96. return 0;
  97. buf_append(buf, "&lt;", 4);
  98. break;
  99. case '>':
  100. excess += 3;
  101. if (!buf_ensure(buf, len - i + excess))
  102. return 0;
  103. buf_append(buf, "&gt;", 4);
  104. break;
  105. default:
  106. buf_char_append(buf, str[i]);
  107. break;
  108. }
  109. }
  110. return 1;
  111. }
  112. static inline PyObject *as_string(PyObject *obj)
  113. {
  114. PyObject *strobj;
  115. if (PyString_Check(obj)) {
  116. strobj = obj;
  117. Py_INCREF(strobj);
  118. } else if (PyUnicode_Check(obj)) {
  119. strobj = PyUnicode_AsUTF8String(obj);
  120. if (!strobj)
  121. goto bail;
  122. } else {
  123. strobj = PyObject_Str(obj);
  124. if (!strobj)
  125. goto bail;
  126. if (!PyString_Check(strobj)) {
  127. Py_DECREF(strobj);
  128. strobj = NULL;
  129. PyErr_SetString(PyExc_TypeError,
  130. "str() did not return a string");
  131. }
  132. }
  133. bail:
  134. return strobj;
  135. }
  136. static int obj_to_xml(Buffer *buf, const char *name, PyObject *obj)
  137. {
  138. PyObject *strobj;
  139. size_t namelen;
  140. Py_ssize_t len;
  141. int ret = 0;
  142. char *str;
  143. strobj = as_string(obj);
  144. if (strobj == NULL)
  145. goto bail;
  146. len = PyString_GET_SIZE(strobj);
  147. str = PyString_AS_STRING(strobj);
  148. namelen = strlen(name);
  149. if (!buf_ensure(buf, namelen * 2 + 5 + len))
  150. goto bail;
  151. buf_char_append(buf, '<');
  152. buf_append(buf, name, namelen);
  153. buf_char_append(buf, '>');
  154. buf_append(buf, str, len);
  155. buf_append(buf, "</", 2);
  156. buf_append(buf, name, namelen);
  157. buf_char_append(buf, '>');
  158. ret = 1;
  159. bail:
  160. Py_XDECREF(strobj);
  161. return ret;
  162. }
  163. static int float_to_xml(Buffer *buf, PyObject *obj)
  164. {
  165. double val = PyFloat_AS_DOUBLE(obj);
  166. if (val == 0.0)
  167. return buf_extend(buf, "<real/>", 7);
  168. if (!buf_ensure(buf, 120))
  169. return 0;
  170. buf_append(buf, "<real>", 6);
  171. PyFloat_AsString(buf->ptr + buf->off,
  172. (PyFloatObject *) obj);
  173. buf->off += strlen(buf->ptr + buf->off);
  174. buf_append(buf, "</real>", 7);
  175. return 1;
  176. }
  177. static int datetime_to_xml(Buffer *buf, PyObject *obj)
  178. {
  179. int has_ms = 0;
  180. PyObject* isoobj = NULL;
  181. PyObject* strobj = NULL;
  182. char* str;
  183. Py_ssize_t len;
  184. int ret = 0;
  185. // Try to get out the microsecond value from obj. If that succeeds
  186. // then we need to use isoformat to get fractional
  187. // seconds. 2009-02-02 Phoenix
  188. has_ms = PyObject_HasAttrString(obj, "microsecond");
  189. if(has_ms)
  190. {
  191. isoobj = PyObject_CallMethod(obj, "isoformat", "()");
  192. }
  193. else
  194. {
  195. isoobj = PyObject_CallMethod(
  196. obj,
  197. "strftime",
  198. "s",
  199. "%Y-%m-%dT%H:%M:%S");
  200. }
  201. if (isoobj == NULL)
  202. goto bail;
  203. strobj = as_string(isoobj);
  204. if (strobj == NULL)
  205. goto bail;
  206. // The magic number used here is the length of the minimal date
  207. // string you can get from 'YYYY-MM-DDTHH:MM:SS' which is exactly
  208. // 19 bytes. If we used isoformat above then this string is
  209. // probably longer.
  210. len = PyString_GET_SIZE(strobj);
  211. str = PyString_AS_STRING(strobj);
  212. if (len < 19)
  213. {
  214. ret = buf_extend(buf, "<date/>", 7);
  215. goto bail;
  216. }
  217. buf_extend(buf, "<date>", 6);
  218. buf_extend(buf, str, len);
  219. buf_extend(buf, "Z</date>", 8);
  220. ret = 1;
  221. bail:
  222. Py_XDECREF(isoobj);
  223. Py_XDECREF(strobj);
  224. return ret;
  225. }
  226. static int string_to_xml(Buffer *buf, PyObject *obj)
  227. {
  228. Py_ssize_t len;
  229. len = PyString_GET_SIZE(obj);
  230. if (len) {
  231. if (!buf_extend(buf, "<string>", 8))
  232. return 0;
  233. if (!esc_extend(buf, PyString_AS_STRING(obj), len))
  234. return 0;
  235. return buf_extend(buf, "</string>", 9);
  236. }
  237. return buf_extend(buf, "<string/>", 9);
  238. }
  239. static int int_to_xml(Buffer *buf, PyObject *obj)
  240. {
  241. long val = PyInt_AS_LONG(obj);
  242. if (val == 0)
  243. return buf_extend(buf, "<integer/>", 10);
  244. if (!buf_ensure(buf, 64))
  245. return 0;
  246. buf->off += PyOS_snprintf(buf->ptr + buf->off, 64,
  247. "<integer>%ld</integer>", val);
  248. return 1;
  249. }
  250. static inline int is_module_type(PyObject *obj, const char *modulename,
  251. const char *typename)
  252. {
  253. PyObject *mod;
  254. int ret = 0;
  255. mod = PyImport_ImportModule(modulename);
  256. if (mod != NULL) {
  257. PyObject *type;
  258. type = PyObject_GetAttrString(mod, typename);
  259. if (type) {
  260. ret = PyObject_IsInstance(obj, type);
  261. Py_DECREF(type);
  262. }
  263. }
  264. Py_XDECREF(mod);
  265. return ret;
  266. }
  267. static int binary_to_xml(Buffer *buf, PyObject *obj)
  268. {
  269. PyObject *mod;
  270. PyObject *base64 = NULL;
  271. int ret = 0;
  272. mod = PyImport_ImportModule("binascii");
  273. if (mod == NULL)
  274. goto bail;
  275. base64 = PyObject_CallMethod(mod, "b2a_base64", "O", obj);
  276. if (base64)
  277. ret = obj_to_xml(buf, "binary", base64);
  278. bail:
  279. Py_XDECREF(base64);
  280. Py_XDECREF(mod);
  281. return ret;
  282. }
  283. static int bool_to_xml(Buffer *buf, PyObject *obj)
  284. {
  285. if (obj == Py_True)
  286. return buf_extend(buf, "<boolean>true</boolean>", 23);
  287. else if (obj == Py_False)
  288. return buf_extend(buf, "<boolean>false</boolean>", 24);
  289. PyErr_SetString(PyExc_TypeError, "impossible bool value");
  290. return 0;
  291. }
  292. static int uri_to_xml(Buffer *buf, PyObject *obj)
  293. {
  294. PyObject *strobj;
  295. int ret = 0;
  296. strobj = as_string(obj);
  297. if (strobj == NULL)
  298. goto bail;
  299. if (!buf_extend(buf, "<uri>", 5))
  300. goto bail;
  301. if (!esc_extend(buf, PyString_AS_STRING(strobj),
  302. PyString_GET_SIZE(strobj)))
  303. goto bail;
  304. if (!buf_extend(buf, "</uri>", 6))
  305. goto bail;
  306. ret = 1;
  307. bail:
  308. Py_XDECREF(strobj);
  309. return ret;
  310. }
  311. static int any_to_xml(Buffer *buf, PyObject *obj);
  312. static int seq_to_xml(Buffer *buf, PyObject *obj)
  313. {
  314. Py_ssize_t len, i;
  315. len = PySequence_Size(obj);
  316. if (len == 0)
  317. return buf_extend(buf, "<array/>", 8);
  318. if (!buf_extend(buf, "<array>", 7))
  319. return 0;
  320. for (i = 0; i < len; i++) {
  321. if (!any_to_xml(buf, PySequence_Fast_GET_ITEM(obj, i)))
  322. return 0;
  323. }
  324. return buf_extend(buf, "</array>", 8);
  325. }
  326. static int iter_to_xml(Buffer *buf, PyObject *obj)
  327. {
  328. PyObject *iter, *cur = NULL;
  329. int ret = 0;
  330. iter = PyObject_GetIter(obj);
  331. if (iter == NULL)
  332. goto bail;
  333. cur = PyIter_Next(iter);
  334. if (cur == NULL) {
  335. ret = buf_extend(buf, "<array/>", 8);
  336. goto bail;
  337. }
  338. if (!buf_extend(buf, "<array>", 7))
  339. goto bail;
  340. do {
  341. ret = any_to_xml(buf, cur);
  342. if (!ret)
  343. goto bail;
  344. Py_DECREF(cur);
  345. cur = PyIter_Next(iter);
  346. } while (cur != NULL);
  347. ret = buf_extend(buf, "</array>", 8);
  348. bail:
  349. Py_XDECREF(cur);
  350. Py_XDECREF(iter);
  351. return ret;
  352. }
  353. static int dict_to_xml(Buffer *buf, PyObject *obj)
  354. {
  355. PyObject *key, *value;
  356. Py_ssize_t pos = 0;
  357. if (PyDict_Size(obj) == 0)
  358. return buf_extend(buf, "<map/>", 6);
  359. if (!buf_extend(buf, "<map>", 5))
  360. return 0;
  361. while (PyDict_Next(obj, &pos, &key, &value)) {
  362. PyObject *strobj;
  363. strobj = as_string(key);
  364. if (strobj == NULL)
  365. return 0;
  366. if (!buf_extend(buf, "<key>", 5))
  367. return 0;
  368. if (!esc_extend(buf, PyString_AS_STRING(strobj),
  369. PyString_GET_SIZE(strobj)))
  370. return 0;
  371. Py_DECREF(strobj);
  372. if (!buf_extend(buf, "</key>", 6))
  373. return 0;
  374. if (!any_to_xml(buf, value))
  375. return 0;
  376. }
  377. if (!buf_extend(buf, "</map>", 6))
  378. return 0;
  379. return 1;
  380. }
  381. static int LLSD_to_xml(Buffer *buf, PyObject *obj)
  382. {
  383. PyObject *thing;
  384. int c = 0;
  385. thing = PyObject_GetAttrString(obj, "thing");
  386. if (thing == NULL)
  387. goto bail;
  388. c = any_to_xml(buf, thing);
  389. bail:
  390. Py_XDECREF(thing);
  391. return c;
  392. }
  393. static int LLUUID_to_xml(Buffer *buf, PyObject *obj)
  394. {
  395. PyObject *isNull = NULL, *strobj = NULL;
  396. int ret = 0;
  397. isNull = PyObject_CallMethod(obj, "isNull", "()");
  398. if (isNull == Py_True) {
  399. ret = buf_extend(buf, "<uuid/>", 7);
  400. goto bail;
  401. }
  402. strobj = PyObject_CallMethod(obj, "toString", "()");
  403. if (strobj == NULL)
  404. goto bail;
  405. ret = obj_to_xml(buf, "uuid", strobj);
  406. bail:
  407. Py_XDECREF(isNull);
  408. Py_XDECREF(strobj);
  409. return ret;
  410. }
  411. static int unicode_to_xml(Buffer *buf, PyObject *obj)
  412. {
  413. PyObject *strobj;
  414. int ret = 0;
  415. strobj = PyUnicode_AsUTF8String(obj);
  416. if (strobj == NULL)
  417. goto bail;
  418. ret = string_to_xml(buf, strobj);
  419. bail:
  420. Py_XDECREF(strobj);
  421. return ret;
  422. }
  423. static int long_to_xml(Buffer *buf, PyObject *obj)
  424. {
  425. long val = PyLong_AsLong(obj);
  426. PyObject *err = PyErr_Occurred();
  427. if (err != NULL)
  428. PyErr_Clear();
  429. if (val != 0 || err != NULL)
  430. return obj_to_xml(buf, "integer", obj);
  431. return buf_extend(buf, "<integer/>", 10);
  432. }
  433. static int any_to_xml(Buffer *buf, PyObject *obj)
  434. {
  435. if (PyDict_Check(obj))
  436. return dict_to_xml(buf, obj);
  437. /*
  438. * Do an exact string check first, deferring the more general
  439. * (and less likely) string check until after the expensive
  440. * and improbable checks for uri and binary.
  441. */
  442. if (PyString_CheckExact(obj))
  443. return string_to_xml(buf, obj);
  444. if (PyUnicode_Check(obj))
  445. return unicode_to_xml(buf, obj);
  446. if (PyBool_Check(obj))
  447. return bool_to_xml(buf, obj);
  448. if (PyInt_Check(obj))
  449. return int_to_xml(buf, obj);
  450. if (PyLong_Check(obj))
  451. return long_to_xml(buf, obj);
  452. if (PyFloat_Check(obj))
  453. return float_to_xml(buf, obj);
  454. if (obj == Py_None)
  455. return buf_extend(buf, "<undef/>", 8);
  456. /* Don't use PySequence_Check here! It's too general. */
  457. if (PyList_Check(obj) || PyTuple_Check(obj))
  458. return seq_to_xml(buf, obj);
  459. /*
  460. * These checks must occur before the more general check for
  461. * strings, or it will erroneously match them because they're
  462. * subclasses of str.
  463. */
  464. if (is_module_type(obj, "indra.base.llsd", "uri"))
  465. return uri_to_xml(buf, obj);
  466. if (is_module_type(obj, "indra.base.llsd", "binary"))
  467. return binary_to_xml(buf, obj);
  468. if (PyString_Check(obj))
  469. return string_to_xml(buf, obj);
  470. /*
  471. * Check for something iterable after we've exhausted all more
  472. * specific possibilities that support iteration.
  473. */
  474. if (PyIter_Check(obj))
  475. return iter_to_xml(buf, obj);
  476. if (is_module_type(obj, "datetime", "datetime") ||
  477. is_module_type(obj, "datetime", "date"))
  478. return datetime_to_xml(buf, obj);
  479. if (is_module_type(obj, "indra.base.llsd", "LLSD"))
  480. return LLSD_to_xml(buf, obj);
  481. if (is_module_type(obj, "indra.base.lluuid", "UUID"))
  482. return LLUUID_to_xml(buf, obj);
  483. PyErr_SetString(PyExc_TypeError, "invalid type");
  484. return 0;
  485. }
  486. static PyObject *llsd_to_xml(PyObject *self, PyObject *args)
  487. {
  488. PyObject *obj, *ret = NULL;
  489. Buffer buf;
  490. buf.ptr = NULL;
  491. if (!PyArg_ParseTuple(args, "O:llsd_to_xml", &obj))
  492. goto bail;
  493. buf.size = 256;
  494. buf.ptr = malloc(buf.size);
  495. if (buf.ptr == NULL)
  496. {
  497. PyErr_SetString(PyExc_MemoryError, "out of memory");
  498. goto bail;
  499. }
  500. buf.off = 0;
  501. buf_extend(&buf, "<?xml version=\"1.0\" ?><llsd>", 28);
  502. if (!any_to_xml(&buf, obj))
  503. goto bail;
  504. if (!buf_extend(&buf, "</llsd>", 7))
  505. goto bail;
  506. ret = PyString_FromStringAndSize(buf.ptr, buf.off);
  507. goto done;
  508. bail:
  509. Py_XDECREF(ret);
  510. Py_XDECREF(obj);
  511. ret = NULL;
  512. done:
  513. if (buf.ptr)
  514. free(buf.ptr);
  515. return ret;
  516. }
  517. static char cllsd_doc[] = "Efficient LLSD parsing.";
  518. static PyMethodDef methods[] = {
  519. {"llsd_to_xml", llsd_to_xml, METH_VARARGS,
  520. "Represent an LLSD value using XML encoding\n"},
  521. {NULL, NULL}
  522. };
  523. PyMODINIT_FUNC initcllsd(void)
  524. {
  525. Py_InitModule3("cllsd", methods, cllsd_doc);
  526. }