1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2006, 2010, 2011, 2013 Free Software Foundation, Inc.
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18
19 #include "libpspp/integer-format.h"
20
21 #include <assert.h>
22
23 /* Returns true if FORMAT is a valid integer format. */
24 static inline bool
is_integer_format(enum integer_format format)25 is_integer_format (enum integer_format format)
26 {
27 return (format == INTEGER_MSB_FIRST
28 || format == INTEGER_LSB_FIRST
29 || format == INTEGER_VAX);
30 }
31
32 /* Converts the CNT bytes in INTEGER from SRC integer_format to DST
33 integer_format. */
34 void
integer_convert(enum integer_format src,const void * from,enum integer_format dst,void * to,size_t cnt)35 integer_convert (enum integer_format src, const void *from,
36 enum integer_format dst, void *to,
37 size_t cnt)
38 {
39 if (src != dst)
40 integer_put (integer_get (src, from, cnt), dst, to, cnt);
41 else if (from != to)
42 memcpy (to, from, cnt);
43 }
44
45 /* Returns the value of the CNT-byte integer at FROM, which is in
46 the given FORMAT. */
47 uint64_t
integer_get(enum integer_format format,const void * from_,size_t cnt)48 integer_get (enum integer_format format, const void *from_, size_t cnt)
49 {
50 const uint8_t *from = from_;
51 uint64_t value = 0;
52 size_t i;
53
54 assert (is_integer_format (format));
55 assert (cnt <= 8);
56
57 switch (format)
58 {
59 case INTEGER_MSB_FIRST:
60 for (i = 0; i < cnt; i++)
61 value = (value << 8) | from[i];
62 break;
63 case INTEGER_LSB_FIRST:
64 for (i = 0; i < cnt; i++)
65 value = (value << 8) | from[cnt - i - 1];
66 break;
67 case INTEGER_VAX:
68 for (i = 0; i < (cnt & ~1); i++)
69 value = (value << 8) | from[i ^ 1];
70 if (cnt & 1)
71 value = (value << 8) | from[cnt - 1];
72 break;
73 }
74
75 return value;
76 }
77
78 /* Stores VALUE as a CNT-byte integer at TO, in the given
79 FORMAT. */
80 void
integer_put(uint64_t value,enum integer_format format,void * to_,size_t cnt)81 integer_put (uint64_t value, enum integer_format format, void *to_, size_t cnt)
82 {
83 uint8_t *to = to_;
84 size_t i;
85
86 assert (is_integer_format (format));
87 assert (cnt <= 8);
88
89 value <<= 8 * (8 - cnt);
90
91 switch (format)
92 {
93 case INTEGER_MSB_FIRST:
94 for (i = 0; i < cnt; i++)
95 {
96 to[i] = value >> 56;
97 value <<= 8;
98 }
99 break;
100 case INTEGER_LSB_FIRST:
101 for (i = 0; i < cnt; i++)
102 {
103 to[cnt - i - 1] = value >> 56;
104 value <<= 8;
105 }
106 break;
107 case INTEGER_VAX:
108 for (i = 0; i < (cnt & ~1); i++)
109 {
110 to[i ^ 1] = value >> 56;
111 value <<= 8;
112 }
113 if (cnt & 1)
114 to[cnt - 1] = value >> 56;
115 break;
116 }
117 }
118
119 /* Returns true if bytes with index IDX1 and IDX2 in VALUE differ
120 in value. */
121 static inline bool
bytes_differ(uint64_t value,unsigned int idx1,unsigned int idx2)122 bytes_differ (uint64_t value, unsigned int idx1, unsigned int idx2)
123 {
124 uint8_t byte1 = value >> (idx1 * 8);
125 uint8_t byte2 = value >> (idx2 * 8);
126 return byte1 != byte2;
127 }
128
129 /* Attempts to identify the integer format in which the LENGTH
130 bytes in INTEGER represent the given EXPECTED_VALUE. Returns
131 true if successful, false otherwise. On success, stores the
132 format in *FORMAT. */
133 bool
integer_identify(uint64_t expected_value,const void * integer,size_t length,enum integer_format * format)134 integer_identify (uint64_t expected_value, const void *integer, size_t length,
135 enum integer_format *format)
136 {
137 /* Odd-length integers are confusing. */
138 assert (length % 2 == 0);
139
140 /* LENGTH must be greater than 2 because VAX format is
141 equivalent to little-endian for 2-byte integers. */
142 assert (length > 2);
143
144 /* EXPECTED_VALUE must contain different byte values, because
145 otherwise all formats are identical. */
146 assert (bytes_differ (expected_value, 0, 1)
147 || bytes_differ (expected_value, 0, 2)
148 || bytes_differ (expected_value, 0, 3)
149 || (length > 4
150 && (bytes_differ (expected_value, 0, 4)
151 || bytes_differ (expected_value, 0, 5)))
152 || (length > 6
153 && (bytes_differ (expected_value, 0, 6)
154 || bytes_differ (expected_value, 0, 7))));
155
156 if (integer_get (INTEGER_MSB_FIRST, integer, length) == expected_value)
157 *format = INTEGER_MSB_FIRST;
158 else if (integer_get (INTEGER_LSB_FIRST, integer, length) == expected_value)
159 *format = INTEGER_LSB_FIRST;
160 else if (integer_get (INTEGER_VAX, integer, length) == expected_value)
161 *format = INTEGER_VAX;
162 else
163 return false;
164
165 return true;
166 }
167