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