Line data Source code
1 : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 : Copyright (c) 2018-2023 The plumed team
3 : (see the PEOPLE file at the root of the distribution for a list of names)
4 :
5 : See http://www.plumed.org for more information.
6 :
7 : This file is part of plumed, version 2.
8 :
9 : plumed is free software: you can redistribute it and/or modify
10 : it under the terms of the GNU Lesser General Public License as published by
11 : the Free Software Foundation, either version 3 of the License, or
12 : (at your option) any later version.
13 :
14 : plumed is distributed in the hope that it will be useful,
15 : but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : GNU Lesser General Public License for more details.
18 :
19 : You should have received a copy of the GNU Lesser General Public License
20 : along with plumed. If not, see <http://www.gnu.org/licenses/>.
21 : +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
22 : #include "h36.h"
23 : #include <vector>
24 : #include "Exception.h"
25 :
26 : namespace PLMD {
27 :
28 : /// Tiny namespace for hybrid36 format.
29 : /// This namespace includes freely available tools for h36 format.
30 : namespace h36 {
31 :
32 :
33 : /*! C port of the hy36encode() and hy36decode() functions in the
34 : hybrid_36.py Python prototype/reference implementation.
35 : See the Python script for more information.
36 :
37 : This file has no external dependencies, NOT even standard C headers.
38 : Optionally, use hybrid_36_c.h, or simply copy the declarations
39 : into your code.
40 :
41 : This file is unrestricted Open Source (cctbx.sf.net).
42 : Please send corrections and enhancements to cctbx@cci.lbl.gov .
43 :
44 : See also: http://cci.lbl.gov/hybrid_36/
45 :
46 : Ralf W. Grosse-Kunstleve, Feb 2007.
47 : */
48 :
49 : /* The following #include may be commented out.
50 : It is here only to enforce consistency of the declarations
51 : and the definitions.
52 : */
53 : // #include <iotbx/pdb/hybrid_36_c.h>
54 :
55 : /* All static functions below are implementation details
56 : (and not accessible from other translation units).
57 : */
58 :
59 : static
60 : const char*
61 114744 : digits_upper() {
62 114744 : return "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
63 : }
64 :
65 : static
66 : const char*
67 11773 : digits_lower() {
68 11773 : return "0123456789abcdefghijklmnopqrstuvwxyz";
69 : }
70 :
71 : static
72 : const char*
73 0 : value_out_of_range() {
74 0 : return "value out of range.";
75 : }
76 :
77 : static
78 2 : const char* invalid_number_literal() {
79 2 : return "invalid number literal.";
80 : }
81 :
82 : static
83 0 : const char* unsupported_width() {
84 0 : return "unsupported width.";
85 : }
86 :
87 : static
88 : void
89 0 : fill_with_stars(unsigned width, char* result) {
90 0 : while (width) {
91 0 : *result++ = '*';
92 0 : width--;
93 : }
94 0 : *result = '\0';
95 0 : }
96 :
97 : static
98 : void
99 102973 : encode_pure(
100 : const char* digits,
101 : unsigned digits_size,
102 : unsigned width,
103 : int value,
104 : char* result) {
105 : char buf[16];
106 : int rest;
107 : unsigned i, j;
108 : i = 0;
109 : j = 0;
110 102973 : if (value < 0) {
111 : j = 1;
112 0 : value = -value;
113 : }
114 : while (1) {
115 493908 : rest = value / digits_size;
116 493908 : buf[i++] = digits[value - rest * digits_size];
117 493908 : if (rest == 0) {
118 : break;
119 : }
120 : value = rest;
121 : }
122 102973 : if (j) {
123 0 : buf[i++] = '-';
124 : }
125 123929 : for(j=i; j<width; j++) {
126 20956 : *result++ = ' ';
127 : }
128 596881 : while (i != 0) {
129 493908 : *result++ = buf[--i];
130 : }
131 102973 : *result = '\0';
132 102973 : }
133 :
134 : static
135 : const char*
136 1009816 : decode_pure(
137 : const int* digits_values,
138 : const unsigned digits_size,
139 : const char* s,
140 : const unsigned s_size,
141 : int* result) {
142 : int si, dv;
143 : int have_minus = 0;
144 : int have_non_blank = 0;
145 : int value = 0;
146 : unsigned i = 0;
147 5553987 : for(; i<s_size; i++) {
148 : // go through unsigned char: plain char is signed on some platforms and unsigned on others
149 4544172 : si = static_cast<unsigned char>(s[i]);
150 4544172 : if (si > 127) {
151 0 : *result = 0;
152 0 : return invalid_number_literal();
153 : }
154 4544172 : if (si == ' ') {
155 1432855 : if (!have_non_blank) {
156 1432855 : continue;
157 : }
158 0 : value *= digits_size;
159 3111317 : } else if (si == '-') {
160 1138 : if (have_non_blank) {
161 0 : *result = 0;
162 0 : return invalid_number_literal();
163 : }
164 : have_non_blank = 1;
165 : have_minus = 1;
166 1138 : continue;
167 : } else {
168 : have_non_blank = 1;
169 3110179 : dv = digits_values[si];
170 3110179 : if (dv < 0 || dv >= static_cast<int>(digits_size)) {
171 1 : *result = 0;
172 1 : return invalid_number_literal();
173 : }
174 3110178 : value *= digits_size;
175 3110178 : value += dv;
176 : }
177 : }
178 1009815 : if (have_minus) {
179 1138 : value = -value;
180 : }
181 1009815 : *result = value;
182 1009815 : return 0;
183 : }
184 :
185 : /*! hybrid-36 encoder: converts integer value to string result
186 :
187 : width: must be 4 (e.g. for residue sequence numbers)
188 : or 5 (e.g. for atom serial numbers)
189 :
190 : value: integer value to be converted
191 :
192 : result: pointer to char array of size width+1 or greater
193 : on return result is null-terminated
194 :
195 : return value: pointer to error message, if any,
196 : or 0 on success
197 :
198 : Example usage (from C++):
199 : char result[4+1];
200 : const char* errmsg = hy36encode(4, 12345, result);
201 : if (errmsg) throw std::runtime_error(errmsg);
202 : */
203 : const char*
204 102973 : hy36encode(unsigned width, int value, char* result) {
205 : int i = value;
206 102973 : if (width == 4U) {
207 1 : if (i >= -999) {
208 1 : if (i < 10000) {
209 0 : encode_pure(digits_upper(), 10U, 4U, i, result);
210 0 : return 0;
211 : }
212 1 : i -= 10000;
213 1 : if (i < 1213056 /* 26*36**3 */) {
214 1 : i += 466560 /* 10*36**3 */;
215 1 : encode_pure(digits_upper(), 36U, 0U, i, result);
216 1 : return 0;
217 : }
218 0 : i -= 1213056;
219 0 : if (i < 1213056) {
220 0 : i += 466560;
221 0 : encode_pure(digits_lower(), 36U, 0U, i, result);
222 0 : return 0;
223 : }
224 : }
225 102972 : } else if (width == 5U) {
226 102972 : if (i >= -9999) {
227 102972 : if (i < 100000) {
228 102859 : encode_pure(digits_upper(), 10U, 5U, i, result);
229 102859 : return 0;
230 : }
231 113 : i -= 100000;
232 113 : if (i < 43670016 /* 26*36**4 */) {
233 112 : i += 16796160 /* 10*36**4 */;
234 112 : encode_pure(digits_upper(), 36U, 0U, i, result);
235 112 : return 0;
236 : }
237 1 : i -= 43670016;
238 1 : if (i < 43670016) {
239 1 : i += 16796160;
240 1 : encode_pure(digits_lower(), 36U, 0U, i, result);
241 1 : return 0;
242 : }
243 : }
244 : } else {
245 0 : fill_with_stars(width, result);
246 0 : return unsupported_width();
247 : }
248 0 : fill_with_stars(width, result);
249 0 : return value_out_of_range();
250 : }
251 :
252 : /*! hybrid-36 decoder: converts string s to integer result
253 :
254 : width: must be 4 (e.g. for residue sequence numbers)
255 : or 5 (e.g. for atom serial numbers)
256 :
257 : s: string to be converted
258 : does not have to be null-terminated
259 :
260 : s_size: size of s
261 : must be equal to width, or an error message is
262 : returned otherwise
263 :
264 : result: integer holding the conversion result
265 :
266 : return value: pointer to error message, if any,
267 : or 0 on success
268 :
269 : Example usage (from C++):
270 : int result;
271 : const char* errmsg = hy36decode(width, "A1T5", 4, &result);
272 : if (errmsg) throw std::runtime_error(errmsg);
273 : */
274 : const char*
275 1009816 : hy36decode(unsigned width, const char* s, unsigned s_size, int* result) {
276 327 : static const std::vector<int> digits_values_upper_vector([]() {
277 327 : std::vector<int> ret(128U,-1);
278 12099 : for(unsigned i=0; i<36U; i++) {
279 11772 : int di = static_cast<unsigned char>(digits_upper()[i]);
280 11772 : if (di > 127) {
281 0 : plumed_error()<<"internal error hy36decode: integer value out of range";
282 : }
283 11772 : ret[di] = i;
284 : }
285 327 : return ret;
286 : }
287 1009816 : ());
288 1009816 : static const int* digits_values_upper=digits_values_upper_vector.data();
289 327 : static const std::vector<int> digits_values_lower_vector([]() {
290 327 : std::vector<int> ret(128U,-1);
291 12099 : for(unsigned i=0; i<36U; i++) {
292 11772 : int di = static_cast<unsigned char>(digits_lower()[i]);
293 11772 : if (di > 127) {
294 0 : plumed_error()<<"internal error hy36decode: integer value out of range";
295 : }
296 11772 : ret[di] = i;
297 : }
298 327 : return ret;
299 : }
300 1009816 : ());
301 1009816 : static const int* digits_values_lower=digits_values_lower_vector.data();
302 : int di;
303 : const char* errmsg;
304 1009816 : if (s_size == width) {
305 1009816 : di = static_cast<unsigned char>(s[0]);
306 1009816 : if (di <= 127) {
307 1009816 : if (digits_values_upper[di] >= 10) {
308 14 : errmsg = decode_pure(digits_values_upper, 36U, s, s_size, result);
309 14 : if (errmsg == 0) {
310 : /* result - 10*36**(width-1) + 10**width */
311 13 : if (width == 4U) {
312 7 : (*result) -= 456560;
313 6 : } else if (width == 5U) {
314 6 : (*result) -= 16696160;
315 : } else {
316 0 : *result = 0;
317 0 : return unsupported_width();
318 : }
319 13 : return 0;
320 : }
321 1009802 : } else if (digits_values_lower[di] >= 10) {
322 0 : errmsg = decode_pure(digits_values_lower, 36U, s, s_size, result);
323 0 : if (errmsg == 0) {
324 : /* result + 16*36**(width-1) + 10**width */
325 0 : if (width == 4U) {
326 0 : (*result) += 756496;
327 0 : } else if (width == 5U) {
328 0 : (*result) += 26973856;
329 : } else {
330 0 : *result = 0;
331 0 : return unsupported_width();
332 : }
333 0 : return 0;
334 : }
335 : } else {
336 1009802 : errmsg = decode_pure(digits_values_upper, 10U, s, s_size, result);
337 1009802 : if (errmsg) {
338 : return errmsg;
339 : }
340 1009802 : if (!(width == 4U || width == 5U)) {
341 0 : *result = 0;
342 0 : return unsupported_width();
343 : }
344 : return 0;
345 : }
346 : }
347 : }
348 1 : *result = 0;
349 1 : return invalid_number_literal();
350 : }
351 :
352 : }
353 :
354 : }
355 :
|