1/* Copyright 2016 Software Freedom Conservancy Inc. 2 * 3 * This software is licensed under the GNU LGPL (version 2.1 or later). 4 * See the COPYING file in this distribution. 5 */ 6 7public uint int64_hash(int64? n) { 8 // Rotating XOR hash 9 uint8 *u8 = (uint8 *) n; 10 uint hash = 0; 11 for (int ctr = 0; ctr < (sizeof(int64) / sizeof(uint8)); ctr++) { 12 hash = (hash << 4) ^ (hash >> 28) ^ (*u8++); 13 } 14 15 return hash; 16} 17 18public bool int64_equal(int64? a, int64? b) { 19 int64 *bia = (int64 *) a; 20 int64 *bib = (int64 *) b; 21 22 return (*bia) == (*bib); 23} 24 25public int int64_compare(int64? a, int64? b) { 26 int64 diff = *((int64 *) a) - *((int64 *) b); 27 if (diff < 0) 28 return -1; 29 else if (diff > 0) 30 return 1; 31 else 32 return 0; 33} 34 35public int uint64_compare(uint64? a, uint64? b) { 36 uint64 a64 = *((uint64 *) a); 37 uint64 b64 = *((uint64 *) b); 38 39 if (a64 < b64) 40 return -1; 41 else if (a64 > b64) 42 return 1; 43 else 44 return 0; 45} 46 47public delegate bool ValueEqualFunc(Value a, Value b); 48 49public bool bool_value_equals(Value a, Value b) { 50 return (bool) a == (bool) b; 51} 52 53public bool int_value_equals(Value a, Value b) { 54 return (int) a == (int) b; 55} 56 57public ulong timeval_to_ms(TimeVal time_val) { 58 return (((ulong) time_val.tv_sec) * 1000) + (((ulong) time_val.tv_usec) / 1000); 59} 60 61public ulong now_ms() { 62 return timeval_to_ms(TimeVal()); 63} 64 65public ulong now_sec() { 66 TimeVal time_val = TimeVal(); 67 68 return time_val.tv_sec; 69} 70 71public inline time_t now_time_t() { 72 return (time_t) now_sec(); 73} 74 75public string md5_file(File file) throws Error { 76 Checksum md5 = new Checksum(ChecksumType.MD5); 77 uint8[] buffer = new uint8[64 * 1024]; 78 79 FileInputStream fins = file.read(null); 80 for (;;) { 81 size_t bytes_read = fins.read(buffer, null); 82 if (bytes_read <= 0) 83 break; 84 85 md5.update((uchar[]) buffer, bytes_read); 86 } 87 88 try { 89 fins.close(null); 90 } catch (Error err) { 91 warning("Unable to close MD5 input stream for %s: %s", file.get_path(), err.message); 92 } 93 94 return md5.get_string(); 95} 96 97// Once generic functions are available in Vala, this could be genericized. 98public bool equal_sets(Gee.Set<string>? a, Gee.Set<string>? b) { 99 if ((a != null && a.size == 0) && (b == null)) 100 return true; 101 102 if ((a == null) && (b != null && b.size == 0)) 103 return true; 104 105 if ((a == null && b != null) || (a != null && b == null)) 106 return false; 107 108 if (a == null && b == null) 109 return true; 110 111 if (a.size != b.size) 112 return false; 113 114 // because they're sets and the same size, only need to iterate over one set to know 115 // it is equal to the other 116 foreach (string element in a) { 117 if (!b.contains(element)) 118 return false; 119 } 120 121 return true; 122} 123 124// Once generic functions are available in Vala, this could be genericized. 125public Gee.Set<string>? intersection_of_sets(Gee.Set<string>? a, Gee.Set<string>? b, 126 Gee.Set<string>? excluded) { 127 if (a != null && b == null) { 128 if (excluded != null) 129 excluded.add_all(a); 130 131 return null; 132 } 133 134 if (a == null && b != null) { 135 if (excluded != null) 136 excluded.add_all(b); 137 138 return null; 139 } 140 141 Gee.Set<string> intersection = new Gee.HashSet<string>(); 142 143 foreach (string element in a) { 144 if (b.contains(element)) 145 intersection.add(element); 146 else if (excluded != null) 147 excluded.add(element); 148 } 149 150 foreach (string element in b) { 151 if (a.contains(element)) 152 intersection.add(element); 153 else if (excluded != null) 154 excluded.add(element); 155 } 156 157 return intersection.size > 0 ? intersection : null; 158} 159 160public uchar[] serialize_photo_ids(Gee.Collection<Photo> photos) { 161 int64[] ids = new int64[photos.size]; 162 int ctr = 0; 163 foreach (Photo photo in photos) 164 ids[ctr++] = photo.get_photo_id().id; 165 166 size_t bytes = photos.size * sizeof(int64); 167 uchar[] serialized = new uchar[bytes]; 168 Memory.copy(serialized, ids, bytes); 169 170 return serialized; 171} 172 173public Gee.List<PhotoID?>? unserialize_photo_ids(uchar* serialized, int size) { 174 size_t count = (size / sizeof(int64)); 175 if (count <= 0 || serialized == null) 176 return null; 177 178 int64[] ids = new int64[count]; 179 Memory.copy(ids, serialized, size); 180 181 Gee.ArrayList<PhotoID?> list = new Gee.ArrayList<PhotoID?>(); 182 foreach (int64 id in ids) 183 list.add(PhotoID(id)); 184 185 return list; 186} 187 188public uchar[] serialize_media_sources(Gee.Collection<MediaSource> media) { 189 Gdk.Atom[] atoms = new Gdk.Atom[media.size]; 190 int ctr = 0; 191 foreach (MediaSource current_media in media) 192 atoms[ctr++] = Gdk.Atom.intern(current_media.get_source_id(), false); 193 194 size_t bytes = media.size * sizeof(Gdk.Atom); 195 uchar[] serialized = new uchar[bytes]; 196 Memory.copy(serialized, atoms, bytes); 197 198 return serialized; 199} 200 201public Gee.List<MediaSource>? unserialize_media_sources(uchar* serialized, int size) { 202 size_t count = (size / sizeof(Gdk.Atom)); 203 if (count <= 0 || serialized == null) 204 return null; 205 206 Gdk.Atom[] atoms = new Gdk.Atom[count]; 207 Memory.copy(atoms, serialized, size); 208 209 Gee.ArrayList<MediaSource> list = new Gee.ArrayList<MediaSource>(); 210 foreach (Gdk.Atom current_atom in atoms) { 211 MediaSource media = MediaCollectionRegistry.get_instance().fetch_media(current_atom.name()); 212 assert(media != null); 213 list.add(media); 214 } 215 216 return list; 217} 218 219public string format_local_datespan(Time from_date, Time to_date) { 220 string from_format, to_format; 221 222 // Ticket #3240 - Change the way date ranges are pretty- 223 // printed if the start and end date occur on consecutive days. 224 if (from_date.year == to_date.year) { 225 // are these consecutive dates? 226 if ((from_date.month == to_date.month) && (from_date.day == (to_date.day - 1))) { 227 // Yes; display like so: Sat, July 4 - 5, 20X6 228 from_format = Resources.get_start_multiday_span_format_string(); 229 to_format = Resources.get_end_multiday_span_format_string(); 230 } else { 231 // No, but they're in the same year; display in shortened 232 // form: Sat, July 4 - Mon, July 6, 20X6 233 from_format = Resources.get_start_multimonth_span_format_string(); 234 to_format = Resources.get_end_multimonth_span_format_string(); 235 } 236 } else { 237 // Span crosses a year boundary, use long form dates 238 // for both start and end date. 239 from_format = Resources.get_long_date_format_string(); 240 to_format = Resources.get_long_date_format_string(); 241 } 242 243 return String.strip_leading_zeroes("%s - %s".printf(from_date.format(from_format), 244 to_date.format(to_format))); 245} 246 247public string format_local_date(Time date) { 248 return String.strip_leading_zeroes(date.format(Resources.get_long_date_format_string())); 249} 250 251public delegate void OneShotCallback(); 252 253public class OneShotScheduler { 254 private string name; 255 private unowned OneShotCallback callback; 256 private uint scheduled = 0; 257 258 public OneShotScheduler(string name, OneShotCallback callback) { 259 this.name = name; 260 this.callback = callback; 261 } 262 263 ~OneShotScheduler() { 264#if TRACE_DTORS 265 debug("DTOR: OneShotScheduler for %s", name); 266#endif 267 268 cancel(); 269 } 270 271 public bool is_scheduled() { 272 return scheduled != 0; 273 } 274 275 public void at_idle() { 276 at_priority_idle(Priority.DEFAULT_IDLE); 277 } 278 279 public void at_priority_idle(int priority) { 280 if (scheduled == 0) 281 scheduled = Idle.add_full(priority, callback_wrapper); 282 } 283 284 public void after_timeout(uint msec, bool reschedule) { 285 priority_after_timeout(Priority.DEFAULT, msec, reschedule); 286 } 287 288 public void priority_after_timeout(int priority, uint msec, bool reschedule) { 289 if (scheduled != 0 && !reschedule) 290 return; 291 292 if (scheduled != 0) 293 Source.remove(scheduled); 294 295 scheduled = Timeout.add_full(priority, msec, callback_wrapper); 296 } 297 298 public void cancel() { 299 if (scheduled == 0) 300 return; 301 302 Source.remove(scheduled); 303 scheduled = 0; 304 } 305 306 private bool callback_wrapper() { 307 scheduled = 0; 308 callback(); 309 310 return false; 311 } 312} 313 314public class OpTimer { 315 private string name; 316 private Timer timer = new Timer(); 317 private long count = 0; 318 private double elapsed = 0; 319 private double shortest = double.MAX; 320 private double longest = double.MIN; 321 322 public OpTimer(string name) { 323 this.name = name; 324 } 325 326 public void start() { 327 timer.start(); 328 } 329 330 public void stop() { 331 double time = timer.elapsed(); 332 333 elapsed += time; 334 335 if (time < shortest) 336 shortest = time; 337 338 if (time > longest) 339 longest = time; 340 341 count++; 342 } 343 344 public string to_string() { 345 if (count > 0) { 346 return "%s: count=%ld elapsed=%.03lfs min/avg/max=%.03lf/%.03lf/%.03lf".printf(name, 347 count, elapsed, shortest, elapsed / (double) count, longest); 348 } else { 349 return "%s: no operations".printf(name); 350 } 351 } 352} 353 354