File: extraData.py

package info (click to toggle)
cf-python 1.3.2%2Bdfsg1-4
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 7,996 kB
  • sloc: python: 51,733; ansic: 2,736; makefile: 78; sh: 2
file content (140 lines) | stat: -rw-r--r-- 4,049 bytes parent folder | download
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)