File: C:/Users/fred/anaconda3/Lib/site-packages/astropy/io/votable/src/tablewriter.c
/******************************************************************************
* C extension code for vo.table.
*
* Everything in this file has an alternate Python implementation and
* is included for performance reasons only.
*
* This contains a write_tabledata function to quickly write out a Numpy array
* in TABLEDATA format.
*
******************************************************************************/
#define PY_SSIZE_T_CLEAN
#include <Python.h>
/******************************************************************************
* Convenience macros and functions
******************************************************************************/
#undef CLAMP
#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
static Py_ssize_t
next_power_of_2(Py_ssize_t n)
{
/* Calculate the next-highest power of two */
n--;
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
n |= n >> 16;
n++;
return n == 0 ? 2 : n;
}
/******************************************************************************
* Write TABLEDATA
******************************************************************************/
#define CHAR char
/*
* Reallocate the write buffer to the requested size
*/
static int
_buffer_realloc(
CHAR** buffer, Py_ssize_t* buffer_size, CHAR** x, Py_ssize_t req_size)
{
Py_ssize_t n = req_size;
CHAR * new_mem = NULL;
if (req_size < *buffer_size) {
return 0;
}
/* Calculate the next-highest power of two */
n = next_power_of_2(n);
if (n < req_size) {
PyErr_SetString(PyExc_MemoryError, "Out of memory for XML text.");
return -1;
}
new_mem = PyMem_Realloc((void *)*buffer, n * sizeof(CHAR));
if (new_mem == NULL) {
PyErr_SetString(PyExc_MemoryError, "Out of memory for XML text.");
return -1;
}
*x = (CHAR *)new_mem + (*x - *buffer);
*buffer = new_mem;
*buffer_size = n;
return 0;
}
/*
* Write *indent* spaces to the buffer
*/
static int
_write_indent(CHAR** buffer, Py_ssize_t* buffer_size,
CHAR** x, Py_ssize_t indent)
{
if (_buffer_realloc(buffer, buffer_size, x,
(*x - *buffer + indent))) {
return 1;
}
for (; indent; --indent) {
*(*x)++ = ' ';
}
return 0;
}
/*
* Write a string into a buffer.
*/
static int
_write_string(CHAR** buffer, Py_ssize_t* buffer_size,
CHAR** x, const CHAR* src, const Py_ssize_t len) {
if (_buffer_realloc(buffer, buffer_size, x,
(*x - *buffer + len))) {
return 1;
}
while (*src != (CHAR)0) {
*(*x)++ = *src++;
}
return 0;
}
/*
* Write an 8-bit ascii-encoded C string to a Unicode string.
*/
static int
_write_cstring(CHAR** buffer, Py_ssize_t* buffer_size,
CHAR** x, const char* src, const Py_ssize_t len) {
if (_buffer_realloc(buffer, buffer_size, x,
(*x - *buffer + len))) {
return 1;
}
while (*src != (char)0) {
*(*x)++ = *src++;
}
return 0;
}
/*
* Write a TABLEDATA element tree to the given write method.
*
* The Python arguments are:
*
* *write_method* (callable): A Python callable that takes a unicode
* string and writes it to a file or buffer.
*
* *array* (numpy structured array): A Numpy record array containing
* the data
*
* *mask* (numpy array): A Numpy array which is True everywhere a
* value is missing. Must have the same shape as *array*.
*
* *converters* (list of callables): A sequence of methods which
* convert from the native data types in the columns of *array* to
* a unicode string in VOTABLE XML format. Must have the same
* length as the number of columns in *array*.
*
* *write_null_values* (boolean): When True, write null values in
* their entirety in the table. When False, just write empty <TD/>
* elements when the data is null or missing.
*
* *indent* (integer): The number of spaces to indent the table.
*
* *buf_size* (integer): The size of the write buffer.
*
* Returns None.
*/
static PyObject*
write_tabledata(PyObject* self, PyObject *args, PyObject *kwds)
{
/* Inputs */
PyObject* write_method = NULL;
PyObject* array = NULL;
PyObject* mask = NULL;
PyObject* converters = NULL;
PyObject* py_supports_empty_values = NULL;
Py_ssize_t indent = 0;
Py_ssize_t buf_size = (Py_ssize_t)1 << 8;
/* Output buffer */
CHAR* buf = NULL;
CHAR* x;
Py_ssize_t nrows = 0;
Py_ssize_t ncols = 0;
Py_ssize_t i, j;
int write_full;
int all;
PyObject* numpy_module = NULL;
PyObject* numpy_all_method = NULL;
PyObject* array_row = NULL;
PyObject* mask_row = NULL;
PyObject* array_val = NULL;
PyObject* mask_val = NULL;
PyObject* converter = NULL;
PyObject* all_masked_obj = NULL;
PyObject* str_val = NULL;
PyObject* tmp = NULL;
const CHAR* str_tmp = NULL;
Py_ssize_t str_len = 0;
int* supports_empty_values = NULL;
PyObject* result = 0;
if (!PyArg_ParseTuple(args, "OOOOOnn:write_tabledata",
&write_method, &array, &mask, &converters,
&py_supports_empty_values, &indent, &buf_size)) {
goto exit;
}
if (!PyCallable_Check(write_method)) goto exit;
if (!PySequence_Check(array)) goto exit;
if (!PySequence_Check(mask)) goto exit;
if (!PyList_Check(converters)) goto exit;
if (!PyList_Check(py_supports_empty_values)) goto exit;
indent = CLAMP(indent, (Py_ssize_t)0, (Py_ssize_t)80);
buf_size = CLAMP(buf_size, (Py_ssize_t)1 << 8, (Py_ssize_t)1 << 24);
if ((numpy_module = PyImport_ImportModule("numpy")) == NULL) goto exit;
if ((numpy_all_method = PyObject_GetAttrString(numpy_module, "all"))
== NULL) goto exit;
if ((nrows = PySequence_Size(array)) == -1) goto exit;
if ((ncols = PyList_Size(converters)) == -1) goto exit;
if (PyList_Size(py_supports_empty_values) != ncols) goto exit;
supports_empty_values = PyMem_Malloc(sizeof(int) * ncols);
if (!supports_empty_values) goto exit;
for (i = 0; i < ncols; ++i) {
supports_empty_values[i] = PyObject_IsTrue(
PyList_GET_ITEM(py_supports_empty_values, i));
}
if ((buf = PyMem_Malloc((size_t)buf_size * sizeof(CHAR))) == NULL) goto exit;
for (i = 0; i < nrows; ++i) {
if ((array_row = PySequence_GetItem(array, i)) == NULL) goto exit;
if ((mask_row = PySequence_GetItem(mask, i)) == NULL) goto exit;
x = buf;
if (_write_indent(&buf, &buf_size, &x, indent)) goto exit;
if (_write_cstring(&buf, &buf_size, &x, " <TR>\n", 6)) goto exit;
for (j = 0; j < ncols; ++j) {
if ((converter = PyList_GET_ITEM(converters, j)) == NULL) goto exit;
if ((array_val = PySequence_GetItem(array_row, j)) == NULL) goto exit;
if ((mask_val = PySequence_GetItem(mask_row, j)) == NULL) goto exit;
write_full = 1;
if (mask_val == Py_True) {
write_full = 0;
} else if (mask_val == Py_False) {
// pass
} else if (supports_empty_values[j]) {
if ((all_masked_obj =
PyObject_CallFunctionObjArgs(numpy_all_method, mask_val, NULL))
== NULL) goto exit;
if ((all = PyObject_IsTrue(all_masked_obj)) == -1) {
Py_DECREF(all_masked_obj);
goto exit;
}
Py_DECREF(all_masked_obj);
write_full = !all;
}
if (write_full) {
if (_write_indent(&buf, &buf_size, &x, indent)) goto exit;
if ((str_val =
PyObject_CallFunctionObjArgs(converter, array_val, mask_val, NULL))
== NULL) goto exit;
if (PyBytes_Check(str_val)) {
tmp = PyUnicode_FromEncodedObject(str_val, "utf-8", "ignore");
Py_DECREF(str_val);
str_val = tmp;
}
if ((str_tmp = PyUnicode_AsUTF8AndSize(str_val, &str_len)) == NULL) {
Py_DECREF(str_val);
goto exit;
}
if (str_len) {
if (_write_cstring(&buf, &buf_size, &x, " <TD>", 6) ||
_write_string(&buf, &buf_size, &x, str_tmp, str_len) ||
_write_cstring(&buf, &buf_size, &x, "</TD>\n", 6)) {
Py_DECREF(str_val);
goto exit;
}
} else {
if (_write_cstring(&buf, &buf_size, &x, " <TD/>\n", 8)) {
Py_DECREF(str_val);
goto exit;
}
}
Py_DECREF(str_val);
} else {
if (_write_indent(&buf, &buf_size, &x, indent)) goto exit;
if (_write_cstring(&buf, &buf_size, &x, " <TD/>\n", 8)) goto exit;
}
Py_DECREF(array_val); array_val = NULL;
Py_DECREF(mask_val); mask_val = NULL;
}
Py_DECREF(array_row); array_row = NULL;
Py_DECREF(mask_row); mask_row = NULL;
if (_write_indent(&buf, &buf_size, &x, indent)) goto exit;
if (_write_cstring(&buf, &buf_size, &x, " </TR>\n", 7)) goto exit;
/* NULL-terminate the string */
*x = (CHAR)0;
if ((tmp = PyObject_CallFunction(write_method, "s#",
buf, (Py_ssize_t)(x - buf))) == NULL) goto exit;
Py_DECREF(tmp);
}
Py_INCREF(Py_None);
result = Py_None;
exit:
Py_XDECREF(numpy_module);
Py_XDECREF(numpy_all_method);
Py_XDECREF(array_row);
Py_XDECREF(mask_row);
Py_XDECREF(array_val);
Py_XDECREF(mask_val);
PyMem_Free(buf);
PyMem_Free(supports_empty_values);
return result;
}
/******************************************************************************
* Module setup
******************************************************************************/
static PyMethodDef module_methods[] =
{
{"write_tabledata", (PyCFunction)write_tabledata, METH_VARARGS,
"Fast C method to write tabledata"},
{NULL} /* Sentinel */
};
struct module_state {
void* none;
};
static int module_traverse(PyObject* m, visitproc visit, void* arg)
{
return 0;
}
static int module_clear(PyObject* m)
{
return 0;
}
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
"tablewriter",
"Fast way to write VOTABLE TABLEDATA",
sizeof(struct module_state),
module_methods,
NULL,
module_traverse,
module_clear,
NULL
};
PyMODINIT_FUNC
PyInit_tablewriter(void)
{
return PyModule_Create(&moduledef);
}