1 // Copyright (c) Lawrence Livermore National Security, LLC and other Conduit
2 // Project developers. See top-level LICENSE AND COPYRIGHT files for dates and
3 // other details. No copyright assignment is required to contribute to Conduit.
4
5 //-----------------------------------------------------------------------------
6 ///
7 /// file: conduit_utils.cpp
8 ///
9 //-----------------------------------------------------------------------------
10 #include "conduit_utils.hpp"
11 #include "conduit_error.hpp"
12
13 //-----------------------------------------------------------------------------
14 // -- standard lib includes --
15 //-----------------------------------------------------------------------------
16
17 // for sleep funcs
18 #if defined(CONDUIT_PLATFORM_WINDOWS)
19 #define NOMINMAX
20 #include <windows.h>
21 #include <direct.h>
22 #if (_MSC_VER && _MSC_VER < 1900)
23 #define snprintf _snprintf
24 #endif
25 #undef min
26 #undef max
27 #else
28 #include <dirent.h>
29 #include <time.h>
30 #endif
31
32 // file system funcs
33 #include <sys/stat.h>
34 #include <sys/types.h>
35
36 #include <string.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39
40 #include <algorithm>
41 #include <limits>
42 #include <fstream>
43 #include <map>
44
45
46 // define proper path sep
47 #if defined(CONDUIT_PLATFORM_WINDOWS)
48 #define CONDUIT_UTILS_FILE_PATH_SEPARATOR "\\"
49 #else
50 #define CONDUIT_UTILS_FILE_PATH_SEPARATOR "/"
51 #endif
52
53 static const std::string file_path_sep_string(CONDUIT_UTILS_FILE_PATH_SEPARATOR);
54
55 #include "conduit.hpp"
56 #include "conduit_fmt/conduit_fmt.h"
57
58
59 //-----------------------------------------------------------------------------
60 // -- libb64 includes --
61 //-----------------------------------------------------------------------------
62 #define BUFFERSIZE 65536
63 #include "b64/encode.h"
64 #include "b64/decode.h"
65 using namespace base64;
66
67
68 //-----------------------------------------------------------------------------
69 // -- begin conduit:: --
70 //-----------------------------------------------------------------------------
71 namespace conduit
72 {
73
74 //-----------------------------------------------------------------------------
75 // -- begin conduit::utils --
76 //-----------------------------------------------------------------------------
77 namespace utils
78 {
79
80 //-----------------------------------------------------------------------------
81 void *
default_alloc_handler(size_t items,size_t item_size)82 default_alloc_handler(size_t items, size_t item_size)
83 {
84 return calloc(items, item_size);
85 }
86
87 //-----------------------------------------------------------------------------
88 void
default_free_handler(void * data_ptr)89 default_free_handler(void *data_ptr)
90 {
91 free(data_ptr);
92 }
93
94 //-----------------------------------------------------------------------------
95 void
default_memset_handler(void * ptr,int value,size_t num)96 default_memset_handler(void * ptr, int value, size_t num )
97 {
98 memset(ptr,value,num);
99 }
100
101 //-----------------------------------------------------------------------------
102 void
default_memcpy_handler(void * destination,const void * source,size_t num)103 default_memcpy_handler(void * destination, const void * source, size_t num)
104 {
105 memcpy(destination,source,num);
106 }
107
108
109 //-----------------------------------------------------------------------------
110 // Private namespace member that holds our memcpy callback.
111 void (*conduit_handle_memcpy)(void * destination,
112 const void * source,
113 size_t num) = default_memcpy_handler;
114
115 //-----------------------------------------------------------------------------
116 // Private namespace member that holds our memset callback.
117 void (*conduit_handle_memset)(void * ptr,
118 int value,
119 size_t num ) = default_memset_handler;
120
121 //-----------------------------------------------------------------------------
122 void
set_memcpy_handler(void (* conduit_hnd_copy)(void *,const void *,size_t))123 set_memcpy_handler(void(*conduit_hnd_copy)(void*,
124 const void *,
125 size_t))
126 {
127 conduit_handle_memcpy = conduit_hnd_copy;
128 }
129
130 //-----------------------------------------------------------------------------
131 void
set_memset_handler(void (* conduit_hnd_memset)(void *,int,size_t))132 set_memset_handler(void(*conduit_hnd_memset)(void*,
133 int,
134 size_t))
135 {
136 conduit_handle_memset = conduit_hnd_memset;
137 }
138
139 namespace detail
140 {
141 //
142 // AllocManager: A singleton that holds our alloc and free function maps
143 //
144 //
145 // NOTE: THE SINGLETON INSTNACE IS INTENTIONALLY LEAKED!
146 //
147 // These maps are used by Node instances to alloc and free
148 // memory, they need to exist as long as any Node object exists!
149 //
150 // We are doing static init correctly here, so we
151 // avoid the C++ static obj init fiasco:
152 // https://isocpp.org/wiki/faq/ctors#static-init-order
153 //
154 // However, we still can't apply fine grained control
155 // to when these objects are cleaned up. This means
156 // if you use a statically initialized Node object
157 // you could get a crash on exit
158 // (unless we leak the singleton)
159 //
160 // One strategy to avoid this is to move everything
161 // into the same compilation unit. We tried this
162 // but we still hit an on exit cleanup where
163 // the alloc maps were destructed, but a static Node
164 // object still needed to cleanup.
165 //
166 // If leaking this singleton offends your sensibilities,
167 // I am sorry. Ideally, these would be cleaned up on
168 // exit absolutely last - meaning you could never use
169 // those few precious bytes for anything else.
170 //
171 class AllocManager {
172
173 public:
instance()174 static AllocManager& instance()
175 {
176 //
177 // NOTE: THIS IS INTENTIONALLY LEAKED
178 // See note above.
179 //
180 static AllocManager *inst = new AllocManager();
181 return *inst;
182 }
183
184 // reg interface
register_allocator(void * (* conduit_hnd_allocate)(size_t,size_t),void (* conduit_hnd_free)(void *))185 index_t register_allocator(void*(*conduit_hnd_allocate) (size_t, size_t),
186 void(*conduit_hnd_free)(void *))
187 {
188 m_allocator_map[m_allocator_id] = conduit_hnd_allocate;
189 m_free_map[m_allocator_id] = conduit_hnd_free;
190 return m_allocator_id++;
191 }
192
193 // alloc interface
allocate(size_t n_items,size_t item_size,index_t allocator_id)194 void *allocate(size_t n_items,
195 size_t item_size,
196 index_t allocator_id)
197 {
198 return m_allocator_map[allocator_id](n_items, item_size);
199 }
200
201 // free interface
free(void * ptr,index_t allocator_id)202 void free(void *ptr,
203 index_t allocator_id)
204 {
205 m_free_map[allocator_id](ptr);
206 }
207
208 private:
209 // constructor
AllocManager()210 AllocManager()
211 : m_allocator_map(),
212 m_free_map()
213 {
214 // register default handlers
215 m_allocator_map[0] = &default_alloc_handler;
216 m_free_map[0] = &default_free_handler;
217
218 m_allocator_id = 1;
219
220 }
221
222 // destructor
~AllocManager()223 ~AllocManager()
224 {
225
226 }
227
228 // vars
229 index_t m_allocator_id;
230 std::map<index_t,void*(*)(size_t, size_t)> m_allocator_map;
231 std::map<index_t,void(*)(void*)> m_free_map;
232
233 };
234 }
235
236 //-----------------------------------------------------------------------------
237 index_t
register_allocator(void * (* conduit_hnd_allocate)(size_t,size_t),void (* conduit_hnd_free)(void *))238 register_allocator(void*(*conduit_hnd_allocate) (size_t, size_t),
239 void(*conduit_hnd_free)(void *))
240 {
241 return detail::AllocManager::instance().register_allocator(conduit_hnd_allocate,
242 conduit_hnd_free);
243 }
244
245
246 //-----------------------------------------------------------------------------
247 void *
conduit_allocate(size_t n_items,size_t item_size,index_t allocator_id)248 conduit_allocate(size_t n_items,
249 size_t item_size,
250 index_t allocator_id)
251 {
252 return detail::AllocManager::instance().allocate(n_items,
253 item_size,
254 allocator_id);
255 }
256
257 //-----------------------------------------------------------------------------
258 void
conduit_free(void * ptr,index_t allocator_id)259 conduit_free(void *ptr,
260 index_t allocator_id)
261 {
262 detail::AllocManager::instance().free(ptr,allocator_id);
263 }
264
265 //-----------------------------------------------------------------------------
266 void
conduit_memcpy(void * destination,const void * source,size_t num)267 conduit_memcpy(void *destination,
268 const void *source,
269 size_t num)
270 {
271 conduit_handle_memcpy(destination,source,num);
272 }
273
274
275 //-----------------------------------------------------------------------------
conduit_memset(void * ptr,int value,size_t num)276 void conduit_memset(void *ptr,
277 int value,
278 size_t num)
279 {
280 conduit_handle_memset(ptr,value,num);
281 }
282 //-----------------------------------------------------------------------------
283 void
conduit_memcpy_strided_elements(void * dest,size_t num_elements,size_t ele_bytes,size_t dest_stride,const void * src,size_t src_stride)284 conduit_memcpy_strided_elements(void *dest,
285 size_t num_elements,
286 size_t ele_bytes,
287 size_t dest_stride,
288 const void *src,
289 size_t src_stride)
290 {
291 // source and dest are compact
292 if( dest_stride == ele_bytes && src_stride == ele_bytes)
293 {
294 utils::conduit_memcpy(dest,
295 src,
296 ele_bytes * num_elements);
297 }
298 else // the source or dest are strided in a non compact way
299 {
300 char *src_data_ptr = (char*) src;
301 char *dest_data_ptr = (char*) dest;
302 for(size_t i=0; i< num_elements; i++)
303 {
304 // copy next strided element
305 utils::conduit_memcpy(dest_data_ptr,
306 src_data_ptr,
307 ele_bytes);
308 // move by src stride
309 src_data_ptr += src_stride;
310 // move by dest stride
311 dest_data_ptr += dest_stride;
312 }
313 }
314 }
315
316 //-----------------------------------------------------------------------------
317 // default info message handler callback, simply prints to std::cout.
318 void
default_info_handler(const std::string & msg,const std::string & file,int line)319 default_info_handler(const std::string &msg,
320 const std::string &file,
321 int line)
322 {
323 std::cout << "[" << file
324 << " : " << line << "]"
325 << "\n " << msg << std::endl;
326 }
327
328 //-----------------------------------------------------------------------------
329 // Private namespace member that holds our info message handler callback.
330 void (*conduit_on_info)(const std::string &,
331 const std::string &,
332 int)= default_info_handler;
333
334 //-----------------------------------------------------------------------------
335 // Allows other libraries to provide an alternate error handler.
336 void
set_info_handler(void (* on_info)(const std::string &,const std::string &,int))337 set_info_handler(void(*on_info)
338 (const std::string&,
339 const std::string&,
340 int))
341 {
342 conduit_on_info = on_info;
343 }
344
345 //-----------------------------------------------------------------------------
346 conduit_info_handler
info_handler()347 info_handler()
348 {
349 return conduit_on_info;
350 }
351
352 //-----------------------------------------------------------------------------
353 void
handle_info(const std::string & msg,const std::string & file,int line)354 handle_info(const std::string &msg,
355 const std::string &file,
356 int line)
357 {
358 conduit_on_info(msg,file,line);
359 }
360
361 //-----------------------------------------------------------------------------
362 // default warning handler callback, simply throws a conduit::Error exception.
363 void
default_warning_handler(const std::string & msg,const std::string & file,int line)364 default_warning_handler(const std::string &msg,
365 const std::string &file,
366 int line)
367 {
368 throw conduit::Error( msg, file, line);
369 }
370
371 //-----------------------------------------------------------------------------
372 // Private namespace member that holds our info message handler callback.
373 void (*conduit_on_warning)(const std::string &,
374 const std::string &,
375 int)= default_warning_handler;
376
377 //-----------------------------------------------------------------------------
378 // Allows other libraries to provide an alternate warning handler.
379 void
set_warning_handler(void (* on_warning)(const std::string &,const std::string &,int))380 set_warning_handler(void(*on_warning)
381 (const std::string&,
382 const std::string&,
383 int))
384 {
385 conduit_on_warning = on_warning;
386 }
387
388 //-----------------------------------------------------------------------------
389 conduit_warning_handler
warning_handler()390 warning_handler()
391 {
392 return conduit_on_warning;
393 }
394
395 //-----------------------------------------------------------------------------
396 void
handle_warning(const std::string & msg,const std::string & file,int line)397 handle_warning(const std::string &msg,
398 const std::string &file,
399 int line)
400 {
401 conduit_on_warning(msg,file,line);
402 }
403
404
405 //-----------------------------------------------------------------------------
406 // default error handler callback, simply throws a conduit::Error exception.
407 void
default_error_handler(const std::string & msg,const std::string & file,int line)408 default_error_handler(const std::string &msg,
409 const std::string &file,
410 int line)
411 {
412 throw conduit::Error( msg, file, line);
413 }
414
415 //-----------------------------------------------------------------------------
416 // Private namespace member that holds our error handler callback.
417 void (*conduit_on_error)(const std::string &,
418 const std::string &,
419 int)= default_error_handler;
420
421 //-----------------------------------------------------------------------------
422 // Allows other libraries to provide an alternate error handler.
423 void
set_error_handler(void (* on_error)(const std::string &,const std::string &,int))424 set_error_handler(void(*on_error)
425 (const std::string&,
426 const std::string&,
427 int))
428 {
429 conduit_on_error = on_error;
430 }
431
432 //-----------------------------------------------------------------------------
433 conduit_error_handler
error_handler()434 error_handler()
435 {
436 return conduit_on_error;
437 }
438
439 //-----------------------------------------------------------------------------
440 void
handle_error(const std::string & msg,const std::string & file,int line)441 handle_error(const std::string &msg,
442 const std::string &file,
443 int line)
444 {
445 conduit_on_error(msg,file,line);
446 }
447
448
449 //-----------------------------------------------------------------------------
450 void
split_string(const std::string & str,const std::string & sep,std::string & curr,std::string & next)451 split_string(const std::string &str,
452 const std::string &sep,
453 std::string &curr,
454 std::string &next)
455 {
456 curr.clear();
457 next.clear();
458
459 std::size_t found = str.find(sep);
460 if (found != std::string::npos)
461 {
462 curr = str.substr(0,found);
463 if(found != str.size()-1)
464 next = str.substr(found+1,str.size()-(found-1));
465 }
466 else
467 {
468 curr = str;
469 }
470 }
471
472 //-----------------------------------------------------------------------------
473 void
split_string(const std::string & str,char sep,std::vector<std::string> & sv)474 split_string(const std::string &str, char sep, std::vector<std::string> &sv)
475 {
476 if(!str.empty())
477 {
478 const char *start = str.c_str();
479 const char *c = str.c_str();
480 while(*c != '\0')
481 {
482 if(*c == sep)
483 {
484 size_t len = c - start;
485 if(len > 0)
486 sv.push_back(std::string(start, len));
487 c++;
488 start = c;
489 }
490 else
491 c++;
492 }
493 if(*start != '\0')
494 {
495 size_t len = c - start;
496 if(len > 0)
497 sv.push_back(std::string(start, len));
498 }
499 }
500 }
501
502 //-----------------------------------------------------------------------------
503 void
rsplit_string(const std::string & str,const std::string & sep,std::string & curr,std::string & next)504 rsplit_string(const std::string &str,
505 const std::string &sep,
506 std::string &curr,
507 std::string &next)
508 {
509 curr.clear();
510 next.clear();
511
512 std::size_t found = str.rfind(sep);
513 if (found != std::string::npos)
514 {
515 next = str.substr(0,found);
516 if(found != str.size()-1)
517 curr = str.substr(found+1,str.size()-(found-1));
518 }
519 else
520 {
521 curr = str;
522 }
523 }
524
525 //-----------------------------------------------------------------------------
526 void
trim_string(std::string & str,const char * chars_to_trim)527 trim_string(std::string &str,
528 const char *chars_to_trim)
529 {
530 // ltrim, rtrim
531 str.erase(0, str.find_first_not_of(chars_to_trim));
532 str.erase(str.find_last_not_of(chars_to_trim) + 1);
533 }
534
535 //-----------------------------------------------------------------------------
536 void
split_path(const std::string & path,std::string & curr,std::string & next)537 split_path(const std::string &path,
538 std::string &curr,
539 std::string &next)
540 {
541 split_string(path,
542 std::string("/"),
543 curr,
544 next);
545 }
546
547 //-----------------------------------------------------------------------------
548 void
rsplit_path(const std::string & path,std::string & curr,std::string & next)549 rsplit_path(const std::string &path,
550 std::string &curr,
551 std::string &next)
552 {
553 rsplit_string(path,
554 std::string("/"),
555 curr,
556 next);
557 }
558
559 //-----------------------------------------------------------------------------
560 std::string
join_path(const std::string & left,const std::string & right)561 join_path(const std::string &left,
562 const std::string &right)
563 {
564 std::string res = left;
565 if(res.size() > 0 &&
566 res[res.size()-1] != '/' &&
567 right.size() > 0 )
568 {
569 res += "/";
570 }
571 res += right;
572 return res;
573 }
574
575 //-----------------------------------------------------------------------------
576 std::string
file_path_separator()577 file_path_separator()
578 {
579 return file_path_sep_string;
580 }
581
582
583 //-----------------------------------------------------------------------------
584 void
split_file_path(const std::string & path,std::string & curr,std::string & next)585 split_file_path(const std::string &path,
586 std::string &curr,
587 std::string &next)
588 {
589 split_string(path,
590 file_path_sep_string,
591 curr,
592 next);
593 }
594
595 //-----------------------------------------------------------------------------
596 void
rsplit_file_path(const std::string & path,std::string & curr,std::string & next)597 rsplit_file_path(const std::string &path,
598 std::string &curr,
599 std::string &next)
600 {
601 rsplit_string(path,
602 file_path_sep_string,
603 curr,
604 next);
605 }
606
607 //---------------------------------------------------------------------------//
608 void
split_file_path(const std::string & path,const std::string & sep,std::string & curr,std::string & next)609 split_file_path(const std::string &path,
610 const std::string &sep,
611 std::string &curr,
612 std::string &next)
613 {
614 // if we are splitting by ":", we need to be careful on windows
615 // since drive letters include ":"
616 //
617 // NOTE: We could if-def for windows, but its nice to be able
618 // to run unit tests on other platforms.
619 if( sep == std::string(":") &&
620 path.size() > 2 &&
621 path[1] == ':' &&
622 path[2] == '\\')
623 {
624 // eval w/o drive letter
625 if(path.size() > 3)
626 {
627 std::string check_path = path.substr(3);
628 conduit::utils::split_string(check_path,
629 sep,
630 curr,
631 next);
632 // add drive letter back
633 curr = path.substr(0,3) + curr;
634 }
635 else
636 {
637 // degen case, we we only have the drive letter
638 curr = path;
639 next = "";
640 }
641 }
642 else
643 {
644 // normal case
645 conduit::utils::split_string(path,
646 sep,
647 curr,
648 next);
649
650 }
651 }
652
653
654 //---------------------------------------------------------------------------//
655 void
rsplit_file_path(const std::string & path,const std::string & sep,std::string & curr,std::string & next)656 rsplit_file_path(const std::string &path,
657 const std::string &sep,
658 std::string &curr,
659 std::string &next)
660 {
661 // if we are splitting by ":", we need to be careful on windows
662 // since drive letters include ":"
663 //
664 // NOTE: We could if-def for windows, but its nice to be able
665 // to run unit tests on other platforms.
666 if( sep == std::string(":") &&
667 path.size() > 2 &&
668 path[1] == ':' &&
669 path[2] == '\\')
670 {
671 // eval w/o drive letter
672 if(path.size() > 3)
673 {
674 std::string check_path = path.substr(3);
675 conduit::utils::rsplit_string(check_path,
676 sep,
677 curr,
678 next);
679 // add drive letter back
680 if(next == "")
681 {
682 // there was no split
683 curr = path.substr(0,3) + curr;
684 }
685 else
686 {
687 // there was a split
688 next = path.substr(0,3) + next;
689 }
690 }
691 else
692 {
693 // degen case, we we only have the drive letter
694 curr = path;
695 next = "";
696 }
697 }
698 else
699 {
700 // normal case
701 conduit::utils::rsplit_string(path,
702 sep,
703 curr,
704 next);
705
706 }
707 }
708
709
710
711 //-----------------------------------------------------------------------------
712 std::string
join_file_path(const std::string & left,const std::string & right)713 join_file_path(const std::string &left,
714 const std::string &right)
715 {
716 std::string res = left;
717 if(res.size() > 0 && res[res.size()-1] != file_path_sep_string[0])
718 {
719 res += file_path_sep_string;
720 }
721 res += right;
722 return res;
723 }
724
725
726 //-----------------------------------------------------------------------------
727 bool
is_file(const std::string & path)728 is_file(const std::string &path)
729 {
730 bool res = false;
731 struct stat path_stat;
732 if(stat(path.c_str(), &path_stat) == 0)
733 {
734 if(path_stat.st_mode & S_IFREG)
735 res = true;
736 }
737 return res;
738 }
739
740 //-----------------------------------------------------------------------------
741 int64
file_size(const std::string & path)742 file_size(const std::string &path)
743 {
744 std::ifstream ifs(path, std::ifstream::ate | std::ifstream::binary);
745 return (int64) ifs.tellg();
746 }
747
748 //-----------------------------------------------------------------------------
749 bool
is_directory(const std::string & path)750 is_directory(const std::string &path)
751 {
752 bool res = false;
753 struct stat path_stat;
754 if (stat(path.c_str(), &path_stat) == 0)
755 {
756 if (path_stat.st_mode & S_IFDIR)
757 res = true;
758 }
759 return res;
760 }
761
762 //-----------------------------------------------------------------------------
763 bool
list_directory_contents(const std::string & path,std::vector<std::string> & contents,bool ignore_dot)764 list_directory_contents(const std::string &path,
765 std::vector<std::string> &contents,
766 bool ignore_dot)
767 {
768 contents.clear();
769 if(path.empty())
770 {
771 return false;
772 }
773
774 // If the given path doesn't end with a path sep, add it to the end
775 const std::string directory =
776 (path.substr(path.size()-1) != file_path_sep_string)
777 ? (path + file_path_sep_string)
778 : path;
779
780 #if defined(CONDUIT_PLATFORM_WINDOWS)
781 // NOTE: Adapted from VisIt
782 if(path == "My Computer")
783 {
784 // Add the drives to the list.
785 char buf[200];
786 DWORD bufLen = 200;
787 DWORD slen = GetLogicalDriveStrings(200, buf);
788
789 if(slen > 0)
790 {
791 char *ptr = buf;
792 while(*ptr != 0)
793 {
794 contents.push_back(ptr);
795 ptr += (contents.back().size() + 1);
796 }
797 }
798 }
799 else
800 {
801 // Search all files in the given directory
802 std::string search_path(directory + "*");
803 WIN32_FIND_DATA fd;
804 HANDLE dirHandle = FindFirstFile(search_path.c_str(), &fd);
805 if(dirHandle != INVALID_HANDLE_VALUE)
806 {
807 // Iterate the contents of the directory using FindNextFile
808 do
809 {
810 const std::string name(fd.cFileName);
811 if(name.empty())
812 {
813 continue;
814 }
815
816 if(ignore_dot && name[0] == '.')
817 {
818 continue;
819 }
820 contents.push_back(directory + name);
821 } while(FindNextFile(dirHandle, &fd));
822 FindClose(dirHandle);
823 }
824 }
825 #else
826 DIR *dir;
827 dirent *ent;
828 // Open the directory
829 dir = opendir(path.c_str());
830 if(dir)
831 {
832 // Iterate the contents of the directory using ent
833 while((ent = readdir(dir)) != NULL)
834 {
835 const std::string name(ent->d_name);
836 if(name.empty())
837 {
838 continue;
839 }
840
841 if(ignore_dot && name[0] == '.')
842 {
843 continue;
844 }
845
846 contents.push_back(directory + ent->d_name);
847 }
848 closedir(dir);
849 }
850 #endif
851 return !contents.empty();
852 }
853
854 //-----------------------------------------------------------------------------
855 bool
create_directory(const std::string & path)856 create_directory(const std::string &path)
857 {
858
859 #if defined(CONDUIT_PLATFORM_WINDOWS)
860 return (_mkdir(path.c_str()) == 0);
861 #else
862 return (mkdir(path.c_str(),S_IRWXU | S_IRWXG) == 0);
863 #endif
864 }
865
866
867 //-----------------------------------------------------------------------------
868 bool
remove_file(const std::string & path)869 remove_file(const std::string &path)
870 {
871 return ( remove(path.c_str()) == 0 );
872 }
873
874 //-----------------------------------------------------------------------------
875 bool
remove_directory(const std::string & path)876 remove_directory(const std::string &path)
877 {
878 #if defined(CONDUIT_PLATFORM_WINDOWS)
879 return ( _rmdir(path.c_str()) == 0 );
880 #else
881 return ( remove(path.c_str()) == 0 );
882 #endif
883 }
884
885
886 //-----------------------------------------------------------------------------
887 bool
remove_path_if_exists(const std::string & path)888 remove_path_if_exists(const std::string &path)
889 {
890 if(utils::is_file(path))
891 {
892 return utils::remove_file(path);
893 }
894 else if(utils::is_directory(path))
895 {
896 return utils::remove_directory(path);
897 }
898 else
899 {
900 // nothing to do, report we didn't remove anything
901 return false;
902 }
903 }
904
905
906 //-----------------------------------------------------------------------------
907 int
system_execute(const std::string & cmd)908 system_execute(const std::string &cmd)
909 {
910 return system(cmd.c_str());
911 }
912
913
914 //-----------------------------------------------------------------------------
915 bool
check_word_char(const char v)916 check_word_char(const char v)
917 {
918 bool res = ( ( 'A' <= v) &&
919 ( v <= 'Z') );
920 res = res || ( ( 'a' <= v) &&
921 ( v <= 'z') );
922 res = res || v == '_';
923 return res;
924 }
925
926 //-----------------------------------------------------------------------------
927 bool
check_num_char(const char v)928 check_num_char(const char v)
929 {
930 bool res = ( ( '0' <= v) &&
931 ( v <= '9') );
932 return res;
933 }
934
935
936 //-----------------------------------------------------------------------------
937 std::string
json_sanitize(const std::string & json)938 json_sanitize(const std::string &json)
939 {
940 ///
941 /// Really wanted to use regexs to solve this
942 /// but posix regs are greedy & it was hard for me to construct
943 /// a viable regex, vs those that support non-greedy (Python + Perl style regex)
944 ///
945 /// Here are regexs I was able to use in python:
946 // *comments*
947 // Remove '//' to end of line
948 // regex: \/\/.*\?n
949 // *limited quoteless*
950 // find words not surrounded by quotes
951 // regex: (?<!"|\w)(\w+)(?!"|\w)
952 // and add quotes
953
954 //
955 // for now, we use a simple char by char parser
956 //
957
958 std::string res;
959 bool in_comment=false;
960 bool in_string=false;
961 bool in_id =false;
962 std::string cur_id = "";
963
964 for(size_t i = 0; i < json.size(); ++i)
965 {
966 bool emit = true;
967 // check for start & end of a string
968 if(json[i] == '\"' && ( i > 0 && ( json[i-1] != '\\' )))
969 {
970 if(in_string)
971 in_string = false;
972 else
973 in_string = true;
974 }
975
976 // handle two cases were we want to sanitize:
977 // comments '//' to end of line & unquoted ids
978 if(!in_string)
979 {
980 if(!in_comment)
981 {
982 if( json[i] == '/' &&
983 i < (json.size()-1) &&
984 json[i+1] == '/')
985 {
986 in_comment = true;
987 emit = false;
988 }
989 }
990
991 if(!in_comment)
992 {
993
994 if( !in_id && check_word_char(json[i]))
995 {
996 // ids can't start with numbers ,
997 // check the prior char if it exists
998 if(i > 0 &&
999 !check_num_char(json[i-1]) &&
1000 json[i-1] != '.')
1001 {
1002 in_id = true;
1003 // accum id chars
1004 cur_id += json[i];
1005 emit = false;
1006 }
1007 }
1008 else if(in_id) // finish the id
1009 {
1010 if(check_word_char(json[i]) || check_num_char(json[i]))
1011 {
1012 in_id = true;
1013 // accum id chars
1014 cur_id += json[i];
1015 emit = false;
1016 }
1017 else
1018 {
1019 in_id = false;
1020 /// check for true, false, and null
1021 /// which we need to support in json
1022 if( !(cur_id == "true" ||
1023 cur_id == "false" ||
1024 cur_id == "null" ))
1025 {
1026 /// emit cur_id
1027 res += "\"" + cur_id + "\"";
1028 }
1029 else
1030 {
1031 /// don't escape true or false
1032 res += cur_id;
1033 }
1034
1035 cur_id = "";
1036 }
1037 // we will also emit this char
1038 }
1039 }
1040
1041 if(in_comment)
1042 {
1043 emit = false;
1044 if(json[i] == '\n')
1045 {
1046 in_comment = false;
1047 }
1048 }
1049 }
1050
1051 if(emit)
1052 res += json[i];
1053 }
1054
1055 return res;
1056 }
1057
1058 //-----------------------------------------------------------------------------
1059 void
indent(std::ostream & os,index_t indent,index_t depth,const std::string & pad)1060 indent(std::ostream &os,
1061 index_t indent,
1062 index_t depth,
1063 const std::string &pad)
1064 {
1065 for(index_t i=0;i<depth;i++)
1066 {
1067 for(index_t j=0;j<indent;j++)
1068 {
1069 os << pad;
1070 }
1071 }
1072 }
1073
1074 //-----------------------------------------------------------------------------
1075 void
sleep(index_t milliseconds)1076 sleep(index_t milliseconds)
1077 {
1078
1079 #if defined(CONDUIT_PLATFORM_WINDOWS)
1080 Sleep((DWORD)milliseconds);
1081 #else // unix, etc
1082 struct timespec ts;
1083 ts.tv_sec = milliseconds / 1000;
1084 ts.tv_nsec = (milliseconds % 1000) * 1000000;
1085 nanosleep(&ts, NULL);
1086 #endif
1087
1088 }
1089
1090
1091 //-----------------------------------------------------------------------------
1092 std::string
escape_special_chars(const std::string & input)1093 escape_special_chars(const std::string &input)
1094 {
1095 std::string res;
1096 for(size_t i = 0; i < input.size(); ++i)
1097 {
1098 char val = input[i];
1099 // supported special chars
1100 switch(val)
1101 {
1102 // quotes and slashes
1103 case '\"':
1104 case '\\':
1105 {
1106 res += '\\';
1107 res += val;
1108 break;
1109 }
1110 // newline
1111 case '\n':
1112 {
1113 res += "\\n";
1114 break;
1115 }
1116 // tab
1117 case '\t':
1118 {
1119 res += "\\t";
1120 break;
1121 }
1122 // backspace
1123 case '\b':
1124 {
1125 res += "\\b";
1126 break;
1127 }
1128 // formfeed
1129 case '\f':
1130 {
1131 res += "\\f";
1132 break;
1133 }
1134 // carriage return
1135 case '\r':
1136 {
1137 res += "\\r";
1138 break;
1139 }
1140
1141 default:
1142 {
1143 res += val;
1144 }
1145 }
1146 }
1147
1148 return res;
1149 }
1150
1151 //-----------------------------------------------------------------------------
1152 std::string
unescape_special_chars(const std::string & input)1153 unescape_special_chars(const std::string &input)
1154 {
1155 std::string res;
1156 size_t input_size = input.size();
1157 for(size_t i = 0; i < input_size; ++i)
1158 {
1159 // check for escape char
1160 if( input[i] == '\\' &&
1161 i < (input_size -1))
1162 {
1163 char val = input[i+1];
1164 switch(val)
1165 {
1166 // quotes and slashes
1167 case '\"':
1168 case '\\':
1169 // even though we don't escape forward slashes
1170 // we support unescaping them.
1171 case '/':
1172 {
1173 res += val;
1174 // skip escape char
1175 i++;
1176 break;
1177 }
1178 // newline
1179 case 'n':
1180 {
1181 res += "\n";
1182 // skip escape char
1183 i++;
1184 break;
1185 }
1186 // tab
1187 case 't':
1188 {
1189 res += "\t";
1190 // skip escape char
1191 i++;
1192 break;
1193 }
1194 // backspace
1195 case 'b':
1196 {
1197 res += "\b";
1198 // skip escape char
1199 i++;
1200 break;
1201 }
1202 // formfeed
1203 case 'f':
1204 {
1205 res += "\f";
1206 // skip escape char
1207 i++;
1208 break;
1209 }
1210 // carriage return
1211 case 'r':
1212 {
1213 res += "\r";
1214 // skip escape char
1215 i++;
1216 break;
1217 }
1218 // \uFFFF & unknown escape strings
1219 default:
1220 {
1221 // simply emit
1222 res += val;
1223 break;
1224 }
1225 }
1226 }
1227 else
1228 {
1229 res += input[i];
1230 }
1231 }
1232
1233 return res;
1234 }
1235
1236
1237 //-----------------------------------------------------------------------------
1238 void
base64_encode(const void * src,index_t src_nbytes,void * dest)1239 base64_encode(const void *src,
1240 index_t src_nbytes,
1241 void *dest)
1242 {
1243 int nbytes = (int)src_nbytes;
1244 base64_encodestate enc_state;
1245 base64_init_encodestate(&enc_state);
1246 const char *src_ptr = (const char*)src;
1247 char *des_ptr = (char*)dest;
1248 memset(des_ptr,0,(size_t)base64_encode_buffer_size(src_nbytes));
1249
1250 int code_len = base64_encode_block(src_ptr,
1251 nbytes,
1252 des_ptr,
1253 &enc_state);
1254 des_ptr += code_len;
1255 code_len = base64_encode_blockend(des_ptr, &enc_state);
1256 des_ptr += code_len;
1257
1258 // for some reason libb64 adds a newline
1259 des_ptr[-1] = 0;
1260 }
1261
1262 //-----------------------------------------------------------------------------
1263 index_t
base64_encode_buffer_size(index_t src_nbytes)1264 base64_encode_buffer_size(index_t src_nbytes)
1265 {
1266 return (4*src_nbytes) / 3 + 4 + 1;
1267 }
1268
1269 //-----------------------------------------------------------------------------
1270 index_t
base64_decode_buffer_size(index_t encoded_nbytes)1271 base64_decode_buffer_size(index_t encoded_nbytes)
1272 {
1273 return (encoded_nbytes / 4) * 3 + 1;
1274 }
1275
1276
1277 //-----------------------------------------------------------------------------
1278 void
base64_decode(const void * src,index_t src_nbytes,void * dest)1279 base64_decode(const void *src,
1280 index_t src_nbytes,
1281 void *dest)
1282 {
1283 base64_decodestate dec_state;
1284 int src_len = (int)src_nbytes;
1285 base64_init_decodestate(&dec_state);
1286 const char *src_ptr = (const char*)src;
1287 char *des_ptr = (char*)dest;
1288 base64_decode_block(src_ptr,
1289 src_len,
1290 des_ptr,
1291 &dec_state);
1292 }
1293
1294 //-----------------------------------------------------------------------------
1295 bool
string_is_integer(const std::string & s)1296 string_is_integer(const std::string &s)
1297 {
1298 int v = -1;
1299 std::istringstream iss(s);
1300 iss >> v;
1301
1302 return !iss.fail();
1303 }
1304
1305
1306 //-----------------------------------------------------------------------------
1307 std::string
float64_to_string(float64 value)1308 float64_to_string(float64 value)
1309 {
1310 char buffer[64] = {0};
1311 snprintf(buffer,64,"%.15g",value);
1312
1313 std::string res(buffer);
1314
1315 // we check for inf or nan in string form.
1316 // std::isnan, isn't portable until c++11
1317 // http://stackoverflow.com/questions/570669/checking-if-a-double-or-float-is-nan-in-c
1318
1319 // searching for 'n' covers inf and nan
1320 if(res.find('n') == std::string::npos &&
1321 res.find('.') == std::string::npos &&
1322 res.find('e') == std::string::npos )
1323 {
1324 res += ".0";
1325 }
1326
1327 return res;
1328 }
1329
1330 //-----------------------------------------------------------------------------
1331 /// fmt style string formatting helpers
1332 //-----------------------------------------------------------------------------
1333
1334 //-----------------------------------------------------------------------------
1335 std::string
format(const std::string & pattern,const conduit::Node & args)1336 format(const std::string &pattern,
1337 const conduit::Node &args)
1338 {
1339 if( !args.dtype().is_object() &&
1340 !args.dtype().is_list())
1341 {
1342 CONDUIT_ERROR("conduit::utils::format args Node must be "
1343 << " an `object`, or `list`.\n"
1344 << "Passed node type: "
1345 << "`" << args.dtype().name() << "`.");
1346 }
1347
1348 // if we have an object, we used named args for fmt
1349 // if we have an list, we don't use named args for fmt
1350 bool is_obj = args.dtype().is_object();
1351
1352 conduit_fmt::dynamic_format_arg_store<conduit_fmt::format_context> store;
1353 conduit::NodeConstIterator itr = args.children();
1354
1355 while(itr.has_next())
1356 {
1357 const conduit::Node &curr = itr.next();
1358 switch(curr.dtype().id())
1359 {
1360 /* ints */
1361 case conduit::DataType::INT8_ID:
1362 {
1363 int8 val = curr.as_int8();
1364 if(is_obj)
1365 {
1366 store.push_back(conduit_fmt::arg(itr.name().c_str(),
1367 val));
1368 }
1369 else
1370 {
1371 store.push_back(val);
1372 }
1373 break;
1374 }
1375 case conduit::DataType::INT16_ID:
1376 {
1377 int16 val = curr.as_int16();
1378 if(is_obj)
1379 {
1380 store.push_back(conduit_fmt::arg(itr.name().c_str(),
1381 val));
1382 }
1383 else
1384 {
1385 store.push_back(val);
1386 }
1387 break;
1388 }
1389 case conduit::DataType::INT32_ID:
1390 {
1391 int32 val = curr.as_int32();
1392 if(is_obj)
1393 {
1394 store.push_back(conduit_fmt::arg(itr.name().c_str(),
1395 val));
1396 }
1397 else
1398 {
1399 store.push_back(val);
1400 }
1401 break;
1402 }
1403 case conduit::DataType::INT64_ID:
1404 {
1405 int64 val = curr.as_int64();
1406 if(is_obj)
1407 {
1408 store.push_back(conduit_fmt::arg(itr.name().c_str(),
1409 val));
1410 }
1411 else
1412 {
1413 store.push_back(val);
1414 }
1415 break;
1416 }
1417 /* uints */
1418 case conduit::DataType::UINT8_ID:
1419 {
1420 uint8 val = curr.as_uint8();
1421 if(is_obj)
1422 {
1423 store.push_back(conduit_fmt::arg(itr.name().c_str(),
1424 val));
1425 }
1426 else
1427 {
1428 store.push_back(val);
1429 }
1430 break;
1431 }
1432 case conduit::DataType::UINT16_ID:
1433 {
1434 uint16 val = curr.as_uint16();
1435 if(is_obj)
1436 {
1437 store.push_back(conduit_fmt::arg(itr.name().c_str(),
1438 val));
1439 }
1440 else
1441 {
1442 store.push_back(val);
1443 }
1444 break;
1445 }
1446 case conduit::DataType::UINT32_ID:
1447 {
1448 uint32 val = curr.as_uint32();
1449 if(is_obj)
1450 {
1451 store.push_back(conduit_fmt::arg(itr.name().c_str(),
1452 val));
1453 }
1454 else
1455 {
1456 store.push_back(val);
1457 }
1458 break;
1459 }
1460 case conduit::DataType::UINT64_ID:
1461 {
1462 uint64 val = curr.as_uint64();
1463 if(is_obj)
1464 {
1465 store.push_back(conduit_fmt::arg(itr.name().c_str(),
1466 val));
1467 }
1468 else
1469 {
1470 store.push_back(val);
1471 }
1472 break;
1473 }
1474 /* floats */
1475 case conduit::DataType::FLOAT32_ID:
1476 {
1477 float32 val = curr.as_float32();
1478 if(is_obj)
1479 {
1480 store.push_back(conduit_fmt::arg(itr.name().c_str(),
1481 val));
1482 }
1483 else
1484 {
1485 store.push_back(val);
1486 }
1487 break;
1488 }
1489 case conduit::DataType::FLOAT64_ID:
1490 {
1491 float64 val = curr.as_float64();
1492 if(is_obj)
1493 {
1494 store.push_back(conduit_fmt::arg(itr.name().c_str(),
1495 val));
1496 }
1497 else
1498 {
1499 store.push_back(val);
1500 }
1501 break;
1502 }
1503 // string case
1504 case conduit::DataType::CHAR8_STR_ID:
1505 {
1506 std::string val = curr.as_string();
1507 if(is_obj)
1508 {
1509 store.push_back(conduit_fmt::arg(itr.name().c_str(),
1510 val));
1511 }
1512 else
1513 {
1514 store.push_back(val);
1515 }
1516
1517 break;
1518 }
1519 default:
1520 {
1521 // ERROR -- list, object, or empty
1522 CONDUIT_ERROR("conduit::utils::format does not support"
1523 << " `object`, `list`, or `empty` Nodes"
1524 << " as arguments.\n"
1525 << "'" << itr.name() << "' type: "
1526 << "`" << curr.dtype().name() << "`.");
1527 }
1528 }
1529 }
1530
1531 std::string res = "";
1532
1533 try
1534 {
1535 res = conduit_fmt::vformat(pattern,store);
1536 }
1537 catch(const std::runtime_error& re)
1538 {
1539 CONDUIT_ERROR("conduit::utils::format error: "
1540 << "fmt error message:\n"
1541 << re.what());
1542 }
1543
1544 return res;
1545 }
1546
1547 //-----------------------------------------------------------------------------
1548 std::string
format(const std::string & pattern,const conduit::Node & maps,index_t map_index)1549 format(const std::string &pattern,
1550 const conduit::Node &maps,
1551 index_t map_index)
1552 {
1553 // neg bounds check
1554 if(map_index < 0)
1555 {
1556 CONDUIT_ERROR("conduit::utils::format map_index must be positive "
1557 << " (map_index = " << map_index << ")");
1558 }
1559
1560 if( !maps.dtype().is_object() &&
1561 !maps.dtype().is_list())
1562 {
1563 CONDUIT_ERROR("conduit::utils::format maps Node must be "
1564 << " an `object`, or `list`\n."
1565 << " Passed node type: "
1566 << "`" << maps.dtype().name() << "`.");
1567 }
1568
1569 // if we have an object, we used named args for fmt
1570 // if we have an list, we don't use named args for fmt
1571 bool is_obj = maps.dtype().is_object();
1572
1573 conduit_fmt::dynamic_format_arg_store<conduit_fmt::format_context> store;
1574 conduit::NodeConstIterator itr = maps.children();
1575 while(itr.has_next())
1576 {
1577 const conduit::Node &curr = itr.next();
1578 // map bounds checks
1579 if(curr.dtype().is_list())
1580 {
1581 if(map_index >= curr.number_of_children())
1582 {
1583 CONDUIT_ERROR("conduit::utils::format map_index "
1584 << "(value = " << map_index << ")"
1585 << " for '" << itr.name() << "'"
1586 << " list map entry "
1587 << " is out of bounds."
1588 << " Number of children = "
1589 << curr.number_of_children()
1590 << ". Valid range is [0,"
1591 << curr.number_of_children() << ").");
1592 }
1593 }
1594 else if(curr.dtype().is_number())
1595 {
1596 if(map_index >= curr.dtype().number_of_elements())
1597 {
1598 CONDUIT_ERROR("conduit::utils::format map_index "
1599 << "(value = " << map_index << ")"
1600 << " for '" << itr.name() << "'"
1601 << " array map entry "
1602 << " is out of bounds."
1603 << " Number of elements = "
1604 << curr.dtype().number_of_elements()
1605 << ". Valid range is [0,"
1606 << curr.dtype().number_of_elements() << ").");
1607 }
1608 }
1609
1610 switch(curr.dtype().id())
1611 {
1612 /* ints */
1613 case conduit::DataType::INT8_ID:
1614 {
1615 int8 val = curr.as_int8_ptr()[map_index];
1616 if(is_obj)
1617 {
1618 store.push_back(conduit_fmt::arg(itr.name().c_str(),
1619 val));
1620 }
1621 else
1622 {
1623 store.push_back(val);
1624 }
1625 break;
1626 }
1627 case conduit::DataType::INT16_ID:
1628 {
1629 int16 val = curr.as_int16_ptr()[map_index];
1630 if(is_obj)
1631 {
1632 store.push_back(conduit_fmt::arg(itr.name().c_str(),
1633 val));
1634 }
1635 else
1636 {
1637 store.push_back(val);
1638 }
1639 break;
1640 }
1641 case conduit::DataType::INT32_ID:
1642 {
1643 int32 val = curr.as_int32_ptr()[map_index];
1644 if(is_obj)
1645 {
1646 store.push_back(conduit_fmt::arg(itr.name().c_str(),
1647 val));
1648 }
1649 else
1650 {
1651 store.push_back(val);
1652 }
1653 break;
1654 }
1655 case conduit::DataType::INT64_ID:
1656 {
1657 int64 val = curr.as_int64_ptr()[map_index];
1658 if(is_obj)
1659 {
1660 store.push_back(conduit_fmt::arg(itr.name().c_str(),
1661 val));
1662 }
1663 else
1664 {
1665 store.push_back(val);
1666 }
1667 break;
1668 }
1669 /* uints */
1670 case conduit::DataType::UINT8_ID:
1671 {
1672 uint8 val = curr.as_uint8_ptr()[map_index];
1673 if(is_obj)
1674 {
1675 store.push_back(conduit_fmt::arg(itr.name().c_str(),
1676 val));
1677 }
1678 else
1679 {
1680 store.push_back(val);
1681 }
1682 break;
1683 }
1684 case conduit::DataType::UINT16_ID:
1685 {
1686 uint16 val = curr.as_uint16_ptr()[map_index];
1687 if(is_obj)
1688 {
1689 store.push_back(conduit_fmt::arg(itr.name().c_str(),
1690 val));
1691 }
1692 else
1693 {
1694 store.push_back(val);
1695 }
1696 break;
1697 }
1698 case conduit::DataType::UINT32_ID:
1699 {
1700 uint32 val = curr.as_uint32_ptr()[map_index];
1701 if(is_obj)
1702 {
1703 store.push_back(conduit_fmt::arg(itr.name().c_str(),
1704 val));
1705 }
1706 else
1707 {
1708 store.push_back(val);
1709 }
1710 break;
1711 }
1712 case conduit::DataType::UINT64_ID:
1713 {
1714 uint64 val = curr.as_uint64_ptr()[map_index];
1715 if(is_obj)
1716 {
1717 store.push_back(conduit_fmt::arg(itr.name().c_str(),
1718 val));
1719 }
1720 else
1721 {
1722 store.push_back(val);
1723 }
1724 break;
1725 }
1726 /* floats */
1727 case conduit::DataType::FLOAT32_ID:
1728 {
1729 float32 val = curr.as_float32_ptr()[map_index];
1730 if(is_obj)
1731 {
1732 store.push_back(conduit_fmt::arg(itr.name().c_str(),
1733 val));
1734 }
1735 else
1736 {
1737 store.push_back(val);
1738 }
1739 break;
1740 }
1741 case conduit::DataType::FLOAT64_ID:
1742 {
1743 float64 val = curr.as_float64_ptr()[map_index];
1744 if(is_obj)
1745 {
1746 store.push_back(conduit_fmt::arg(itr.name().c_str(),
1747 val));
1748 }
1749 else
1750 {
1751 store.push_back(val);
1752 }
1753 break;
1754 }
1755 // support lists of strings ONLY ...
1756 case conduit::DataType::LIST_ID:
1757 {
1758 const Node &lst_ent = curr[map_index];
1759 if(!lst_ent.dtype().is_string())
1760 {
1761 CONDUIT_ERROR("conduit::utils::format (maps) only supports "
1762 << " the list maps case for strings."
1763 << "'" << itr.name() << "' entry at index "
1764 << map_index << " type: "
1765 << "`" << lst_ent.dtype().name() << "`.");
1766 }
1767
1768 std::string val = lst_ent.as_string();
1769 if(is_obj)
1770 {
1771 store.push_back(conduit_fmt::arg(itr.name().c_str(),
1772 val));
1773 }
1774 else
1775 {
1776 store.push_back(val);
1777 }
1778
1779 break;
1780 }
1781 default:
1782 {
1783 // ERROR -- object, string, or empty
1784 CONDUIT_ERROR("conduit::utils::format (maps) does not support"
1785 << " `object`, `string, or `empty` Nodes"
1786 << " as arguments."
1787 << "'" << itr.name() << "' type: "
1788 << "`" << curr.dtype().name() << "`.");
1789 }
1790 }
1791 }
1792
1793 std::string res = "";
1794
1795 try
1796 {
1797 res = conduit_fmt::vformat(pattern,store);
1798 }
1799 catch(const std::runtime_error& re)
1800 {
1801 CONDUIT_ERROR("conduit::utils::format error: "
1802 << "fmt error message:\n"
1803 << re.what());
1804 }
1805
1806 return res;
1807 }
1808
1809
1810 //-----------------------------------------------------------------------------
1811 // String hash functions
1812 //-----------------------------------------------------------------------------
1813 namespace hashing
1814 {
1815 // NOTE: Borrowed from VisIt.
1816
1817 // ****************************************************************************
1818 // Function: Hash
1819 //
1820 // Purpose:
1821 // Hash a variable length stream of bytes into a 32-bit value.
1822 //
1823 // Can also be used effectively as a checksum.
1824 //
1825 // The best hash table sizes are powers of 2. There is no need to do
1826 // mod a prime (mod is sooo slow!). If you need less than 32 bits,
1827 // use a bitmask. For example, if you need only 10 bits, do
1828 // h = (h & BJHashmask(10));
1829 // In which case, the hash table should have hashsize(10) elements.
1830 //
1831 // If you are hashing n strings (unsigned char **)k, do it like this:
1832 // for (i=0, h=0; i<n; ++i) h = hash( k[i], len[i], h);
1833 //
1834 // Arguments:
1835 // k: the key ((the unaligned variable-length array of bytes)
1836 // length: the length of the key, in bytes
1837 // initval: can be any 4-byte value
1838 //
1839 // Returns: A 32-bit value. Every bit of the key affects every bit of
1840 // the return value. Every 1-bit and 2-bit delta achieves avalanche.
1841 //
1842 // Programmer: By Bob Jenkins, 1996. bob_jenkins@burtleburtle.net.
1843 //
1844 // You may use this code any way you wish, private, educational, or
1845 // commercial. It's free. However, do NOT use for cryptographic purposes.
1846 //
1847 // See http://burtleburtle.net/bob/hash/evahash.html
1848 // ****************************************************************************
1849
1850 #define bjhash_mix(a,b,c) \
1851 { \
1852 a -= b; a -= c; a ^= (c>>13); \
1853 b -= c; b -= a; b ^= (a<<8); \
1854 c -= a; c -= b; c ^= (b>>13); \
1855 a -= b; a -= c; a ^= (c>>12); \
1856 b -= c; b -= a; b ^= (a<<16); \
1857 c -= a; c -= b; c ^= (b>>5); \
1858 a -= b; a -= c; a ^= (c>>3); \
1859 b -= c; b -= a; b ^= (a<<10); \
1860 c -= a; c -= b; c ^= (b>>15); \
1861 }
1862
Hash(const unsigned char * k,unsigned int length,unsigned int initval)1863 inline unsigned int Hash(const unsigned char *k, unsigned int length, unsigned int initval)
1864 {
1865 unsigned int a,b,c,len;
1866
1867 len = length;
1868 a = b = 0x9e3779b9;
1869 c = initval;
1870
1871 while (len >= 12)
1872 {
1873 a += (k[0] +((unsigned int)k[1]<<8) +((unsigned int)k[2]<<16) +((unsigned int)k[3]<<24));
1874 b += (k[4] +((unsigned int)k[5]<<8) +((unsigned int)k[6]<<16) +((unsigned int)k[7]<<24));
1875 c += (k[8] +((unsigned int)k[9]<<8) +((unsigned int)k[10]<<16)+((unsigned int)k[11]<<24));
1876 bjhash_mix(a,b,c);
1877 k += 12; len -= 12;
1878 }
1879
1880 c += length;
1881
1882 switch(len)
1883 {
1884 case 11: c+=((unsigned int)k[10]<<24); /* FALLTHROUGH */
1885 case 10: c+=((unsigned int)k[9]<<16); /* FALLTHROUGH */
1886 case 9 : c+=((unsigned int)k[8]<<8); /* FALLTHROUGH */
1887 case 8 : b+=((unsigned int)k[7]<<24); /* FALLTHROUGH */
1888 case 7 : b+=((unsigned int)k[6]<<16); /* FALLTHROUGH */
1889 case 6 : b+=((unsigned int)k[5]<<8); /* FALLTHROUGH */
1890 case 5 : b+=k[4]; /* FALLTHROUGH */
1891 case 4 : a+=((unsigned int)k[3]<<24); /* FALLTHROUGH */
1892 case 3 : a+=((unsigned int)k[2]<<16); /* FALLTHROUGH */
1893 case 2 : a+=((unsigned int)k[1]<<8); /* FALLTHROUGH */
1894 case 1 : a+=k[0];
1895 }
1896
1897 bjhash_mix(a,b,c);
1898
1899 return c;
1900 }
1901
1902 // Just to keep this macro from leaking out and polluting the global namespace
1903 #undef bjhash_mix
1904
1905 }
1906 //-----------------------------------------------------------------------------
1907 // -- end conduit::utils::hashing --
1908 //-----------------------------------------------------------------------------
1909
1910 unsigned int
hash(const char * k,unsigned int length,unsigned int initval)1911 hash(const char *k, unsigned int length, unsigned int initval)
1912 {
1913 return hashing::Hash((unsigned char const*)k, length, initval);
1914 }
1915
1916 unsigned int
hash(const char * k,unsigned int initval)1917 hash(const char *k, unsigned int initval)
1918 {
1919 return hashing::Hash((unsigned char const*)k,
1920 (unsigned int)strlen(k), initval);
1921 }
1922
1923 unsigned int
hash(const std::string & k,unsigned int initval)1924 hash(const std::string &k, unsigned int initval)
1925 {
1926 return hashing::Hash((unsigned char const*)k.c_str(),
1927 (unsigned int)k.size(), initval);
1928 }
1929
1930 }
1931 //-----------------------------------------------------------------------------
1932 // -- end conduit::utils --
1933 //-----------------------------------------------------------------------------
1934
1935 }
1936 //-----------------------------------------------------------------------------
1937 // -- end conduit:: --
1938 //-----------------------------------------------------------------------------
1939
1940
1941