1 """
2 Binary VOTable encoding.
3 """
4
5
6
7
8
9
10
11 import datetime
12 import struct
13
14 from gavo import utils
15 from gavo.utils import pgsphere
16 from gavo.votable import coding
17 from gavo.votable import common
18
19
20 floatNaN = struct.pack("!f", common.NaN)
21 doubleNaN = struct.pack("!d", common.NaN)
22
23
25 """returns common code for almost all array serialization.
26
27 Field must describe an array (as opposed to a single value).
28
29 padder must be python-source for whatever is used to pad
30 arrays that are too short.
31 """
32 base = [
33 "if val is None: val = []"]
34 if field.isMultiDim():
35
36 base.append("val = coding.ravel(val)")
37 if field.hasVarLength():
38 return base+["tokens.append(struct.pack('!i', len(val)))"]
39 else:
40 return base+["val = coding.trim(val, %s, %s)"%(
41 field.getLength(), padder)]
42
43
45 """adds code to let null values kick in a necessary.
46
47 nullvalue here has to be a ready-made *python* literal. Take care
48 when passing in user supplied values here.
49 """
50 if nullvalue is None:
51 action = (" raise common.BadVOTableData('None passed for field"
52 " that has no NULL value', None, '%s', hint='Integers in VOTable"
53 " have no natural serializations for missing values. You need to"
54 " define one using values null to allow for NULL in integer columns')"
55 )%field.getDesignation()
56 else:
57 action = " tokens.append(%s)"%nullvalue
58 return [
59 "if val is None:",
60 action,
61 "else:"
62 ]+common.indentList(src, " ")
63
64
66 return [
67 "if val is None:",
68 " tokens.append('?')",
69 "elif val:",
70 " tokens.append('1')",
71 "else:",
72 " tokens.append('0')",
73 ]
74
75
77
78
79 length = field.getLength()
80 if allowNULL:
81 src = [
82 "if val is None:"
83 " tokens.append('\\0\\0\\0\\0')",]
84 else:
85 src = [
86 "if val is None:",
87 " raise common.BadVOTableData('Bits have no NULL value', None,",
88 " '%s')"%field.getDesignation(),]
89
90 src.extend([
91 "else:",
92 " tmp = []",
93 " curByte, rest = val%256, val//256",
94 " while curByte:",
95 " tmp.append(chr(curByte))",
96 " curByte, rest = rest%256, rest//256",
97 " if not tmp:",
98 " tmp.append(chr(0))",
99 " tmp.reverse()",])
100
101 if length!=1:
102 if length is None:
103 src.extend([
104 " tokens.append(struct.pack('!i', len(tmp)*8))"])
105 else:
106 numBytes = int(length)//8+(not not int(length)%8)
107 src.extend([
108 " if len(tmp)<%d: tmp = [chr(0)]*(%d-len(tmp))+tmp"%(
109 numBytes, numBytes),
110 " if len(tmp)>%d: tmp = tmp[-%d:]"%(numBytes, numBytes)])
111
112 src.extend([
113 " tokens.append(struct.pack('%ds'%len(tmp), ''.join(tmp)))"])
114 return src
115
116
118 def makeFloatEncoder(field):
119 return [
120 "if val is None:",
121 " tokens.append(%s)"%nullName,
122 "else:",
123 " tokens.append(struct.pack('%s', val))"%fmtCode]
124 return makeFloatEncoder
125
126
128 def makeComplexEncoder(field):
129 return [
130 "if val is None:",
131 " tokens.append(%s+%s)"%(singleNull, singleNull),
132 "else:",
133 " tokens.append(struct.pack('%s', val.real, val.imag))"%fmtCode]
134 return makeComplexEncoder
135
136
144 return makeIntEncoder
145
146
148
149 nullvalue = coding.getNullvalue(field, int)
150 if nullvalue is not None:
151 nullvalue = repr(struct.pack("B", int(nullvalue)))
152 return _addNullvalueCode(field, nullvalue,[
153 "if isinstance(val, int):",
154 " tokens.append(struct.pack('B', val))",
155 "else:",
156 " tokens.append(struct.pack('c', val[:1]))"])
157
158
166
175
176
178
179
180 nullvalue = coding.getNullvalue(field, lambda _: True, default="")
181 src = []
182
183 src.extend(common.getXtypeEncoderCode(field))
184 src.append("val = coding.trimString(val, %s)"%repr(field.arraysize))
185
186 if field.hasVarLength():
187 src.append("tokens.append(struct.pack('!i', len(val)))")
188 if nullvalue is None:
189 nullvalue = repr('\0\0\0\0')
190 else:
191
192
193 nullvalue = repr(struct.pack("!i%ds"%len(nullvalue),
194 len(nullvalue), str(nullvalue)))
195 else:
196 if nullvalue is not None:
197 nullvalue = repr(struct.pack("%ds"%field.getLength(),
198 str(coding.trimString(nullvalue, field.arraysize))))
199
200
201 if field.datatype=="unicodeChar":
202 src.append("val = val.encode('utf-16be')")
203 elif field.datatype=="char":
204 src.extend([
205 'if isinstance(val, unicode):',
206 ' val = val.encode("ascii", "replace")'])
207
208 src.append("tokens.append(struct.pack('%ds'%len(val), val))")
209 return _addNullvalueCode(field, nullvalue, src)
210
211
212 _encoders = {
213 "boolean": _makeBooleanEncoder,
214 "bit": _makeBitEncoder,
215 "unsignedByte": _makeUnsignedByteEncoder,
216 "short": _generateIntEncoderMaker('!h'),
217 "int": _generateIntEncoderMaker('!i'),
218 "long": _generateIntEncoderMaker('!q'),
219 "char": _makeCharEncoder,
220 "unicodeChar": _makeUnicodeCharEncoder,
221 "double": _generateFloatEncoderMaker("!d", "doubleNaN"),
222 "float": _generateFloatEncoderMaker("!f", "floatNaN"),
223 "doubleComplex": _generateComplexEncoderMaker("!dd", "doubleNaN"),
224 "floatComplex": _generateComplexEncoderMaker("!ff", "floatNaN"),
225 }
226
228 """returns python lines to encode array values of field.
229 """
230 type = field.datatype
231
232
233 if type=="bit":
234 return _makeBitEncoder(field)
235
236 if type=="char" or type=="unicodeChar":
237 return _makeCharArrayEncoder(field)
238
239
240
241
242 padder = '[None]'
243 src = [
244 "fullTokens = tokens",
245 "tokens = []",
246 "if val is None:",
247 " arr = []",
248 "else:",
249 " arr = val",
250 "for val in arr:"
251 ]+common.indentList(_encoders[field.datatype](field), " ")
252
253 src.extend([
254 "fullTokens.append(''.join(tokens))",
255 "tokens = fullTokens"])
256
257 return (common.getXtypeEncoderCode(field)
258 + _getArrayShapingCode(field, padder)
259 + src)
260
261
263 """returns a sequence of python source lines to encode values described
264 by field into tabledata.
265 """
266 if field.isScalar():
267 return _encoders[field.datatype](field)
268 else:
269 return _getArrayEncoderLines(field)
270
271
272 -def getPostamble(tableDefinition):
273 return [
274 "return ''.join(tokens)"]
275
276
279