Commit | Line | Data |
---|---|---|
6dc2ca62 MD |
1 | /* |
2 | * Common Trace Format | |
3 | * | |
4 | * Floating point read/write functions. | |
5 | * | |
6 | * Copyright 2010 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com> | |
7 | * | |
8 | * Reference: ISO C99 standard 5.2.4 | |
9 | * | |
10 | * Dual LGPL v2.1/GPL v2 license. | |
11 | */ | |
12 | ||
13 | #include <ctf/ctf-types.h> | |
14 | #include <glib.h> | |
15 | #include <endian.h> | |
16 | ||
17 | /* | |
18 | * Aliasing float/double and unsigned long is not strictly permitted by strict | |
19 | * aliasing, but in practice type prunning is well supported, and this permits | |
20 | * us to use per-word read/writes rather than per-byte. | |
21 | */ | |
22 | ||
23 | #if defined(__GNUC__) || defined(__MINGW32__) || defined(_MSC_VER) | |
24 | #define HAS_TYPE_PRUNING | |
25 | #endif | |
26 | ||
27 | union floatIEEE754 { | |
28 | float v; | |
29 | #ifdef HAS_TYPE_PRUNING | |
30 | unsigned long bits[(sizeof(float) + sizeof(unsigned long) - 1) / sizeof(unsigned long)]; | |
31 | #else | |
32 | unsigned char bits[sizeof(float)]; | |
33 | #endif | |
34 | }; | |
35 | ||
36 | union doubleIEEE754 { | |
37 | double v; | |
38 | #ifdef HAS_TYPE_PRUNING | |
39 | unsigned long bits[(sizeof(double) + sizeof(unsigned long) - 1) / sizeof(unsigned long)]; | |
40 | #else | |
41 | unsigned char bits[sizeof(double)]; | |
42 | #endif | |
43 | }; | |
44 | ||
45 | double float_read(const uint8_t *ptr, size_t len, int byte_order) | |
46 | { | |
47 | int rbo = (byte_order != __FLOAT_WORD_ORDER); /* reverse byte order */ | |
48 | ||
49 | switch (len) { | |
50 | case 32: | |
51 | { | |
52 | union floatIEEE754 u; | |
53 | uint32_t tmp; | |
54 | ||
55 | if (!rbo) | |
56 | return (double) *(const float *) ptr; | |
57 | /* | |
58 | * Need to reverse byte order. Read the opposite from our | |
59 | * architecture. | |
60 | */ | |
61 | if (__FLOAT_WORD_ORDER == LITTLE_ENDIAN) { | |
62 | /* Read big endian */ | |
63 | tmp = bitfield_unsigned_read(ptr, 0, 1, BIG_ENDIAN); | |
64 | bitfield_unsigned_write(&u.bits, 31, 1, LITTLE_ENDIAN, | |
65 | tmp); | |
66 | tmp = bitfield_unsigned_read(ptr, 1, 8, BIG_ENDIAN); | |
67 | bitfield_unsigned_write(&u.bits, 23, 8, LITTLE_ENDIAN, | |
68 | tmp); | |
69 | tmp = bitfield_unsigned_read(ptr, 9, 23, BIG_ENDIAN); | |
70 | bitfield_unsigned_write(&u.bits, 0, 23, LITTLE_ENDIAN, | |
71 | tmp); | |
72 | } else { | |
73 | /* Read little endian */ | |
74 | tmp = bitfield_unsigned_read(ptr, 31, 1, LITTLE_ENDIAN); | |
75 | bitfield_unsigned_write(&u.bits, 0, 1, BIG_ENDIAN, | |
76 | tmp); | |
77 | tmp = bitfield_unsigned_read(ptr, 23, 8, LITTLE_ENDIAN); | |
78 | bitfield_unsigned_write(&u.bits, 1, 8, BIG_ENDIAN, | |
79 | tmp); | |
80 | tmp = bitfield_unsigned_read(ptr, 0, 23, LITTLE_ENDIAN); | |
81 | bitfield_unsigned_write(&u.bits, 9, 23, BIG_ENDIAN, | |
82 | tmp); | |
83 | } | |
84 | return (double) u.v; | |
85 | } | |
86 | case 64: | |
87 | { | |
88 | union doubleIEEE754 u; | |
89 | uint64_t tmp; | |
90 | ||
91 | if (!rbo) | |
92 | return (double) *(const double *) ptr; | |
93 | /* | |
94 | * Need to reverse byte order. Read the opposite from our | |
95 | * architecture. | |
96 | */ | |
97 | if (__FLOAT_WORD_ORDER == LITTLE_ENDIAN) { | |
98 | /* Read big endian */ | |
99 | tmp = bitfield_unsigned_read(ptr, 0, 1, BIG_ENDIAN); | |
100 | bitfield_unsigned_write(&u.bits, 63, 1, LITTLE_ENDIAN, | |
101 | tmp); | |
102 | tmp = bitfield_unsigned_read(ptr, 1, 11, BIG_ENDIAN); | |
103 | bitfield_unsigned_write(&u.bits, 52, 11, LITTLE_ENDIAN, | |
104 | tmp); | |
105 | tmp = bitfield_unsigned_read(ptr, 12, 52, BIG_ENDIAN); | |
106 | bitfield_unsigned_write(&u.bits, 0, 52, LITTLE_ENDIAN, | |
107 | tmp); | |
108 | } else { | |
109 | /* Read little endian */ | |
110 | tmp = bitfield_unsigned_read(ptr, 63, 1, LITTLE_ENDIAN); | |
111 | bitfield_unsigned_write(&u.bits, 0, 1, BIG_ENDIAN, | |
112 | tmp); | |
113 | tmp = bitfield_unsigned_read(ptr, 52, 11, LITTLE_ENDIAN); | |
114 | bitfield_unsigned_write(&u.bits, 1, 11, BIG_ENDIAN, | |
115 | tmp); | |
116 | tmp = bitfield_unsigned_read(ptr, 0, 52, LITTLE_ENDIAN); | |
117 | bitfield_unsigned_write(&u.bits, 12, 52, BIG_ENDIAN, | |
118 | tmp); | |
119 | } | |
120 | return u.v; | |
121 | } | |
122 | default: | |
123 | printf("float read unavailable for size %u\n", len); | |
124 | assert(0); | |
125 | } | |
126 | } | |
127 | ||
128 | size_t float_write(uint8_t *ptr, size_t len, int byte_order, double v) | |
129 | { | |
130 | int rbo = (byte_order != __FLOAT_WORD_ORDER); /* reverse byte order */ | |
131 | ||
132 | if (!ptr) | |
133 | goto end; | |
134 | ||
135 | switch (len) { | |
136 | case 32: | |
137 | { | |
138 | union floatIEEE754 u; | |
139 | uint32_t tmp; | |
140 | ||
141 | if (!rbo) { | |
142 | *(float *) ptr = (float) v; | |
143 | break; | |
144 | } | |
145 | u.v = v; | |
146 | /* | |
147 | * Need to reverse byte order. Write the opposite from our | |
148 | * architecture. | |
149 | */ | |
150 | if (__FLOAT_WORD_ORDER == LITTLE_ENDIAN) { | |
151 | /* Write big endian */ | |
152 | tmp = bitfield_unsigned_read(ptr, 31, 1, LITTLE_ENDIAN); | |
153 | bitfield_unsigned_write(&u.bits, 0, 1, BIG_ENDIAN, | |
154 | tmp); | |
155 | tmp = bitfield_unsigned_read(ptr, 23, 8, LITTLE_ENDIAN); | |
156 | bitfield_unsigned_write(&u.bits, 1, 8, BIG_ENDIAN, | |
157 | tmp); | |
158 | tmp = bitfield_unsigned_read(ptr, 0, 23, LITTLE_ENDIAN); | |
159 | bitfield_unsigned_write(&u.bits, 9, 23, BIG_ENDIAN, | |
160 | tmp); | |
161 | } else { | |
162 | /* Write little endian */ | |
163 | tmp = bitfield_unsigned_read(ptr, 0, 1, BIG_ENDIAN); | |
164 | bitfield_unsigned_write(&u.bits, 31, 1, LITTLE_ENDIAN, | |
165 | tmp); | |
166 | tmp = bitfield_unsigned_read(ptr, 1, 8, BIG_ENDIAN); | |
167 | bitfield_unsigned_write(&u.bits, 23, 8, LITTLE_ENDIAN, | |
168 | tmp); | |
169 | tmp = bitfield_unsigned_read(ptr, 9, 23, BIG_ENDIAN); | |
170 | bitfield_unsigned_write(&u.bits, 0, 23, LITTLE_ENDIAN, | |
171 | tmp); | |
172 | } | |
173 | break; | |
174 | } | |
175 | case 64: | |
176 | { | |
177 | union doubleIEEE754 u; | |
178 | uint64_t tmp; | |
179 | ||
180 | if (!rbo) { | |
181 | *(double *) ptr = v; | |
182 | break; | |
183 | } | |
184 | u.v = v; | |
185 | /* | |
186 | * Need to reverse byte order. Write the opposite from our | |
187 | * architecture. | |
188 | */ | |
189 | if (__FLOAT_WORD_ORDER == LITTLE_ENDIAN) { | |
190 | /* Write big endian */ | |
191 | tmp = bitfield_unsigned_read(ptr, 63, 1, LITTLE_ENDIAN); | |
192 | bitfield_unsigned_write(&u.bits, 0, 1, BIG_ENDIAN, | |
193 | tmp); | |
194 | tmp = bitfield_unsigned_read(ptr, 52, 11, LITTLE_ENDIAN); | |
195 | bitfield_unsigned_write(&u.bits, 1, 11, BIG_ENDIAN, | |
196 | tmp); | |
197 | tmp = bitfield_unsigned_read(ptr, 0, 52, LITTLE_ENDIAN); | |
198 | bitfield_unsigned_write(&u.bits, 12, 52, BIG_ENDIAN, | |
199 | tmp); | |
200 | } else { | |
201 | /* Write little endian */ | |
202 | tmp = bitfield_unsigned_read(ptr, 0, 1, BIG_ENDIAN); | |
203 | bitfield_unsigned_write(&u.bits, 63, 1, LITTLE_ENDIAN, | |
204 | tmp); | |
205 | tmp = bitfield_unsigned_read(ptr, 1, 11, BIG_ENDIAN); | |
206 | bitfield_unsigned_write(&u.bits, 52, 11, LITTLE_ENDIAN, | |
207 | tmp); | |
208 | tmp = bitfield_unsigned_read(ptr, 12, 52, BIG_ENDIAN); | |
209 | bitfield_unsigned_write(&u.bits, 0, 52, LITTLE_ENDIAN, | |
210 | tmp); | |
211 | } | |
212 | break; | |
213 | } | |
214 | default: | |
215 | printf("float write unavailable for size %u\n", len); | |
216 | assert(0); | |
217 | } | |
218 | end: | |
219 | return len; | |
220 | } |