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 110928 : digits_upper() { return "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; }
62 :
63 : static
64 : const char*
65 8677 : digits_lower() { return "0123456789abcdefghijklmnopqrstuvwxyz"; }
66 :
67 : static
68 : const char*
69 0 : value_out_of_range() { return "value out of range."; }
70 :
71 : static
72 2 : const char* invalid_number_literal() { return "invalid number literal."; }
73 :
74 : static
75 0 : const char* unsupported_width() { return "unsupported width."; }
76 :
77 : static
78 : void
79 0 : fill_with_stars(unsigned width, char* result)
80 : {
81 0 : while (width) {
82 0 : *result++ = '*';
83 0 : width--;
84 : }
85 0 : *result = '\0';
86 0 : }
87 :
88 : static
89 : void
90 102253 : encode_pure(
91 : const char* digits,
92 : unsigned digits_size,
93 : unsigned width,
94 : int value,
95 : char* result)
96 : {
97 : char buf[16];
98 : int rest;
99 : unsigned i, j;
100 : i = 0;
101 : j = 0;
102 102253 : if (value < 0) {
103 : j = 1;
104 0 : value = -value;
105 : }
106 : while (1) {
107 492778 : rest = value / digits_size;
108 492778 : buf[i++] = digits[value - rest * digits_size];
109 492778 : if (rest == 0) break;
110 : value = rest;
111 : }
112 102253 : if (j) buf[i++] = '-';
113 120739 : for(j=i; j<width; j++) *result++ = ' ';
114 595031 : while (i != 0) *result++ = buf[--i];
115 102253 : *result = '\0';
116 102253 : }
117 :
118 : static
119 : const char*
120 893912 : decode_pure(
121 : const int* digits_values,
122 : unsigned digits_size,
123 : const char* s,
124 : unsigned s_size,
125 : int* result)
126 : {
127 : int si, dv;
128 : int have_minus = 0;
129 : int have_non_blank = 0;
130 : int value = 0;
131 : unsigned i = 0;
132 4916515 : for(; i<s_size; i++) {
133 4022604 : si = s[i];
134 4022604 : if (si < 0 || si > 127) {
135 0 : *result = 0;
136 0 : return invalid_number_literal();
137 : }
138 4022604 : if (si == ' ') {
139 1208661 : if (!have_non_blank) continue;
140 0 : value *= digits_size;
141 : }
142 2813943 : else if (si == '-') {
143 0 : if (have_non_blank) {
144 0 : *result = 0;
145 0 : return invalid_number_literal();
146 : }
147 : have_non_blank = 1;
148 : have_minus = 1;
149 0 : continue;
150 : }
151 : else {
152 : have_non_blank = 1;
153 2813943 : dv = digits_values[si];
154 2813943 : if (dv < 0 || dv >= digits_size) {
155 1 : *result = 0;
156 1 : return invalid_number_literal();
157 : }
158 2813942 : value *= digits_size;
159 2813942 : value += dv;
160 : }
161 : }
162 893911 : if (have_minus) value = -value;
163 893911 : *result = value;
164 893911 : return 0;
165 : }
166 :
167 : /*! hybrid-36 encoder: converts integer value to string result
168 :
169 : width: must be 4 (e.g. for residue sequence numbers)
170 : or 5 (e.g. for atom serial numbers)
171 :
172 : value: integer value to be converted
173 :
174 : result: pointer to char array of size width+1 or greater
175 : on return result is null-terminated
176 :
177 : return value: pointer to error message, if any,
178 : or 0 on success
179 :
180 : Example usage (from C++):
181 : char result[4+1];
182 : const char* errmsg = hy36encode(4, 12345, result);
183 : if (errmsg) throw std::runtime_error(errmsg);
184 : */
185 : const char*
186 102253 : hy36encode(unsigned width, int value, char* result)
187 : {
188 : int i = value;
189 102253 : if (width == 4U) {
190 1 : if (i >= -999) {
191 1 : if (i < 10000) {
192 0 : encode_pure(digits_upper(), 10U, 4U, i, result);
193 0 : return 0;
194 : }
195 1 : i -= 10000;
196 1 : if (i < 1213056 /* 26*36**3 */) {
197 1 : i += 466560 /* 10*36**3 */;
198 1 : encode_pure(digits_upper(), 36U, 0U, i, result);
199 1 : return 0;
200 : }
201 0 : i -= 1213056;
202 0 : if (i < 1213056) {
203 0 : i += 466560;
204 0 : encode_pure(digits_lower(), 36U, 0U, i, result);
205 0 : return 0;
206 : }
207 : }
208 : }
209 102252 : else if (width == 5U) {
210 102252 : if (i >= -9999) {
211 102252 : if (i < 100000) {
212 102143 : encode_pure(digits_upper(), 10U, 5U, i, result);
213 102143 : return 0;
214 : }
215 109 : i -= 100000;
216 109 : if (i < 43670016 /* 26*36**4 */) {
217 108 : i += 16796160 /* 10*36**4 */;
218 108 : encode_pure(digits_upper(), 36U, 0U, i, result);
219 108 : return 0;
220 : }
221 1 : i -= 43670016;
222 1 : if (i < 43670016) {
223 1 : i += 16796160;
224 1 : encode_pure(digits_lower(), 36U, 0U, i, result);
225 1 : return 0;
226 : }
227 : }
228 : }
229 : else {
230 0 : fill_with_stars(width, result);
231 0 : return unsupported_width();
232 : }
233 0 : fill_with_stars(width, result);
234 0 : return value_out_of_range();
235 : }
236 :
237 : /*! hybrid-36 decoder: converts string s to integer result
238 :
239 : width: must be 4 (e.g. for residue sequence numbers)
240 : or 5 (e.g. for atom serial numbers)
241 :
242 : s: string to be converted
243 : does not have to be null-terminated
244 :
245 : s_size: size of s
246 : must be equal to width, or an error message is
247 : returned otherwise
248 :
249 : result: integer holding the conversion result
250 :
251 : return value: pointer to error message, if any,
252 : or 0 on success
253 :
254 : Example usage (from C++):
255 : int result;
256 : const char* errmsg = hy36decode(width, "A1T5", 4, &result);
257 : if (errmsg) throw std::runtime_error(errmsg);
258 : */
259 : const char*
260 893912 : hy36decode(unsigned width, const char* s, unsigned s_size, int* result)
261 : {
262 241 : static const std::vector<int> digits_values_upper_vector([]() {
263 241 : std::vector<int> ret(128U,-1);
264 8917 : for(unsigned i=0; i<36U; i++) {
265 8676 : int di = digits_upper()[i];
266 8676 : if (di < 0 || di > 127) {
267 0 : plumed_error()<<"internal error hy36decode: integer value out of range";
268 : }
269 8676 : ret[di] = i;
270 : }
271 241 : return ret;
272 893912 : }());
273 893912 : static const int* digits_values_upper=digits_values_upper_vector.data();
274 241 : static const std::vector<int> digits_values_lower_vector([]() {
275 241 : std::vector<int> ret(128U,-1);
276 8917 : for(unsigned i=0; i<36U; i++) {
277 8676 : int di = digits_lower()[i];
278 8676 : if (di < 0 || di > 127) {
279 0 : plumed_error()<<"internal error hy36decode: integer value out of range";
280 : }
281 8676 : ret[di] = i;
282 : }
283 241 : return ret;
284 893912 : }());
285 893912 : static const int* digits_values_lower=digits_values_lower_vector.data();
286 : int di;
287 : const char* errmsg;
288 893912 : if (s_size == width) {
289 893912 : di = s[0];
290 893912 : if (di >= 0 && di <= 127) {
291 893912 : if (digits_values_upper[di] >= 10) {
292 14 : errmsg = decode_pure(digits_values_upper, 36U, s, s_size, result);
293 14 : if (errmsg == 0) {
294 : /* result - 10*36**(width-1) + 10**width */
295 13 : if (width == 4U) (*result) -= 456560;
296 6 : else if (width == 5U) (*result) -= 16696160;
297 : else {
298 0 : *result = 0;
299 0 : return unsupported_width();
300 : }
301 13 : return 0;
302 : }
303 : }
304 893898 : else if (digits_values_lower[di] >= 10) {
305 0 : errmsg = decode_pure(digits_values_lower, 36U, s, s_size, result);
306 0 : if (errmsg == 0) {
307 : /* result + 16*36**(width-1) + 10**width */
308 0 : if (width == 4U) (*result) += 756496;
309 0 : else if (width == 5U) (*result) += 26973856;
310 : else {
311 0 : *result = 0;
312 0 : return unsupported_width();
313 : }
314 0 : return 0;
315 : }
316 : }
317 : else {
318 893898 : errmsg = decode_pure(digits_values_upper, 10U, s, s_size, result);
319 893898 : if (errmsg) return errmsg;
320 893898 : if (!(width == 4U || width == 5U)) {
321 0 : *result = 0;
322 0 : return unsupported_width();
323 : }
324 : return 0;
325 : }
326 : }
327 : }
328 1 : *result = 0;
329 1 : return invalid_number_literal();
330 : }
331 :
332 : }
333 :
334 : }
335 :
|