1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
|
import sys
import string
import numpy
_codes = {
1 : ('x', float),
2 : ('y', float),
3 : ('y_domain_lower_bound', float),
4 : ('x_domain_lower_bound', float),
5 : ('y_domain_upper_bound', float),
6 : ('x_domain_upper_bound', float),
7 : ('z_domain_lower_bound', float),
8 : ('x_domain_upper_bound', float),
10 : ('title', str),
11 : ('domain_title', str),
12 : ('x_lower_bound', float),
13 : ('x_upper_bound', float),
14 : ('y_lower_bound', float),
15 : ('y_upper_bound', float),
}
class ExtraData(dict):
"""
Extends dictionary class with a comparison method between extra
data for different records.
"""
_key_to_type = dict([(key, typ) for key, typ in _codes.values()])
def sorted_keys(self):
k = self.keys()
k.sort()
return k
_tolerances = {numpy.dtype(numpy.float32): 1e-5,
numpy.dtype(numpy.float64): 1e-13}
def _cmp_floats(self, a, b, tolerance):
if a == b:
return 0
delta = abs(b * tolerance)
if a < b - delta: return -1
if a > b + delta: return 1
return 0
def _cmp_float_arrays(self, avals, bvals):
n = len(avals)
c = cmp(n, len(bvals))
if c != 0:
return c
tolerance = self._tolerances[avals.dtype]
for i in range(n):
c = self._cmp_floats(avals[i], bvals[i], tolerance)
if c != 0:
return c
return 0
def __cmp__(self, other):
"""
Compare two extra data dictionaries returned by unpacker
"""
if other == None:
return 1
ka = self.sorted_keys()
kb = other.sorted_keys()
c = cmp(ka, kb)
if c != 0:
return c
for key in ka:
valsa = self[key]
valsb = other[key]
typ = self._key_to_type[key]
if typ == float:
c = self._cmp_float_arrays(valsa, valsb)
elif type == str:
c = cmp(valsa, valsb)
else:
assert(False)
if c != 0:
return c
return 0
class ExtraDataUnpacker:
_int_types = {4: numpy.int32, 8: numpy.int64}
_float_types = {4: numpy.float32, 8: numpy.float64}
def __init__(self, raw_extra_data, word_size, byte_ordering):
self.rdata = raw_extra_data
self.ws = word_size
self.itype = self._int_types[word_size]
self.ftype = self._float_types[word_size]
# byte_ordering is 'little_endian' or 'big_endian'
# sys.byteorder is 'little' or 'big'
self.is_swapped = not byte_ordering.startswith(sys.byteorder)
def next_words(self, n):
"""
return next n words as raw data string, and pop them off the
front of the string
"""
pos = n * self.ws
rv = self.rdata[:pos]
assert(len(rv) == pos)
self.rdata = self.rdata[pos:]
return rv
def tweak_string(self, st):
"""
undo byte-swapping of string and remove trailing NULs
"""
if self.is_swapped:
# concatenate backwards substrings
st = string.join([st[pos : pos + self.ws][::-1]
for pos in range(0, len(st), self.ws)], "")
while st.endswith("\x00"):
st = st[:-1]
return st
def get_data(self):
"""
get list of (key, value) for extra data
"""
d = {}
while self.rdata:
i = numpy.fromstring(self.next_words(1), self.itype)[0]
if i == 0:
break
ia, ib = (i / 1000, i % 1000)
key, type = _codes[ib]
rawvals = self.next_words(ia)
if type == float:
vals = numpy.fromstring(rawvals, self.ftype)
elif type == str:
vals = self.tweak_string(rawvals)
d[key] = vals
return ExtraData(d)
|