diff options
Diffstat (limited to 'src/util/misc.vala')
-rw-r--r-- | src/util/misc.vala | 377 |
1 files changed, 377 insertions, 0 deletions
diff --git a/src/util/misc.vala b/src/util/misc.vala new file mode 100644 index 0000000..73ce428 --- /dev/null +++ b/src/util/misc.vala @@ -0,0 +1,377 @@ +/* Copyright 2009-2014 Yorba Foundation + * + * This software is licensed under the GNU LGPL (version 2.1 or later). + * See the COPYING file in this distribution. + */ + +public uint int64_hash(int64? n) { + // Rotating XOR hash + uint8 *u8 = (uint8 *) n; + uint hash = 0; + for (int ctr = 0; ctr < (sizeof(int64) / sizeof(uint8)); ctr++) { + hash = (hash << 4) ^ (hash >> 28) ^ (*u8++); + } + + return hash; +} + +public bool int64_equal(int64? a, int64? b) { + int64 *bia = (int64 *) a; + int64 *bib = (int64 *) b; + + return (*bia) == (*bib); +} + +public int int64_compare(int64? a, int64? b) { + int64 diff = *((int64 *) a) - *((int64 *) b); + if (diff < 0) + return -1; + else if (diff > 0) + return 1; + else + return 0; +} + +public int uint64_compare(uint64? a, uint64? b) { + uint64 a64 = *((uint64 *) a); + uint64 b64 = *((uint64 *) b); + + if (a64 < b64) + return -1; + else if (a64 > b64) + return 1; + else + return 0; +} + +public delegate bool ValueEqualFunc(Value a, Value b); + +public bool bool_value_equals(Value a, Value b) { + return (bool) a == (bool) b; +} + +public bool int_value_equals(Value a, Value b) { + return (int) a == (int) b; +} + +public ulong timeval_to_ms(TimeVal time_val) { + return (((ulong) time_val.tv_sec) * 1000) + (((ulong) time_val.tv_usec) / 1000); +} + +public ulong now_ms() { + return timeval_to_ms(TimeVal()); +} + +public ulong now_sec() { + TimeVal time_val = TimeVal(); + + return time_val.tv_sec; +} + +public inline time_t now_time_t() { + return (time_t) now_sec(); +} + +public string md5_binary(uint8 *buffer, size_t length) { + assert(length != 0); + + Checksum md5 = new Checksum(ChecksumType.MD5); + md5.update((uchar []) buffer, length); + + return md5.get_string(); +} + +public string md5_file(File file) throws Error { + Checksum md5 = new Checksum(ChecksumType.MD5); + uint8[] buffer = new uint8[64 * 1024]; + + FileInputStream fins = file.read(null); + for (;;) { + size_t bytes_read = fins.read(buffer, null); + if (bytes_read <= 0) + break; + + md5.update((uchar[]) buffer, bytes_read); + } + + try { + fins.close(null); + } catch (Error err) { + warning("Unable to close MD5 input stream for %s: %s", file.get_path(), err.message); + } + + return md5.get_string(); +} + +// Once generic functions are available in Vala, this could be genericized. +public bool equal_sets(Gee.Set<string>? a, Gee.Set<string>? b) { + if ((a != null && a.size == 0) && (b == null)) + return true; + + if ((a == null) && (b != null && b.size == 0)) + return true; + + if ((a == null && b != null) || (a != null && b == null)) + return false; + + if (a == null && b == null) + return true; + + if (a.size != b.size) + return false; + + // because they're sets and the same size, only need to iterate over one set to know + // it is equal to the other + foreach (string element in a) { + if (!b.contains(element)) + return false; + } + + return true; +} + +// Once generic functions are available in Vala, this could be genericized. +public Gee.Set<string>? intersection_of_sets(Gee.Set<string>? a, Gee.Set<string>? b, + Gee.Set<string>? excluded) { + if (a != null && b == null) { + if (excluded != null) + excluded.add_all(a); + + return null; + } + + if (a == null && b != null) { + if (excluded != null) + excluded.add_all(b); + + return null; + } + + Gee.Set<string> intersection = new Gee.HashSet<string>(); + + foreach (string element in a) { + if (b.contains(element)) + intersection.add(element); + else if (excluded != null) + excluded.add(element); + } + + foreach (string element in b) { + if (a.contains(element)) + intersection.add(element); + else if (excluded != null) + excluded.add(element); + } + + return intersection.size > 0 ? intersection : null; +} + +public uchar[] serialize_photo_ids(Gee.Collection<Photo> photos) { + int64[] ids = new int64[photos.size]; + int ctr = 0; + foreach (Photo photo in photos) + ids[ctr++] = photo.get_photo_id().id; + + size_t bytes = photos.size * sizeof(int64); + uchar[] serialized = new uchar[bytes]; + Memory.copy(serialized, ids, bytes); + + return serialized; +} + +public Gee.List<PhotoID?>? unserialize_photo_ids(uchar* serialized, int size) { + size_t count = (size / sizeof(int64)); + if (count <= 0 || serialized == null) + return null; + + int64[] ids = new int64[count]; + Memory.copy(ids, serialized, size); + + Gee.ArrayList<PhotoID?> list = new Gee.ArrayList<PhotoID?>(); + foreach (int64 id in ids) + list.add(PhotoID(id)); + + return list; +} + +public uchar[] serialize_media_sources(Gee.Collection<MediaSource> media) { + Gdk.Atom[] atoms = new Gdk.Atom[media.size]; + int ctr = 0; + foreach (MediaSource current_media in media) + atoms[ctr++] = Gdk.Atom.intern(current_media.get_source_id(), false); + + size_t bytes = media.size * sizeof(Gdk.Atom); + uchar[] serialized = new uchar[bytes]; + Memory.copy(serialized, atoms, bytes); + + return serialized; +} + +public Gee.List<MediaSource>? unserialize_media_sources(uchar* serialized, int size) { + size_t count = (size / sizeof(Gdk.Atom)); + if (count <= 0 || serialized == null) + return null; + + Gdk.Atom[] atoms = new Gdk.Atom[count]; + Memory.copy(atoms, serialized, size); + + Gee.ArrayList<MediaSource> list = new Gee.ArrayList<MediaSource>(); + foreach (Gdk.Atom current_atom in atoms) { + MediaSource media = MediaCollectionRegistry.get_instance().fetch_media(current_atom.name()); + assert(media != null); + list.add(media); + } + + return list; +} + +public string format_local_datespan(Time from_date, Time to_date) { + string from_format, to_format; + + // Ticket #3240 - Change the way date ranges are pretty- + // printed if the start and end date occur on consecutive days. + if (from_date.year == to_date.year) { + // are these consecutive dates? + if ((from_date.month == to_date.month) && (from_date.day == (to_date.day - 1))) { + // Yes; display like so: Sat, July 4 - 5, 20X6 + from_format = Resources.get_start_multiday_span_format_string(); + to_format = Resources.get_end_multiday_span_format_string(); + } else { + // No, but they're in the same year; display in shortened + // form: Sat, July 4 - Mon, July 6, 20X6 + from_format = Resources.get_start_multimonth_span_format_string(); + to_format = Resources.get_end_multimonth_span_format_string(); + } + } else { + // Span crosses a year boundary, use long form dates + // for both start and end date. + from_format = Resources.get_long_date_format_string(); + to_format = Resources.get_long_date_format_string(); + } + + return String.strip_leading_zeroes("%s - %s".printf(from_date.format(from_format), + to_date.format(to_format))); +} + +public string format_local_date(Time date) { + return String.strip_leading_zeroes(date.format(Resources.get_long_date_format_string())); +} + +public delegate void OneShotCallback(); + +public class OneShotScheduler { + private string name; + private unowned OneShotCallback callback; + private uint scheduled = 0; + + public OneShotScheduler(string name, OneShotCallback callback) { + this.name = name; + this.callback = callback; + } + + ~OneShotScheduler() { +#if TRACE_DTORS + debug("DTOR: OneShotScheduler for %s", name); +#endif + + cancel(); + } + + public bool is_scheduled() { + return scheduled != 0; + } + + public void at_idle() { + at_priority_idle(Priority.DEFAULT_IDLE); + } + + public void at_priority_idle(int priority) { + if (scheduled == 0) + scheduled = Idle.add_full(priority, callback_wrapper); + } + + public void after_timeout(uint msec, bool reschedule) { + priority_after_timeout(Priority.DEFAULT, msec, reschedule); + } + + public void priority_after_timeout(int priority, uint msec, bool reschedule) { + if (scheduled != 0 && !reschedule) + return; + + if (scheduled != 0) + Source.remove(scheduled); + + scheduled = Timeout.add_full(priority, msec, callback_wrapper); + } + + public void cancel() { + if (scheduled == 0) + return; + + Source.remove(scheduled); + scheduled = 0; + } + + private bool callback_wrapper() { + scheduled = 0; + callback(); + + return false; + } +} + +public class OpTimer { + private string name; + private Timer timer = new Timer(); + private long count = 0; + private double elapsed = 0; + private double shortest = double.MAX; + private double longest = double.MIN; + + public OpTimer(string name) { + this.name = name; + } + + public void start() { + timer.start(); + } + + public void stop() { + double time = timer.elapsed(); + + elapsed += time; + + if (time < shortest) + shortest = time; + + if (time > longest) + longest = time; + + count++; + } + + public string to_string() { + if (count > 0) { + return "%s: count=%ld elapsed=%.03lfs min/avg/max=%.03lf/%.03lf/%.03lf".printf(name, + count, elapsed, shortest, elapsed / (double) count, longest); + } else { + return "%s: no operations".printf(name); + } + } +} + +// Dummy function for suppressing 'could not stat file' errors +// generated when saving into a previously non-existent file - +// please see https://bugzilla.gnome.org/show_bug.cgi?id=662814 +// and to work around a spurious warning given by GDK when a +// key press event is passed from a child class' event handler +// to a parent's; (gnome bug pending, but see https://bugzilla.redhat.com/show_bug.cgi?id=665568). +public void suppress_warnings(string? log_domain, LogLevelFlags log_levels, string message) { + // do nothing. +} + +public bool is_twentyfour_hr_time_system() { + // if no AM/PM designation is found, the location is set to use a 24 hr time system + return is_string_empty(Time.local(0).format("%p")); +} + |