1 // sidmiscutil.h - Useful utility classes. -*- C++ -*-
3 // Copyright (C) 1999-2003, 2006, 2007 Red Hat.
4 // This file is part of SID and is licensed under the GPL.
5 // See the file COPYING.SID for conditions for redistribution.
10 #include <sidconfig.h>
11 #include <sidpinutil.h>
23 #include <sys/cygwin.h>
25 // XXX: kludge for compatibility both with old & new libstdc++
27 #define STDCTYPE(func) std::func
29 #define STDCTYPE(func) func
41 // Instances of this class template function as bidirectional lookup
42 // tables (bijections) for mapping objects to each other by value.
43 template <class Obj1, class Obj2>
46 typedef std::map<Obj1,Obj2> forward_t;
47 typedef std::map<Obj2,Obj1> backward_t;
48 mutable forward_t forward;
49 mutable backward_t backward;
55 find(const Obj1& one, Obj2& two) const
57 typename forward_t::const_iterator it = this->forward.find(one);
58 if (it != this->forward.end())
60 typename backward_t::const_iterator ti = this->backward.find(it->second);
61 assert (ti != this->backward.end());
71 find(const Obj2& two, Obj1& one) const
73 typename backward_t::iterator ti = this->backward.find(two);
74 if (ti != this->backward.end())
76 typename forward_t::const_iterator it = this->forward.find(ti->second);
77 assert (it != this->forward.end());
85 // Register Obj1<->Obj2 pair. Both must be unique.
87 add (const Obj1& one, const Obj2& two)
89 assert (this->forward.find(one) == this->forward.end());
90 this->forward[one] = two;
91 assert (this->backward.find(two) == this->backward.end());
92 this->backward[two] = one;
95 // Register Obj1<->Obj2 pair. Both must be unique.
97 add (const Obj2& two, const Obj1& one)
105 this->forward.erase(this->forward.begin(), this->forward.end());
106 this->backward.erase(this->backward.begin(), this->backward.end());
111 // This class, a possible mix-in, may be used to limit the number of
112 // levels of recursion of functions. It functions as a counter that
113 // trips an assert() failure if it is made to exceed a configured
114 // limit. A limit of 0 is nonsensical and disallowed by assertion.
115 class recursion_limited
117 friend class recursion_record;
119 // XXX: might be nice to be signal-safe.
125 // These functions are for use by recursion_record instances
127 void recursion_enter ()
129 if (this->count <= this->max)
132 std::cerr << label << ": recursion limit exceeded (" << this->count << ")" << std::endl;
135 void recursion_exit ()
140 std::cerr << label << ": recursion limit underflow (" << this->count << ")" << std::endl;
145 return (this->count >= 0 && this->count <= this->max);
149 static const unsigned default_recursion_limit = 1000;
151 recursion_limited (const std::string& l,
152 int m = default_recursion_limit): count(0), max(m), label(l)
154 assert (this->max > 0);
159 // Each instance of the recursion_record class, during its lifetime,
160 // occupies one spot in a designated recursion_limited object's
161 // slot. Merely instantiate an instance of this type in any block
162 // that may suffer from indefinite recursion.
163 class recursion_record
165 recursion_limited* limiter;
168 recursion_record (recursion_limited* l): limiter(l)
170 this->limiter->recursion_enter ();
175 return (this->limiter->ok());
180 this->limiter->recursion_exit ();
186 // The string2stream and stream2string classes may be used as
187 // wrappers around string values to transmit them to/from streams.
188 // Normal string stream-in operators break on word boundaries.
189 // These wrappers encode arbitrary strings safely.
193 // string s = "anything at all";
194 // o << string2stream(s);
198 // i >> stream2string(s);
202 const std::string& sendme;
203 friend std::ostream& operator << (std::ostream& o, const string2stream& it);
205 string2stream (const std::string& s): sendme(s) {}
210 operator << (std::ostream& o, const string2stream& it)
213 for (unsigned i=0; i<it.sendme.length(); i++)
215 char c = it.sendme[i];
216 char hex[] = "0123456789ABCDEF";
217 if ((isprint(c)) && (! isspace(c)) && (c != '%'))
218 // NB: character class must not include '%' itself, and must
219 // include only chars that iostream>>std::string would consider
220 // parts of a single string.
223 // print URL-encoded char
224 o << "%" << hex[(c >> 4) & 0x0f] << hex[c & 0x0f];
232 std::string& parseme;
233 friend std::istream& operator >> (std::istream& i, const stream2string& it);
235 stream2string (std::string& s): parseme(s) {}
240 operator >> (std::istream& i, const stream2string& it)
247 for (j=1; j<encoded.length()-1; j++) // ignore double-quote delimiters
252 else if (j < encoded.length() - 3) // limiting case: "......%DD"
254 char c1 = toupper (encoded[++j]);
255 char c2 = toupper (encoded[++j]);
259 (c1 >= '0' && c1 <= '9') ? (c1 - '0') :
260 (c1 >= 'A' && c1 <= 'F') ? (c1 - 'A' + 10) :
261 (j = encoded.length(), 0); // error: detected just before exit
263 (c2 >= '0' && c2 <= '9') ? (c2 - '0') :
264 (c2 >= 'A' && c2 <= 'F') ? (c2 - 'A' + 10) :
265 (j = encoded.length(), 0); // error: detected just before exit
267 char cc = (d1 << 4) | d2;
274 // set flag on parse error
275 if (j != encoded.length() - 1)
276 i.setstate(std::ios::badbit);
282 // Tokenize a string. Use given set of separator characters.
283 inline std::vector<std::string>
284 tokenize (const std::string& str, const std::string& sep)
286 std::vector<std::string> tokens;
287 std::string remainder = str;
289 while (remainder.size() > 0)
291 std::string::size_type sep_pos = remainder.find_first_of (sep);
292 if (sep_pos == std::string::npos) // no more tokens
294 tokens.push_back (remainder);
299 tokens.push_back (remainder.substr (0, sep_pos));
301 remainder = remainder.substr (sep_pos);
302 // trim to next non-separator
303 sep_pos = remainder.find_first_not_of (sep);
304 if (sep_pos == std::string::npos) // none?
306 tokens.push_back ("");
309 remainder = remainder.substr (sep_pos);
319 const char* error_message = "(unknown error)";
321 error_message = strerror (errno);
323 return std::string(error_message);
327 // Return a vector of directory names, where the SID_LIBRARY_PATH
328 // and SID_EXEC_PREFIX environment variables are pointing. Convert
329 // all paths to POSIX form on Cygwin.
330 inline std::vector<std::string>
331 sid_file_search_path ()
333 std::vector<std::string> search_directories;
335 char* slp = getenv ("SID_LIBRARY_PATH"); // run-time configuration
338 search_directories = tokenize (slp, ":");
341 char* sep = getenv ("SID_EXEC_PREFIX"); // install-time configuration
343 char conv_fn[PATH_MAX*2];
346 int rc = cygwin_conv_to_full_posix_path (sep, conv_fn);
348 std::cerr << "sid_file_search_path: cygwin_conv_to_full_posix_path failed: "
349 << std_error_string () << std::endl;
354 if (!sep) sep = SID_EXEC_PREFIX; // build-time configuration
355 // We really just want to get to pkgdatadir, which is $prefix/share
356 // Guess exec-prefix == prefix
357 std::string pkglibdir1 = std::string(sep) + std::string("/share");
358 search_directories.push_back (pkglibdir1);
359 // Guess exec-prefix == prefix/H-HOST
360 std::string pkglibdir2 = std::string(sep) + std::string("/../share");
361 search_directories.push_back (pkglibdir2);
363 return search_directories;
367 // Look around cwd, the standard search dirs (.../sid)
368 // At worst, return the given name.
370 find_sid_data_file (const std::string& file)
372 std::vector<std::string> file_path = sid_file_search_path ();
373 std::vector<std::string> path;
375 path.push_back (std::string("")); // no prefix
376 for (unsigned i=0; i<file_path.size(); i++)
377 path.push_back (file_path[i] + std::string("/sid/"));
379 for (unsigned i=0; i<path.size(); i++)
381 const std::string& dir = path[i];
382 std::string full_path = dir + file;
385 std::ifstream f (full_path.c_str());
395 #define SID_LOG_PERSISTENT_BUFFER (HAVE_VSNPRINTF || ! HAVE_VASPRINTF)
396 #define SID_LOG_TRANSIENT_MALLOC_BUFFER (! SID_LOG_PERSISTENT_BUFFER)
398 logger (sidutil::output_pin *p_ulog_out_pin,
399 bool p_buffer_output,
400 sid::host_int_4 p_ulog_level,
401 std::string p_ulog_mode) :
402 ulog_level (p_ulog_level),
403 ulog_mode (p_ulog_mode),
404 buffer_output (p_buffer_output),
405 buffer_size (4096), // big enough for now
409 ulog_out_pin = p_ulog_out_pin;
410 #if SID_LOG_PERSISTENT_BUFFER
411 buffer = new char[buffer_size];
416 // Output any saved messages.
417 output_saved_messages ();
418 #if SID_LOG_PERSISTENT_BUFFER
423 void set_attributes (bool p_buffer_output,
424 sid::host_int_4 p_ulog_level,
425 std::string p_ulog_mode)
427 buffer_output = p_buffer_output;
428 ulog_level = p_ulog_level;
429 ulog_mode = p_ulog_mode;
432 virtual void log (sid::host_int_4 level, const char *fmt, va_list ap)
436 // Output any saved messages first
437 output_saved_messages ();
439 // Check the logging level and mode.
440 if (! check_level (level))
444 // Write the message into a buffer.
449 length = vsnprintf (buffer, buffer_size, fmt, ap);
450 if (length < buffer_size)
453 buffer_size = length + 256;
454 buffer = new char[buffer_size];
456 length = vasprintf (&buffer, fmt, ap);
459 length = STDCTYPE(vsprintf) (buffer, fmt, ap);
460 if (length >= buffer_size)
461 std::cerr << "Error: ulog buffer overflow!!!" << std::endl;
466 // If the output pin is not connected yet, Save the message for
467 // later. This happens when the log message is issued from the
468 // component's constructor.
471 saved_messages.push_back (std::string (buffer));
472 saved_levels.push_back (level);
476 // Otherwise, output the new message.
477 for (int i = 0; i < length; ++i)
478 ulog_out_pin->drive (buffer[i]);
481 #if SID_LOG_TRANSIENT_MALLOC_BUFFER
487 bool check_level (sid::host_int_4 level)
489 if (level > ulog_level)
492 if (level != ulog_level
493 && (ulog_mode == "match" || ulog_mode == "equal"))
500 void output_saved_messages ()
502 while (saved_messages.size () > 0)
504 if (check_level (saved_levels[0]))
506 std::string s = saved_messages[0];
507 for (int i = 0; i < s.size (); ++i)
508 ulog_out_pin->drive (s[i]);
510 saved_messages.erase (saved_messages.begin ());
511 saved_levels.erase (saved_levels.begin ());
516 sid::host_int_4 ulog_level;
517 std::string ulog_mode;
518 sidutil::output_pin *ulog_out_pin;
522 std::vector<std::string> saved_messages;
523 std::vector<sid::host_int_4> saved_levels;
524 #undef SID_LOG_PERSISTENT_BUFFER
525 #undef SID_LOG_TRANSIENT_MALLOC_BUFFER
527 void delete_saved_messages ()
529 saved_messages.clear ();
530 saved_levels.clear ();
534 // This class is intended for the implementation of change logging and
535 // change reversing. It is optimized for a potentially large number of
536 // elements and for growth, shrinkage and access as a LIFO stack. It is
537 // also optimized for growing again after shrinking.
541 change_log (sid::host_int_4 g = 0x100000) :
554 // Begin a new record and add the given data.
555 void push (const void *data, sid::host_int_4 length)
561 // Add the given data to the current record.
562 void add (const void *data, sid::host_int_4 length)
564 if (buf_index + length > buf_size)
566 buf_size += growth_rate * length;
567 unsigned char* new_buf = new unsigned char[buf_size];
570 memcpy (new_buf, buffer, buf_index);
576 memcpy (buffer + buf_index, data, length);
578 current_length += length;
581 // Complete the current record by writing its length.
584 unsigned char l = current_length;
588 // Remove the last record.
591 unsigned len = buffer[--buf_index];
595 // Return a pointer to the last record.
596 const void *top (sid::host_int_4 &length) const
598 length = buffer[buf_index - 1];
599 return buffer + buf_index - length - 1;
602 // Is the change log emtpy?
603 bool empty () const { return buf_index <= 0; }
606 sid::host_int_4 growth_rate;
607 unsigned char* buffer;
608 sid::host_int_4 buf_index;
609 sid::host_int_4 buf_size;
610 unsigned char current_length;
614 #endif // SIDMISCUTIL_H