Commits
Darrell Schiebel authored d71c4b15af7
1 - | //# table_convert.h |
2 - | //# Copyright (C) 2022,2023 |
3 - | //# Associated Universities, Inc. Washington DC, USA. |
4 - | //# |
5 - | //# This library is free software; you can redistribute it and/or modify it |
6 - | //# under the terms of the GNU Library General Public License as published by |
7 - | //# the Free Software Foundation; either version 2 of the License, or (at your |
8 - | //# option) any later version. |
9 - | //# |
10 - | //# This library is distributed in the hope that it will be useful, but WITHOUT |
11 - | //# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
12 - | //# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public |
13 - | //# License for more details. |
14 - | //# |
15 - | //# You should have received a copy of the GNU Library General Public License |
16 - | //# along with this library; if not, write to the Free Software Foundation, |
17 - | //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. |
18 - | //# |
19 - | //# Correspondence concerning AIPS++ should be addressed as follows: |
20 - | //# Internet email: aips2-request@nrao.edu. |
21 - | //# Postal address: AIPS++ Project Office |
22 - | //# National Radio Astronomy Observatory |
23 - | //# 520 Edgemont Road |
24 - | //# Charlottesville, VA 22903-2475 USA |
25 - | //# |
26 - | |
27 - | |
28 - | |
29 - | |
30 - | //#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION |
31 - | |
32 - | |
33 - | |
34 - | |
35 - | |
36 - | |
37 - | |
38 - | // ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- |
39 - | // These conversion functions are used by tablerow_cmpt.cc and table_getcoliter.cc to |
40 - | // convert casacore C++ values into python values. These functions allocate python |
41 - | // memory so the PyGILState_Ensure( ) must be used before calling these functions to |
42 - | // ensure that the GIL is locked (preventing any other Python activity) while these |
43 - | // functions do the conversion, allocating python ojects in the process. |
44 - | // |
45 - | // Previously, these functions were included as static functions within |
46 - | // tablerow_cmpt.cc which was introduced as part of CAS-13894. |
47 - | // |
48 - | // ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- |
49 - | namespace casac { |
50 - | |
51 - | inline size_t non_zero( size_t val ) { |
52 - | return val <= 0 ? 5 : val; |
53 - | } |
54 - | |
55 - | // convert a boolean value to a PyObject |
56 - | inline PyObject *toPy( bool b ) { |
57 - | if ( b ) { Py_INCREF(Py_True); return Py_True; } |
58 - | else { Py_INCREF(Py_False); return Py_False; } |
59 - | } |
60 - | |
61 - | // convert numeric scalars to a PyObject |
62 - | |
63 - | |
64 - | |
65 - | |
66 - | |
67 - | |
68 - | PY_NUM_SCALAR( int8_t, NPY_INT8 ) |
69 - | PY_NUM_SCALAR( uint8_t, NPY_UINT8 ) |
70 - | PY_NUM_SCALAR( int16_t, NPY_INT16 ) |
71 - | PY_NUM_SCALAR( uint16_t, NPY_UINT16 ) |
72 - | PY_NUM_SCALAR( int32_t, NPY_INT32 ) |
73 - | PY_NUM_SCALAR( uint32_t, NPY_UINT32 ) |
74 - | PY_NUM_SCALAR( int64_t, NPY_INT64 ) |
75 - | PY_NUM_SCALAR( uint64_t, NPY_UINT64 ) |
76 - | PY_NUM_SCALAR( float, NPY_FLOAT ) |
77 - | PY_NUM_SCALAR( double, NPY_DOUBLE ) |
78 - | PY_NUM_SCALAR( casacore::Complex, NPY_COMPLEX64 ) |
79 - | PY_NUM_SCALAR( casacore::DComplex, NPY_COMPLEX128 ) |
80 - | |
81 - | // convert a string to a PyObject |
82 - | inline PyObject *toPy( const casacore::String &s ) { return PyUnicode_FromString(s.c_str( )); } |
83 - | |
84 - | // convert an array of strings to a PyObject |
85 - | inline PyObject *toPy( const casacore::Array<casacore::String> &a ) { |
86 - | auto shape = a.shape( ); |
87 - | size_t stringlen = std::accumulate( a.begin( ), a.end( ), (size_t) 0, []( size_t tally, const casacore::String &s ) { return s.size( ) > tally ? s.length( ) : tally; } ); |
88 - | size_t memlen = a.nelements( ) * non_zero(stringlen) * sizeof(uint32_t); |
89 - | void *mem = PyDataMem_NEW(memlen); |
90 - | uint32_t *ptr = reinterpret_cast<uint32_t*>(mem); |
91 - | for ( const auto &str : a ) { |
92 - | for ( size_t i=0; i < non_zero(stringlen); ++i ) { |
93 - | *ptr++ = i < str.size( ) ? (unsigned char) str[i] : 0; |
94 - | } |
95 - | } |
96 - | return PyArray_New( &PyArray_Type, shape.nelements( ), (npy_intp*) shape.storage( ), NPY_UNICODE, nullptr, mem, non_zero(stringlen)*sizeof(uint32_t), NPY_ARRAY_OWNDATA | NPY_ARRAY_FARRAY, nullptr ); |
97 - | } |
98 - | |
99 - | // convert numeric arrays to PyObjects |
100 - | // --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- |
101 - | // Allocating the result object with: |
102 - | // |
103 - | // PyObject *ndarray = PyArray_New( &PyArray_Type, shape.nelements( ), (npy_intp*) shape.storage( ), NUMPY_TYPE, |
104 - | // nullptr, nullptr, 0, NPY_ARRAY_FARRAY, nullptr ); |
105 - | // |
106 - | // and then filling it after the fact with: |
107 - | // |
108 - | // bool free_storage = false; |
109 - | // auto storage = a.getStorage( free_storage ); |
110 - | // std::memcpy( PyArray_DATA( reinterpret_cast<PyArrayObject *>(ndarray)), |
111 - | // storage, a.nelements( ) * sizeof(CASACORE_TYPE) ); |
112 - | // PyArray_ENABLEFLAGS( reinterpret_cast<PyArrayObject *>(ndarray), NPY_ARRAY_OWNDATA ); |
113 - | // if ( free_storage ) delete storage; |
114 - | // |
115 - | // worked on RHEL7 + python 3.6 but fails on macos 10.15 + python 3.8 |
116 - | // |
117 - | |
118 - | |
119 - | |
120 - | |
121 - | |
122 - | |
123 - | |
124 - | |
125 - | |
126 - | |
127 - | |
128 - | /*** setting NPY_ARRAY_OWNDATA here is required to avoid memory leak of allocated data (rhel7 + python 3.6) ***/ \ |
129 - | PyArray_ENABLEFLAGS( reinterpret_cast<PyArrayObject*>(result), NPY_ARRAY_OWNDATA ); \ |
130 - | return result; \ |
131 - | } |
132 - | |
133 - | PY_NUM_ARRAY( bool, NPY_BOOL ) |
134 - | PY_NUM_ARRAY( int8_t, NPY_INT8 ) |
135 - | PY_NUM_ARRAY( uint8_t, NPY_UINT8 ) |
136 - | PY_NUM_ARRAY( int16_t, NPY_INT16 ) |
137 - | PY_NUM_ARRAY( uint16_t, NPY_UINT16 ) |
138 - | PY_NUM_ARRAY( int32_t, NPY_INT32 ) |
139 - | PY_NUM_ARRAY( uint32_t, NPY_UINT32 ) |
140 - | PY_NUM_ARRAY( int64_t, NPY_INT64 ) |
141 - | PY_NUM_ARRAY( uint64_t, NPY_UINT64 ) |
142 - | PY_NUM_ARRAY( float, NPY_FLOAT ) |
143 - | PY_NUM_ARRAY( double, NPY_DOUBLE ) |
144 - | PY_NUM_ARRAY( casacore::Complex, NPY_COMPLEX64 ) |
145 - | PY_NUM_ARRAY( casacore::DComplex, NPY_COMPLEX128 ) |
146 - | |
147 - | inline PyObject *toPy( const casacore::Record &rec ) { |
148 - | using namespace casacore; |
149 - | |
150 - | // build map from table cell types to conversion functions |
151 - | std::map<int,std::function<PyObject*(size_t i)>> function_map = { {TpBool,[&](size_t i) ->PyObject* { return toPy(rec.asBool(i)); }}, |
152 - | {TpChar,[&](size_t i) ->PyObject* { return toPy(rec.asuChar(i)); }}, |
153 - | {TpUChar,[&](size_t i) ->PyObject* { return toPy(rec.asuChar(i)); }}, |
154 - | {TpShort,[&](size_t i) ->PyObject* { return toPy(rec.asShort(i)); }}, |
155 - | {TpUShort,[&](size_t i) ->PyObject* { return toPy(rec.asShort(i)); }}, |
156 - | {TpInt,[&](size_t i) ->PyObject* { return toPy(rec.asInt(i)); }}, |
157 - | {TpUInt,[&](size_t i) ->PyObject* { return toPy(rec.asuInt(i)); }}, |
158 - | {TpInt64,[&](size_t i) ->PyObject* { return toPy((int64_t)rec.asInt64(i)); }}, |
159 - | {TpFloat,[&](size_t i) ->PyObject* { return toPy(rec.asFloat(i)); }}, |
160 - | {TpDouble,[&](size_t i) ->PyObject* { return toPy(rec.asDouble(i)); }}, |
161 - | {TpComplex,[&](size_t i) ->PyObject* { return toPy(rec.asComplex(i)); }}, |
162 - | {TpDComplex,[&](size_t i) ->PyObject* { return toPy(rec.asDComplex(i)); }}, |
163 - | {TpArrayBool,[&](size_t i) ->PyObject* { return toPy(rec.asArrayBool(i)); }}, |
164 - | {TpArrayUChar,[&](size_t i) ->PyObject* { return toPy(rec.asArrayuChar(i)); }}, |
165 - | {TpArrayChar,[&](size_t i) ->PyObject* { return toPy(rec.asArrayuChar(i)); }}, |
166 - | {TpArrayShort,[&](size_t i) ->PyObject* { return toPy(rec.asArrayShort(i)); }}, |
167 - | {TpArrayUShort,[&](size_t i) ->PyObject* { return toPy(rec.asArrayShort(i)); }}, |
168 - | {TpArrayInt,[&](size_t i) ->PyObject* { return toPy(rec.asArrayInt(i)); }}, |
169 - | {TpArrayUInt,[&](size_t i) ->PyObject* { return toPy(rec.asArrayuInt(i)); }}, |
170 - | {TpArrayFloat,[&](size_t i) ->PyObject* { return toPy(rec.asArrayFloat(i)); }}, |
171 - | {TpArrayDouble,[&](size_t i) ->PyObject* { return toPy(rec.asArrayDouble(i)); }}, |
172 - | {TpArrayComplex,[&](size_t i) ->PyObject* { return toPy(rec.asArrayComplex(i)); }}, |
173 - | {TpArrayDComplex,[&](size_t i) ->PyObject* { return toPy(rec.asArrayDComplex(i)); }}, |
174 - | {TpString,[&](size_t i) ->PyObject* { return toPy(rec.asString(i)); }}, |
175 - | {TpRecord,[&](size_t i) ->PyObject* { return toPy(rec.asRecord(i)); }} |
176 - | }; |
177 - | |
178 - | // create result |
179 - | auto result = PyDict_New( ); |
180 - | if ( result == nullptr ) throw PyExc_MemoryError; |
181 - | // loop through record fields |
182 - | for ( uInt i=0; i < rec.nfields( ); ++i ) { |
183 - | auto func = function_map.find( rec.dataType(i) ); |
184 - | // lookup conversion function |
185 - | if ( func != function_map.end( ) ) { |
186 - | auto newobj = func->second(i); |
187 - | auto name = PyUnicode_FromString(rec.name(i).c_str( )); |
188 - | // set field in result |
189 - | if ( PyDict_SetItem( result, name, newobj ) != 0 ) { |
190 - | Py_DECREF(result); |
191 - | Py_DECREF(newobj); |
192 - | Py_DECREF(name); |
193 - | throw PyExc_ValueError; |
194 - | } |
195 - | Py_DECREF(newobj); |
196 - | Py_DECREF(name); |
197 - | } else { |
198 - | Py_DECREF(result); |
199 - | throw PyExc_TypeError; |
200 - | } |
201 - | } |
202 - | return result; |
203 - | } |
204 - | |
205 - | inline PyObject *toPy( const casacore::ValueHolder &hldr ) { |
206 - | using namespace casacore; |
207 - | |
208 - | // build map from table cell types to conversion functions |
209 - | std::map<int,std::function<PyObject*( )>> function_map = { {TpBool,[&]( ) ->PyObject* { return toPy(hldr.asBool( )); }}, |
210 - | {TpChar,[&]( ) ->PyObject* { return toPy(hldr.asuChar( )); }}, |
211 - | {TpUChar,[&]( ) ->PyObject* { return toPy(hldr.asuChar( )); }}, |
212 - | {TpShort,[&]( ) ->PyObject* { return toPy(hldr.asShort( )); }}, |
213 - | {TpUShort,[&]( ) ->PyObject* { return toPy(hldr.asuShort( )); }}, |
214 - | {TpInt,[&]( ) ->PyObject* { return toPy(hldr.asInt( )); }}, |
215 - | {TpUInt,[&]( ) ->PyObject* { return toPy(hldr.asuInt( )); }}, |
216 - | {TpInt64,[&]( ) ->PyObject* { return toPy((int64_t)hldr.asInt64( )); }}, |
217 - | {TpFloat,[&]( ) ->PyObject* { return toPy(hldr.asFloat( )); }}, |
218 - | {TpDouble,[&]( ) ->PyObject* { return toPy(hldr.asDouble( )); }}, |
219 - | {TpComplex,[&]( ) ->PyObject* { return toPy(hldr.asComplex( )); }}, |
220 - | {TpDComplex,[&]( ) ->PyObject* { return toPy(hldr.asDComplex( )); }}, |
221 - | {TpString,[&]( ) ->PyObject* { return toPy(hldr.asString( )); }}, |
222 - | {TpArrayBool,[&]( ) ->PyObject* { return toPy(hldr.asArrayBool( )); }}, |
223 - | {TpArrayUChar,[&]( ) ->PyObject* { return toPy(hldr.asArrayuChar( )); }}, |
224 - | {TpArrayChar,[&]( ) ->PyObject* { return toPy(hldr.asArrayuChar( )); }}, |
225 - | {TpArrayShort,[&]( ) ->PyObject* { return toPy(hldr.asArrayShort( )); }}, |
226 - | {TpArrayUShort,[&]( ) ->PyObject* { return toPy(hldr.asArrayuShort( )); }}, |
227 - | {TpArrayInt,[&]( ) ->PyObject* { return toPy(hldr.asArrayInt( )); }}, |
228 - | {TpArrayUInt,[&]( ) ->PyObject* { return toPy(hldr.asArrayuInt( )); }}, |
229 - | {TpArrayInt64,[&]( ) ->PyObject* { return toPy(hldr.asArrayInt64( )); }}, |
230 - | {TpArrayFloat,[&]( ) ->PyObject* { return toPy(hldr.asArrayFloat( )); }}, |
231 - | {TpArrayDouble,[&]( ) ->PyObject* { return toPy(hldr.asArrayDouble( )); }}, |
232 - | {TpArrayComplex,[&]( ) ->PyObject* { return toPy(hldr.asArrayComplex( )); }}, |
233 - | {TpArrayDComplex,[&]( ) ->PyObject* { return toPy(hldr.asArrayDComplex( )); }}, |
234 - | {TpArrayString,[&]( ) ->PyObject* { return toPy(hldr.asArrayString( )); }}, |
235 - | {TpRecord,[&]( ) ->PyObject* { return toPy(hldr.asRecord( )); }} |
236 - | }; |
237 - | |
238 - | auto func = function_map.find( hldr.dataType( ) ); |
239 - | // lookup conversion function |
240 - | if ( func != function_map.end( ) ) { |
241 - | return func->second( ); |
242 - | } else { |
243 - | throw PyExc_TypeError; |
244 - | } |
245 - | } |
246 - | } |