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