1 /*
2 A* -------------------------------------------------------------------
3 B* This file contains source code for the PyMOL computer program
4 C* Copyright (c) Schrodinger, LLC.
5 D* -------------------------------------------------------------------
6 E* It is unlawful to modify or remove this copyright notice.
7 F* -------------------------------------------------------------------
8 G* Please see the accompanying LICENSE file for further information.
9 H* -------------------------------------------------------------------
10 I* Additional authors of this source file include:
11 -*
12 -*
13 -*
14 Z* -------------------------------------------------------------------
15 */
16
17 #include <algorithm>
18 #include <iterator>
19
20 #include"os_predef.h"
21 #include"os_std.h"
22 #include"os_time.h"
23
24 #include"Util.h"
25 #include"MemoryDebug.h"
26 #include"Err.h"
27
28 struct _CUtil {
29 double StartSec;
30 };
31
32
UtilInit(PyMOLGlobals * G)33 int UtilInit(PyMOLGlobals *G)
34 {
35 G->Util = pymol::calloc<CUtil>(1);
36 G->Util->StartSec = UtilGetSecondsEpoch();
37 return 1;
38 }
39
UtilFree(PyMOLGlobals * G)40 void UtilFree(PyMOLGlobals *G)
41 {
42 FreeP(G->Util);
43 }
44
UtilShouldWePrintQuantity(int quantity)45 int UtilShouldWePrintQuantity(int quantity)
46 {
47 if(quantity<10)
48 return 1;
49 if((quantity>0)&&(quantity<0x07FFFFFF)) /* avoids overflow, just in case */ {
50 int factor = 10;
51 while((factor*10)<quantity)
52 factor *= 10;
53 return ((quantity/factor)*factor == quantity);
54 }
55 return 0;
56 }
UtilCountStringVLA(char * vla)57 int UtilCountStringVLA(char *vla)
58 {
59 int result=0;
60 int cc;
61 if (vla) {
62 cc=VLAGetSize(vla);
63 while(cc--) {
64 if(!*vla)
65 result++;
66 vla++;
67 }
68 }
69 return(result);
70 }
71
72 /**
73 * Get a timestamp in seconds since PyMOL was started
74 */
UtilGetSeconds(PyMOLGlobals * G)75 double UtilGetSeconds(PyMOLGlobals *G)
76 {
77 return UtilGetSecondsEpoch() - G->Util->StartSec;
78 }
79
80 /**
81 * Get a timestamp in seconds since 1970-01-01
82 */
UtilGetSecondsEpoch()83 double UtilGetSecondsEpoch()
84 {
85 #ifndef _WIN32
86 struct timeval tv;
87 gettimeofday(&tv,NULL);
88 return tv.tv_sec + (tv.tv_usec / 1e6);
89 #else
90 struct __timeb64 timebuffer;
91 _ftime64( &timebuffer );
92 return timebuffer.time + (timebuffer.millitm / 1e3);
93 #endif
94 }
95
UtilConcat(char * where,const char * what)96 char *UtilConcat(char *where,const char *what)
97 {
98 while(*what)
99 *(where++)=*(what++);
100 *where=0;
101 return(where);
102 }
103
UtilConcatVLA(char ** vla,ov_size * cc,const char * str)104 void UtilConcatVLA(char **vla,ov_size *cc,const char *str)
105 {
106 const char *what;
107 char *where;
108 ov_size len;
109
110 len=strlen(str);
111 VLACheck((*vla),char,len+*cc+1);
112 where = (*cc)+(*vla);
113 what = str;
114 while(*what)
115 *(where++)=*(what++);
116 *where=0;
117 *(cc)+=len;
118 }
119
UtilNPadVLA(char ** vla,ov_size * cc,const char * str,ov_size len)120 void UtilNPadVLA(char **vla,ov_size *cc,const char *str,ov_size len)
121 {
122 const char *what;
123 char *where;
124 ov_size n = 0;
125 VLACheck((*vla),char,len + *cc +1);
126 where = (*cc)+(*vla);
127 what = str;
128 while(*what) {
129 if(n>=len)
130 break;
131 *(where++)=*(what++);
132 n++;
133 }
134 while(n<len) {
135 *(where++) = ' ';
136 n++;
137 }
138 *where=0;
139 *(cc)+=len;
140 }
141
UtilFillVLA(char ** vla,ov_size * cc,char what,ov_size len)142 void UtilFillVLA(char **vla,ov_size *cc,char what,ov_size len)
143 {
144 char *where;
145 VLACheck((*vla),char,len+(*cc)+1);
146 where = (*cc)+(*vla);
147 *(cc)+=len;
148 while((len--)>0)
149 *(where++)=what;
150 *where=0;
151 }
152
153
UtilNConcat(char * dst,const char * src,ov_size n)154 void UtilNConcat(char *dst,const char *src,ov_size n) { /* copies up to N-1 chars */
155 ov_size l;
156 l=strlen(dst);
157 if(n>l) {
158 UtilNCopy(dst+l,src,n-l);
159 }
160 }
161
UtilNCopy(char * dst,const char * src,ov_size n)162 void UtilNCopy(char *dst,const char *src,ov_size n)
163 { /* copies up to N-1 chars */
164 if(n--) {
165 while(n--) {
166 if(!*src)
167 break;
168 else
169 *(dst++)=*(src++);
170 }
171 }
172 *dst=0;
173 }
174
UtilNCopyToLower(char * dst,const char * src,ov_size n)175 void UtilNCopyToLower(char *dst,const char *src,ov_size n)
176 {
177 if(n--) {
178 while(n--) {
179 if(!*src)
180 break;
181 else
182 *(dst++)=tolower(*(src++));
183 }
184 }
185 *dst=0;
186 }
187
UtilCleanStr(char * s)188 void UtilCleanStr(char *s) /*remove flanking white and all unprintables*/
189 {
190 char *p,*q;
191 p=s;
192 q=s;
193 while(*p)
194 if(*p>32)
195 break;
196 else
197 p++;
198 while(*p)
199 if(*p>=32)
200 (*q++)=(*p++);
201 else
202 p++;
203 *q=0;
204 while(q>=s)
205 {
206 if(*q>32)
207 break;
208 else
209 {
210 (*q)=0;
211 q--;
212 }
213 }
214 }
215
216 /**
217 * Removes unprintables and flanking whitespace
218 * @param s string to be cleaned
219 * @return whitespace-trimmed string with readable characters
220 */
UtilCleanStdStr(const std::string & s)221 std::string UtilCleanStdStr(const std::string& s)
222 {
223 std::string ret;
224
225 auto is_not_whitespace = [](char c) { return c > ' '; };
226 auto is_printable = [](char c) { return c >= ' '; };
227
228 auto leading = std::find_if(s.begin(), s.end(), is_not_whitespace);
229 auto trailing = std::find_if(s.rbegin(), s.rend(), is_not_whitespace);
230
231 std::copy_if(leading, trailing.base(), std::back_inserter(ret), is_printable);
232 return ret;
233 }
234
235 /*
236 * Remove ANSI Escape sequences in-place
237 */
UtilStripANSIEscapes(char * s)238 void UtilStripANSIEscapes(char *s)
239 {
240 for (const char *p = s;; ++p, ++s) {
241 while (p[0] == '\033' && p[1] == '[') {
242 while (' ' <= p[2] && p[2] < '@') ++p;
243 p += 3;
244 }
245 if (p != s)
246 *s = *p;
247 if (!p[0])
248 break;
249 }
250 }
UtilStripANSIEscapes(std::string & str)251 void UtilStripANSIEscapes(std::string& str)
252 {
253 UtilStripANSIEscapes(&str[0]);
254 str.resize(strlen(str.c_str()));
255 }
256
UtilZeroMem(void * ptr,ov_size howMuch)257 void UtilZeroMem(void *ptr,ov_size howMuch)
258 {
259 char *p,*q;
260 p=(char*)ptr;
261 q=p+howMuch;
262 MemoryZero(p,q);
263 }
264
UtilCopyMem(void * dst,const void * src,ov_size howMuch)265 void UtilCopyMem(void *dst,const void *src,ov_size howMuch) /* optimize! */
266 {
267 /* need to determine the memory is non-overlapping. If so, then use memcpy. */
268 char *c,*d;
269 c=(char*)dst;
270 d=(char*)src;
271 while(howMuch--)
272 *(c++)=*(d++);
273 }
274
UtilExpandArrayElements(void * src,void * dst,int n_entries,int old_rec_size,int new_rec_size)275 void UtilExpandArrayElements(void *src,void *dst,int n_entries,int old_rec_size,int new_rec_size)
276 {
277 /* simple but ineffient byte-based copy */
278 char *p,*q,*p_stop,*q_stop;
279 int a;
280 for(a=0;a<n_entries;a++) {
281 p=((char*)src)+(old_rec_size*a);
282 p_stop=p+old_rec_size;
283 q=((char*)dst)+(new_rec_size*a);
284 q_stop=q+new_rec_size;
285 while(p!=p_stop) {
286 *(q++)=*(p++);
287 }
288 while(q!=q_stop) {
289 *(q++)=0;
290 }
291 }
292 }
293
UtilArrayCalloc(unsigned int * dim,ov_size ndim,ov_size atom_size)294 void *UtilArrayCalloc(unsigned int *dim,ov_size ndim,ov_size atom_size)
295 {
296 ov_size size;
297 ov_size sum,product;
298 ov_size chunk;
299 ov_size a,b,c;
300 void *result;
301 char **p;
302 char *q;
303
304 sum = 0;
305 for(a=0;a<(ndim-1);a++) {
306 product = dim[0];
307 for(b=1;b<=a;b++)
308 product = product * dim[b];
309 sum = sum + product * sizeof(void*);
310 }
311 size = atom_size;
312 for(a=0;a<ndim;a++)
313 size = size * dim[a];
314 size = size + sum;
315 result = pymol::calloc<char>(size);
316
317 if(result) {
318 chunk = 1;
319 p = (char**) result;
320 for(c=0;c<(ndim-1);c++) {
321 if(c<(ndim-2)) {
322 chunk = dim[c+1] * sizeof(void*);
323 } else {
324 chunk = dim[c+1] * atom_size;
325 }
326
327 product = dim[0];
328 for(b=1;b<=c;b++)
329 product = product * dim[b];
330 q = ((char*)p) + product * sizeof(void*);
331 for(a=0;a<product;a++) {
332 *p = q;
333 p++;
334 q+=chunk;
335 }
336 }
337 }
338 return(result);
339 }
340
UtilApplySortedIndices(int n,int * x,int rec_size,void * src,void * dst)341 void UtilApplySortedIndices(int n,int *x, int rec_size, void *src, void *dst)
342 {
343 int a;
344 for(a=0;a<n;a++) {
345 memcpy(((char*)dst)+(a*rec_size),
346 ((char*)src)+(x[a]*rec_size),
347 rec_size);
348 }
349 }
350
351
UtilSortIndex(int n,void * array,int * x,UtilOrderFn * fOrdered)352 void UtilSortIndex(int n,void *array,int *x,UtilOrderFn* fOrdered)
353 {
354 int l,a,r,t,i;
355
356 if(n<1) return;
357 else if(n==1) { x[0]=0; return; }
358 x--;
359 for(a=1;a<=n;a++) x[a]=a;
360 l=(n>>1)+1;
361 r=n;
362 while(1) {
363 if(l>1)
364 t = x[--l];
365 else {
366 t = x[r];
367 x[r] = x[1];
368 if( --r == 1) {
369 x[1] = t;
370 break;
371 }
372 }
373 i=l;
374 a=l << 1;
375 while (a <= r) {
376 if (a < r && (!fOrdered(array,x[a+1]-1,x[a]-1))) a++;
377 if (!fOrdered(array,x[a]-1,t-1)) {
378 x[i] = x[a];
379 a += (i=a);
380 } else
381 a = r + 1;
382 }
383 x[i] = t;
384 }
385 x++;
386 for(a=0;a<n;a++) x[a]--;
387 }
388
UtilSortIndexGlobals(PyMOLGlobals * G,int n,const void * array,int * x,UtilOrderFnGlobals * fOrdered)389 void UtilSortIndexGlobals(PyMOLGlobals *G,int n,const void *array,int *x,UtilOrderFnGlobals* fOrdered)
390 {
391 int l,a,r,t,i;
392
393 if(n<1) return;
394 else if(n==1) { x[0]=0; return; }
395 x--;
396 for(a=1;a<=n;a++) x[a]=a;
397 l=(n>>1)+1;
398 r=n;
399 while(1) {
400 if(l>1)
401 t = x[--l];
402 else {
403 t = x[r];
404 x[r] = x[1];
405 if( --r == 1) {
406 x[1] = t;
407 break;
408 }
409 }
410 i=l;
411 a=l << 1;
412 while (a <= r) {
413 if (a < r && (!fOrdered(G,array,x[a+1]-1,x[a]-1))) a++;
414 if (!fOrdered(G,array,x[a]-1,t-1)) {
415 x[i] = x[a];
416 a += (i=a);
417 } else
418 a = r + 1;
419 }
420 x[i] = t;
421 }
422 x++;
423 for(a=0;a<n;a++) x[a]--;
424 }
425
426 #define MAX_BIN = 100
427
428 #ifndef R_SMALL8
429 #define R_SMALL8 0.00000001F
430 #endif
431
UtilSemiSortFloatIndex(int n,float * array,int * x,int forward)432 int UtilSemiSortFloatIndex(int n,float *array,int *x, int forward)
433 {
434 return UtilSemiSortFloatIndexWithNBins(n, n, array, x, forward);
435 }
436
UtilSemiSortFloatIndexWithNBins(int n,int nbins,float * array,int * destx,int forward)437 int UtilSemiSortFloatIndexWithNBins(int n, int nbins, float *array, int *destx, int forward)
438 {
439 int *start1 = pymol::calloc<int>(n + nbins);
440 int ret = UtilSemiSortFloatIndexWithNBinsImpl(start1, n, nbins, array, destx, forward);
441 mfree(start1);
442 return ret;
443 }
444
UtilSemiSortFloatIndexWithNBinsImpl(int * start1,int n,int nbins,float * array,int * destx,int forward)445 int UtilSemiSortFloatIndexWithNBinsImpl(int *start1, int n, int nbins, float *array, int *destx, int forward)
446 {
447 /* approximate sort, for quick handling of transparency values */
448 /* this sort uses 2 arrays start1 and next1 to keep track of */
449 /* the indexes. The values in start1 are set to the index */
450 /* relative to the array value within the min/max values. If */
451 /* there is a collision, the value in next1 is set to the value */
452 /* that is collided, and start1[idx] is set to the index plus 1 (a+1) */
453 /* This makes it easy to go through the 2 arrays and write into the */
454 /* x array the approximate order of the floating point values in array */
455 /* by indexes. */
456 /* Since there are two arrays, this guarentees that there */
457 /* will be enough memory to hold all indexes. If there are many collisions, */
458 /* the next1 array will hold a link to most of the indexes, which are traversed */
459 /* when the first index is found in start1. If there are few collisions, then */
460 /* the majority of the start1 array is used. The total number of items used in */
461 /* both arrays will always be the number of values, i.e., n. */
462 /* 9/9/14: BB - added start1 and nbins argument
463 start1 - pre-allocated memory
464 nbins - allows the first array to be controled as the number of bins,
465 to match how CGORenderGLAlpha() sorts its triangles.
466 */
467 int ok = true;
468 if(n>0) {
469 float min,max,*f,v;
470 float range, scale;
471 int a;
472 int *next1;
473 int idx1;
474
475 CHECKOK(ok, start1);
476 if (!ok){
477 return false;
478 }
479
480 next1 = start1 + nbins;
481 max = (min = array[0]);
482 f = array + 1;
483 for(a=1;a<n;a++) {
484 v = *(f++);
485 if(max<v) max=v;
486 if(min>v) min=v;
487 }
488 range = (max-min)/.9999F; /* for boundary conditions */
489 if(range<R_SMALL8) {
490 for(a=0;a<n;a++)
491 destx[a] = a;
492 } else {
493 scale = nbins/range;
494 f = array;
495 /* hash by value (actually binning) */
496 if(forward) {
497 for(a=0;a<n;a++) {
498 idx1 = (int)((*(f++)-min)*scale);
499 next1[a] = start1[idx1];
500 start1[idx1] = a+1;
501 }
502 } else {
503 for(a=0;a<n;a++) {
504 idx1 = (nbins-1) - (int)((*(f++)-min)*scale);
505 next1[a] = start1[idx1];
506 start1[idx1] = a+1;
507 }
508 }
509 /* now read out */
510 {
511 int c=0;
512 int cur1;
513 a=0;
514 while(a<nbins) {
515 if( (cur1 = start1[a]) ) {
516 idx1 = cur1 - 1;
517 while(1) {
518 destx[c++] = idx1;
519 if(! (cur1 = next1[idx1]))
520 break;
521 idx1 = cur1 - 1;
522 }
523 }
524 a++;
525 }
526 }
527 }
528 }
529 return true;
530 }
531
UtilSortInPlace(PyMOLGlobals * G,void * array,int nItem,unsigned int itemSize,UtilOrderFn * fOrdered)532 void UtilSortInPlace(PyMOLGlobals *G,void *array,int nItem,
533 unsigned int itemSize,
534 UtilOrderFn *fOrdered)
535
536 {
537 char *tmp;
538 int *index;
539 int ia;
540 int a;
541 if(nItem>0)
542 {
543 tmp = pymol::malloc<char>((itemSize*nItem));
544 index = pymol::malloc<int>(nItem+1);
545 ErrChkPtr(G,tmp);
546 ErrChkPtr(G,index);
547 UtilSortIndex(nItem,array,index,fOrdered);
548 for(a=0;a<nItem;a++) index[a]++; /* ^tricky index adjustment to avoid flag array */
549 for(a=0;a<nItem;a++)
550 {
551 ia = abs(index[a])-1; /* ^ */
552 if(ia!=a)
553 {
554 if(index[a]>0) /* this record not yet copied, so save copy */
555 {
556 memcpy(((char*)tmp )+(a*itemSize),
557 ((char*)array)+(a*itemSize),
558 itemSize);
559 index[a] = -index[a]; /* set nega-flag */
560 }
561 if(index[ia]<0) /* nega-flag, so record is stored in tmp */
562 memcpy(((char*)array)+(a*itemSize),
563 ((char*)tmp )+(ia*itemSize),
564 itemSize);
565 else
566 {
567 memcpy(((char*)array)+(a*itemSize),
568 ((char*)array)+(ia*itemSize),
569 itemSize);
570 index[ia] = -index[ia];
571 /* nega-flag: record doesn't need to be backed up */
572 }
573 }
574 }
575 mfree(tmp);
576 mfree(index);
577 }
578 }
579