[gnome-settings-daemon/wip/chergert/mem-reduce: 1/3] datetime: reduce runtime memory usage



commit bc7a2ae472087ccac7934a387f76388e9a526b01
Author: Christian Hergert <chergert redhat com>
Date:   Tue Mar 10 09:44:12 2020 -0700

    datetime: reduce runtime memory usage
    
    Just over 1 MB of runtime data was keeping track of allocations for
    TzLocation and containing GList elements. We can make much of that all go
    away if we use intern'd strings and arrays to contain TzLocation data.
    
    A much bigger amount of allocations are held by libgweather which is
    storing rougly 8MB of Locations.xml data in memory. A follow up commit
    will try to address that more directly.

 plugins/datetime/gsd-timezone-monitor.c |  92 ++-----
 plugins/datetime/tz.c                   | 446 +++++++++++++++-----------------
 plugins/datetime/tz.h                   |  48 ++--
 plugins/datetime/weather-tz.c           | 126 ++++-----
 plugins/datetime/weather-tz.h           |   8 +-
 5 files changed, 309 insertions(+), 411 deletions(-)
---
diff --git a/plugins/datetime/gsd-timezone-monitor.c b/plugins/datetime/gsd-timezone-monitor.c
index ca3e6a7f..fa7b5cef 100644
--- a/plugins/datetime/gsd-timezone-monitor.c
+++ b/plugins/datetime/gsd-timezone-monitor.c
@@ -104,8 +104,8 @@ queue_set_timezone (GsdTimezoneMonitor *self,
 }
 
 static gint
-compare_locations (TzLocation *a,
-                   TzLocation *b)
+compare_locations (const TzLocation *a,
+                   const TzLocation *b)
 {
         if (a->dist > b->dist)
                 return 1;
@@ -116,59 +116,21 @@ compare_locations (TzLocation *a,
         return 0;
 }
 
-static GList *
-sort_by_closest_to (GList           *locations,
+static void
+sort_by_closest_to (GArray          *locations,
                     GeocodeLocation *location)
 {
-        GList *l;
-
-        for (l = locations; l; l = l->next) {
-                GeocodeLocation *loc;
-                TzLocation *tz_location = l->data;
+        for (guint i = 0; i < locations->len; i++) {
+                TzLocation *tzloc = &g_array_index (locations, TzLocation, i);
+                g_autoptr(GeocodeLocation) loc = NULL;
 
-                loc = geocode_location_new (tz_location->latitude,
-                                            tz_location->longitude,
+                loc = geocode_location_new (tzloc->latitude,
+                                            tzloc->longitude,
                                             GEOCODE_LOCATION_ACCURACY_UNKNOWN);
-                tz_location->dist = geocode_location_get_distance_from (loc, location);
-                g_object_unref (loc);
+                tzloc->dist = geocode_location_get_distance_from (loc, location);
         }
 
-        return g_list_sort (locations, (GCompareFunc) compare_locations);
-}
-
-static GList *
-ptr_array_to_list (GPtrArray *array)
-{
-        GList *l = NULL;
-        gint i;
-
-        for (i = 0; i < array->len; i++)
-                l = g_list_prepend (l, g_ptr_array_index (array, i));
-
-        return l;
-}
-
-static GList *
-find_by_country (GList       *locations,
-                 const gchar *country_code)
-{
-        GList *l, *found = NULL;
-        gchar *c1;
-        gchar *c2;
-
-        c1 = g_ascii_strdown (country_code, -1);
-
-        for (l = locations; l; l = l->next) {
-                TzLocation *loc = l->data;
-
-                c2 = g_ascii_strdown (loc->country, -1);
-                if (g_strcmp0 (c1, c2) == 0)
-                        found = g_list_prepend (found, loc);
-                g_free (c2);
-        }
-        g_free (c1);
-
-        return found;
+        g_array_sort (locations, (GCompareFunc) compare_locations);
 }
 
 static const gchar *
@@ -176,35 +138,29 @@ find_timezone (GsdTimezoneMonitor *self,
                GeocodeLocation    *location,
                const gchar        *country_code)
 {
-        GList *filtered;
-        GList *locations;
         GsdTimezoneMonitorPrivate *priv = gsd_timezone_monitor_get_instance_private (self);
-        TzLocation *closest_tz_location;
+        g_autoptr(GArray) locations = g_array_new (FALSE, FALSE, sizeof (TzLocation));
+        TzLocation closest_tz_location;
 
         /* First load locations from Olson DB */
-        locations = ptr_array_to_list (tz_get_locations (priv->tzdb));
-        g_return_val_if_fail (locations != NULL, NULL);
+        tz_populate_locations (priv->tzdb, locations, country_code);
 
         /* ... and then add libgweather's locations as well */
-        locations = g_list_concat (locations,
-                                   weather_tz_db_get_locations (priv->weather_tzdb));
-
-        /* Filter tz locations by country */
-        filtered = find_by_country (locations, country_code);
-        if (filtered != NULL) {
-                g_list_free (locations);
-                locations = filtered;
-        } else {
+        weather_tz_db_populate_locations (priv->weather_tzdb, locations, country_code);
+
+        if (locations->len == 0) {
                 g_debug ("No match for country code '%s' in tzdb", country_code);
+
+               /* Populate with all of the items instead of by country */
+               tz_populate_locations (priv->tzdb, locations, NULL);
+               weather_tz_db_populate_locations (priv->weather_tzdb, locations, NULL);
         }
 
         /* Find the closest tz location */
-        locations = sort_by_closest_to (locations, location);
-        closest_tz_location = (TzLocation *) locations->data;
-
-        g_list_free (locations);
+        sort_by_closest_to (locations, location);
+        closest_tz_location = g_array_index (locations, TzLocation, 0);
 
-        return closest_tz_location->zone;
+        return closest_tz_location.zone;
 }
 
 static void
diff --git a/plugins/datetime/tz.c b/plugins/datetime/tz.c
index 034d63d6..751cc695 100644
--- a/plugins/datetime/tz.c
+++ b/plugins/datetime/tz.c
@@ -2,6 +2,7 @@
 /* Generic timezone utilities.
  *
  * Copyright (C) 2000-2001 Ximian, Inc.
+ * Copyright (C) 2020 Christian Hergert <chergert redhat com>
  *
  * Authors: Hans Petter Jansson <hpj ximian com>
  * 
@@ -21,7 +22,11 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+#endif
 
+#include <errno.h>
 #include <glib.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -30,100 +35,48 @@
 #include <math.h>
 #include <string.h>
 #include <ctype.h>
-#include "tz.h"
 
+#include "tz.h"
 
-/* Forward declarations for private functions */
-
-static float convert_pos (gchar *pos, int digits);
-static int compare_country_names (const void *a, const void *b);
-static void sort_locations_by_country (GPtrArray *locations);
-static gchar * tz_data_file_get (void);
-static void load_backward_tz (TzDB *tz_db);
+static float    convert_pos               (gchar       *pos,
+                                           int          digits);
+static int      compare_country_names     (const void  *a,
+                                           const void  *b);
+static void     sort_locations_by_country (GArray      *locations);
+static void     load_backward_tz          (TzDB        *tz_db);
+static gboolean tz_location_parse         (TzLocation  *loc,
+                                           char        *line);
+static gboolean parse_word                (char        *line,
+                                           char       **save,
+                                           const char **strp);
 
-/* ---------------- *
- * Public interface *
- * ---------------- */
 TzDB *
 tz_load_db (void)
 {
-       gchar *tz_data_file;
        TzDB *tz_db;
        FILE *tzfile;
        char buf[4096];
 
-       tz_data_file = tz_data_file_get ();
-       if (!tz_data_file) {
-               g_warning ("Could not get the TimeZone data file name");
-               return NULL;
-       }
-       tzfile = fopen (tz_data_file, "r");
-       if (!tzfile) {
-               g_warning ("Could not open *%s*\n", tz_data_file);
-               g_free (tz_data_file);
+       if (!(tzfile = fopen (TZ_DATA_FILE, "r"))) {
+               g_warning ("Could not open %s: %s",
+                          TZ_DATA_FILE, g_strerror (errno));
                return NULL;
        }
 
        tz_db = g_new0 (TzDB, 1);
-       tz_db->locations = g_ptr_array_new ();
-
-       while (fgets (buf, sizeof(buf), tzfile))
-       {
-               gchar **tmpstrarr;
-               gchar *latstr, *lngstr, *p;
-               TzLocation *loc;
-
-               if (*buf == '#') continue;
-
-               g_strchomp(buf);
-               tmpstrarr = g_strsplit(buf,"\t", 6);
-               
-               latstr = g_strdup (tmpstrarr[1]);
-               p = latstr + 1;
-               while (*p != '-' && *p != '+') p++;
-               lngstr = g_strdup (p);
-               *p = '\0';
-               
-               loc = g_new0 (TzLocation, 1);
-               loc->country = g_strdup (tmpstrarr[0]);
-               loc->zone = g_strdup (tmpstrarr[2]);
-               loc->latitude  = convert_pos (latstr, 2);
-               loc->longitude = convert_pos (lngstr, 3);
-               
-#ifdef __sun
-               if (tmpstrarr[3] && *tmpstrarr[3] == '-' && tmpstrarr[4])
-                       loc->comment = g_strdup (tmpstrarr[4]);
-
-               if (tmpstrarr[3] && *tmpstrarr[3] != '-' && !islower(loc->zone)) {
-                       TzLocation *locgrp;
-
-                       /* duplicate entry */
-                       locgrp = g_new0 (TzLocation, 1);
-                       locgrp->country = g_strdup (tmpstrarr[0]);
-                       locgrp->zone = g_strdup (tmpstrarr[3]);
-                       locgrp->latitude  = convert_pos (latstr, 2);
-                       locgrp->longitude = convert_pos (lngstr, 3);
-                       locgrp->comment = (tmpstrarr[4]) ? g_strdup (tmpstrarr[4]) : NULL;
-
-                       g_ptr_array_add (tz_db->locations, (gpointer) locgrp);
-               }
-#else
-               loc->comment = (tmpstrarr[3]) ? g_strdup(tmpstrarr[3]) : NULL;
-#endif
+       tz_db->locations = g_array_new (FALSE, FALSE, sizeof (TzLocation));
 
-               g_ptr_array_add (tz_db->locations, (gpointer) loc);
+       while (fgets (buf, sizeof buf, tzfile)) {
+               TzLocation loc;
 
-               g_free (latstr);
-               g_free (lngstr);
-               g_strfreev (tmpstrarr);
+                if (tz_location_parse (&loc, buf))
+                        g_array_append_val (tz_db->locations, loc);
        }
-       
+
        fclose (tzfile);
        
        /* now sort by country */
        sort_locations_by_country (tz_db->locations);
-       
-       g_free (tz_data_file);
 
        /* Load up the hashtable of backward links */
        load_backward_tz (tz_db);
@@ -131,60 +84,14 @@ tz_load_db (void)
        return tz_db;
 }
 
-void
-tz_location_free (TzLocation *loc)
-{
-       g_free (loc->country);
-       g_free (loc->zone);
-       g_free (loc->comment);
-
-       g_free (loc);
-}
-
 void
 tz_db_free (TzDB *db)
 {
-       g_ptr_array_foreach (db->locations, (GFunc) tz_location_free, NULL);
-       g_ptr_array_free (db->locations, TRUE);
+       g_array_unref (db->locations);
        g_hash_table_destroy (db->backward);
        g_free (db);
 }
 
-GPtrArray *
-tz_get_locations (TzDB *db)
-{
-       return db->locations;
-}
-
-
-gchar *
-tz_location_get_country (TzLocation *loc)
-{
-       return loc->country;
-}
-
-
-gchar *
-tz_location_get_zone (TzLocation *loc)
-{
-       return loc->zone;
-}
-
-
-gchar *
-tz_location_get_comment (TzLocation *loc)
-{
-       return loc->comment;
-}
-
-
-void
-tz_location_get_position (TzLocation *loc, double *longitude, double *latitude)
-{
-       *longitude = loc->longitude;
-       *latitude = loc->latitude;
-}
-
 glong
 tz_location_get_utc_offset (TzLocation *loc)
 {
@@ -204,16 +111,13 @@ tz_info_from_location (TzLocation *loc)
        time_t curtime;
        struct tm *curzone;
        gchar *tz_env_value;
-       
+
        g_return_val_if_fail (loc != NULL, NULL);
        g_return_val_if_fail (loc->zone != NULL, NULL);
-       
+
        tz_env_value = g_strdup (getenv ("TZ"));
-       setenv ("TZ", loc->zone, 1);
-       
-#if 0
-       tzset ();
-#endif
+       g_setenv ("TZ", loc->zone, 1);
+
        tzinfo = g_new0 (TzInfo, 1);
 
        curtime = time (NULL);
@@ -222,10 +126,10 @@ tz_info_from_location (TzLocation *loc)
 #ifndef __sun
        /* Currently this solution doesnt seem to work - I get that */
        /* America/Phoenix uses daylight savings, which is wrong    */
-       tzinfo->tzname_normal = g_strdup (curzone->tm_zone);
+       tzinfo->tzname_normal = tz_intern (curzone->tm_zone);
        if (curzone->tm_isdst) 
                tzinfo->tzname_daylight =
-                       g_strdup (&curzone->tm_zone[curzone->tm_isdst]);
+                       tz_intern (&curzone->tm_zone[curzone->tm_isdst]);
        else
                tzinfo->tzname_daylight = NULL;
 
@@ -239,9 +143,9 @@ tz_info_from_location (TzLocation *loc)
        tzinfo->daylight = curzone->tm_isdst;
 
        if (tz_env_value)
-               setenv ("TZ", tz_env_value, 1);
+               g_setenv ("TZ", tz_env_value, 1);
        else
-               unsetenv ("TZ");
+               g_unsetenv ("TZ");
 
        g_free (tz_env_value);
        
@@ -252,10 +156,6 @@ tz_info_from_location (TzLocation *loc)
 void
 tz_info_free (TzInfo *tzinfo)
 {
-       g_return_if_fail (tzinfo != NULL);
-       
-       if (tzinfo->tzname_normal) g_free (tzinfo->tzname_normal);
-       if (tzinfo->tzname_daylight) g_free (tzinfo->tzname_daylight);
        g_free (tzinfo);
 }
 
@@ -293,21 +193,18 @@ compare_timezones (const char *a,
 {
        if (g_str_equal (a, b))
                return TRUE;
+
        if (strchr (b, '/') == NULL) {
-               char *prefixed;
+               g_autofree char *prefixed = g_strdup_printf ("/%s", b);
 
-               prefixed = g_strdup_printf ("/%s", b);
-               if (g_str_has_suffix (a, prefixed)) {
-                       g_free (prefixed);
+               if (g_str_has_suffix (a, prefixed))
                        return TRUE;
-               }
-               g_free (prefixed);
        }
 
        return FALSE;
 }
 
-char *
+const char *
 tz_info_get_clean_name (TzDB *tz_db,
                        const char *tz)
 {
@@ -347,23 +244,8 @@ tz_info_get_clean_name (TzDB *tz_db,
                timezone = tz;
 
        ret = g_hash_table_lookup (tz_db->backward, timezone);
-       if (ret == NULL)
-               return g_strdup (timezone);
-       return g_strdup (ret);
-}
-
-/* ----------------- *
- * Private functions *
- * ----------------- */
 
-static gchar *
-tz_data_file_get (void)
-{
-       gchar *file;
-
-       file = g_strdup (TZ_DATA_FILE);
-
-       return file;
+        return ret ? ret : timezone;
 }
 
 static float
@@ -373,9 +255,9 @@ convert_pos (gchar *pos, int digits)
        gchar *fraction;
        gint i;
        float t1, t2;
-       
+
        if (!pos || strlen(pos) < 4 || digits > 9) return 0.0;
-       
+
        for (i = 0; i < digits + 1; i++) whole[i] = pos[i];
        whole[i] = '\0';
        fraction = pos + digits + 1;
@@ -387,96 +269,188 @@ convert_pos (gchar *pos, int digits)
        else return t1 - t2/pow (10.0, strlen(fraction));
 }
 
+static int
+compare_country_names (gconstpointer a,
+                       gconstpointer b)
+{
+       const TzLocation *tza = a;
+       const TzLocation *tzb = b;
+
+       return strcmp (tza->zone, tzb->zone);
+}
 
-#if 0
 
-/* Currently not working */
 static void
-free_tzdata (TzLocation *tz)
+sort_locations_by_country (GArray *locations)
 {
-       
-       if (tz->country)
-         g_free(tz->country);
-       if (tz->zone)
-         g_free(tz->zone);
-       if (tz->comment)
-         g_free(tz->comment);
-       
-       g_free(tz);
+        g_array_sort (locations, compare_country_names);
 }
-#endif
 
+static void
+load_backward_tz (TzDB *tz_db)
+{
+       FILE *backward;
+       char buf[4096];
 
-static int
-compare_country_names (const void *a, const void *b)
+       g_assert (tz_db != NULL);
+       g_assert (tz_db->backward == NULL);
+
+       tz_db->backward = g_hash_table_new (g_str_hash, g_str_equal);
+
+       if (!(backward = fopen (GNOMECC_DATA_DIR "/datetime/backward", "r"))) {
+               g_warning ("Failed to load 'backward' file: %s",
+                          g_strerror (errno));
+               return;
+       }
+
+       while (fgets (buf, sizeof buf, backward)) {
+               const char *ignore;
+               const char *real;
+               const char *alias;
+               char *save = NULL;
+
+               /* Skip comments and empty lines */
+               if (g_ascii_strncasecmp (buf, "Link\t", 5) != 0)
+                       continue;
+
+               if (parse_word (buf, &save, &ignore) &&
+                   parse_word (NULL, &save, &real) &&
+                   parse_word (NULL, &save, &alias)) {
+                       /* We don't need more than one name for it */
+                       if (g_str_equal (real, "Etc/UTC") ||
+                           g_str_equal (real, "Etc/UCT"))
+                               real = "Etc/GMT";
+
+                       g_hash_table_insert (tz_db->backward,
+                                            (char *)tz_intern (alias),
+                                            (char *)tz_intern (real));
+               }
+       }
+
+       fclose (backward);
+}
+
+static gboolean
+parse_word (char        *line,
+            char       **save,
+            const char **strp)
 {
-       const TzLocation *tza = * (TzLocation **) a;
-       const TzLocation *tzb = * (TzLocation **) b;
-       
-       return strcmp (tza->zone, tzb->zone);
+       char *ret = strtok_r (line, "\t", save);
+
+       if (ret != NULL) {
+               *strp = tz_intern (ret);
+               return TRUE;
+       }
+
+       return FALSE;
 }
 
+static gboolean
+parse_lat_lng (char    *line,
+               char   **save,
+               gfloat  *lat,
+               gfloat  *lng)
+{
+       char *ret = strtok_r (line, "\t", save);
+       char *p;
+       char sign;
+
+       /* The value from zone.tab looks something like "+4230+00131" */
+       if (ret == NULL || (ret[0] != '-' && ret[0] != '+'))
+               return FALSE;
+
+       /* Advance p to longitude portion */
+       for (p = ret+1; *p && *p != '-' && *p != '+'; p++) { /* Do nothing */ }
+       sign = *p;
+       *p = 0;
+       *lat = convert_pos (ret, 2);
+       *p = sign;
+       *lng = convert_pos (p, 3);
+
+       return TRUE;
+}
 
-static void
-sort_locations_by_country (GPtrArray *locations)
+static gboolean
+tz_location_parse (TzLocation *loc,
+                   char       *line)
 {
-       qsort (locations->pdata, locations->len, sizeof (gpointer),
-              compare_country_names);
+       char *save = NULL;
+
+       g_assert (loc != NULL);
+       g_assert (line != NULL);
+
+       if (line[0] == '#')
+               return FALSE;
+
+       loc->dist = 0;
+
+       return parse_word (line, &save, &loc->country) &&
+              parse_lat_lng (NULL, &save, &loc->latitude, &loc->longitude) &&
+              parse_word (NULL, &save, &loc->zone);
 }
 
-static void
-load_backward_tz (TzDB *tz_db)
+static inline gboolean
+char_equal_no_case (char a,
+                    char b)
 {
-  GError *error = NULL;
-  char **lines, *contents;
-  guint i;
-
-  tz_db->backward = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
-
-  if (g_file_get_contents (GNOMECC_DATA_DIR "/datetime/backward", &contents, NULL, &error) == FALSE)
-    {
-      g_warning ("Failed to load 'backward' file: %s", error->message);
-      return;
-    }
-  lines = g_strsplit (contents, "\n", -1);
-  g_free (contents);
-  for (i = 0; lines[i] != NULL; i++)
-    {
-      char **items;
-      guint j;
-      char *real, *alias;
-
-      if (g_ascii_strncasecmp (lines[i], "Link\t", 5) != 0)
-        continue;
-
-      items = g_strsplit (lines[i], "\t", -1);
-      real = NULL;
-      alias = NULL;
-      /* Skip the "Link<tab>" part */
-      for (j = 1; items[j] != NULL; j++)
-        {
-          if (items[j][0] == '\0')
-            continue;
-          if (real == NULL)
-            {
-              real = items[j];
-              continue;
-            }
-          alias = items[j];
-          break;
-        }
-
-      if (real == NULL || alias == NULL)
-        g_warning ("Could not parse line: %s", lines[i]);
-
-      /* We don't need more than one name for it */
-      if (g_str_equal (real, "Etc/UTC") ||
-          g_str_equal (real, "Etc/UCT"))
-        real = "Etc/GMT";
-
-      g_hash_table_insert (tz_db->backward, g_strdup (alias), g_strdup (real));
-      g_strfreev (items);
-    }
-  g_strfreev (lines);
+       /* 32 (0100000) denotes upper vs lowercase in ASCII */
+       return (a & ~0100000) == (b & ~0100000);
+}
+
+gboolean
+tz_location_is_country_code (const TzLocation *loc,
+                             const char       *country_code)
+{
+       const char *z, *c;
+
+       g_assert (loc != NULL);
+       g_assert (country_code != NULL);
+
+       z = loc->zone;
+       c = country_code;
+
+       for (; *z && *c; z++, c++) {
+               if (!char_equal_no_case (*z, *c))
+                       return FALSE;
+       }
+
+       return *z == 0 && *c == 0;
 }
 
+void
+tz_populate_locations (TzDB       *db,
+                       GArray     *locations,
+                       const char *country_code)
+{
+       g_assert (db != NULL);
+       g_assert (locations != NULL);
+
+       if (country_code == NULL) {
+               if (db->locations->len > 0)
+                       g_array_append_vals (locations,
+                                            &g_array_index (db->locations, TzLocation, 0),
+                                            db->locations->len);
+               return;
+       }
+
+       for (guint i = 0; i < db->locations->len; i++) {
+               const TzLocation *loc = &g_array_index (db->locations, TzLocation, i);
+
+               if (tz_location_is_country_code (loc, country_code))
+                       g_array_append_vals (locations, loc, 1);
+       }
+}
+
+const char *
+tz_intern (const char *str)
+{
+       static GStringChunk *chunks;
+
+       if (str == NULL)
+               return NULL;
+
+       if (chunks == NULL)
+               chunks = g_string_chunk_new (4*4096);
+
+       return g_string_chunk_insert_const (chunks, str);
+}
diff --git a/plugins/datetime/tz.h b/plugins/datetime/tz.h
index ab5535c6..e0cef6c4 100644
--- a/plugins/datetime/tz.h
+++ b/plugins/datetime/tz.h
@@ -37,22 +37,19 @@ typedef struct _TzDB TzDB;
 typedef struct _TzLocation TzLocation;
 typedef struct _TzInfo TzInfo;
 
-
 struct _TzDB
 {
-       GPtrArray  *locations;
+       GArray     *locations;
        GHashTable *backward;
 };
 
 struct _TzLocation
 {
-       gchar *country;
-       gdouble latitude;
-       gdouble longitude;
-       gchar *zone;
-       gchar *comment;
-
-       gdouble dist; /* distance to clicked point for comparison */
+       gfloat      latitude;
+       gfloat      longitude;
+       gdouble     dist;      /* distance to clicked point for comparison */
+       const char *country;   /* string is interned */
+       const char *zone;      /* string is interned */
 };
 
 /* see the glibc info page information on time zone information */
@@ -63,27 +60,24 @@ struct _TzLocation
 
 struct _TzInfo
 {
-       gchar *tzname_normal;
-       gchar *tzname_daylight;
+       const char *tzname_normal;
+       const char *tzname_daylight;
        glong utc_offset;
        gint daylight;
 };
 
-
-TzDB      *tz_load_db                 (void);
-void       tz_db_free                 (TzDB *db);
-char *     tz_info_get_clean_name     (TzDB *tz_db,
-                                      const char *tz);
-GPtrArray *tz_get_locations           (TzDB *db);
-void       tz_location_get_position   (TzLocation *loc,
-                                      double *longitude, double *latitude);
-void       tz_location_free           (TzLocation *loc);
-char      *tz_location_get_country    (TzLocation *loc);
-gchar     *tz_location_get_zone       (TzLocation *loc);
-gchar     *tz_location_get_comment    (TzLocation *loc);
-glong      tz_location_get_utc_offset (TzLocation *loc);
-gint       tz_location_set_locally    (TzLocation *loc);
-TzInfo    *tz_info_from_location      (TzLocation *loc);
-void       tz_info_free               (TzInfo *tz_info);
+TzDB       *tz_load_db                  (void);
+void        tz_db_free                  (TzDB             *db);
+const char *tz_info_get_clean_name      (TzDB             *tz_db,
+                                         const char       *tz);
+void        tz_populate_locations       (TzDB             *db,
+                                         GArray           *locations,
+                                         const char       *country_code);
+glong       tz_location_get_utc_offset  (TzLocation       *loc);
+gboolean    tz_location_is_country_code (const TzLocation *loc,
+                                         const char       *country_code);
+TzInfo     *tz_info_from_location       (TzLocation       *loc);
+void        tz_info_free                (TzInfo           *tz_info);
+const char *tz_intern                   (const char       *str);
 
 #endif
diff --git a/plugins/datetime/weather-tz.c b/plugins/datetime/weather-tz.c
index 26668463..09995ea4 100644
--- a/plugins/datetime/weather-tz.c
+++ b/plugins/datetime/weather-tz.c
@@ -27,102 +27,75 @@
 
 struct _WeatherTzDB
 {
-        GList *tz_locations;
+        GArray *tz_locations;
 };
 
-static GList *
-location_get_cities (GWeatherLocation *parent_location)
+static void
+load_timezones (GArray           *tz_locations,
+                GWeatherLocation *location)
 {
-        GList *cities = NULL;
-        GWeatherLocation **children;
-        gint i;
-
-        children = gweather_location_get_children (parent_location);
-        for (i = 0; children[i]; i++) {
-                if (gweather_location_get_level (children[i]) == GWEATHER_LOCATION_CITY) {
-                        cities = g_list_prepend (cities,
-                                                 children[i]);
-                } else {
-                        cities = g_list_concat (cities,
-                                                location_get_cities (children[i]));
-                }
-        }
-
-        return cities;
-}
-
-static gboolean
-weather_location_has_timezone (GWeatherLocation *loc)
-{
-        return gweather_location_get_timezone (loc) != NULL;
-}
-
-/**
- * load_timezones:
- * @cities: a list of #GWeatherLocation
- *
- * Returns: a list of #TzLocation
- */
-static GList *
-load_timezones (GList *cities)
-{
-        GList *l;
-        GList *tz_locations = NULL;
+        if (location == NULL)
+                return;
 
-        for (l = cities; l; l = l->next) {
-                TzLocation *loc;
+        if (gweather_location_get_level (location) == GWEATHER_LOCATION_CITY &&
+            gweather_location_has_coords (location) &&
+            gweather_location_get_timezone (location) != NULL)
+        {
                 const gchar *country;
                 const gchar *timezone_id;
                 gdouble latitude;
                 gdouble longitude;
+                TzLocation loc;
 
-                if (!gweather_location_has_coords (l->data) ||
-                    !weather_location_has_timezone (l->data)) {
-                        g_debug ("Incomplete GWeather location entry: (%s) %s",
-                                 gweather_location_get_country (l->data),
-                                 gweather_location_get_city_name (l->data));
-                        continue;
-                }
-
-                country = gweather_location_get_country (l->data);
-                timezone_id = gweather_timezone_get_tzid (gweather_location_get_timezone (l->data));
-                gweather_location_get_coords (l->data,
-                                              &latitude,
-                                              &longitude);
-
-                loc = g_new0 (TzLocation, 1);
-                loc->country = g_strdup (country);
-                loc->latitude = latitude;
-                loc->longitude = longitude;
-                loc->zone = g_strdup (timezone_id);
-                loc->comment = NULL;
-
-                tz_locations = g_list_prepend (tz_locations, loc);
-        }
+                country = gweather_location_get_country (location);
+                timezone_id = gweather_timezone_get_tzid (gweather_location_get_timezone (location));
+                gweather_location_get_coords (location, &latitude, &longitude);
+
+                loc.latitude = latitude;
+                loc.longitude = longitude;
+                loc.dist = 0;
+                loc.zone = tz_intern (timezone_id);
+                loc.country = tz_intern (country);
 
-        return tz_locations;
+                g_array_append_val (tz_locations, loc);
+        }
+        else
+        {
+                GWeatherLocation **children = gweather_location_get_children (location);
+                for (guint i = 0; children[i] != NULL; i++)
+                        load_timezones (tz_locations, children[i]);
+        }
 }
 
-GList *
-weather_tz_db_get_locations (WeatherTzDB *tzdb)
+void
+weather_tz_db_populate_locations (WeatherTzDB *tzdb,
+                                  GArray      *ar,
+                                  const char  *country_code)
 {
-        return g_list_copy (tzdb->tz_locations);
+       if (country_code == NULL) {
+               if (tzdb->tz_locations->len > 0)
+                       g_array_append_vals (ar,
+                                            &g_array_index (tzdb->tz_locations, TzLocation, 0),
+                                            tzdb->tz_locations->len);
+               return;
+       }
+
+        for (guint i = 0; i < tzdb->tz_locations->len; i++) {
+                const TzLocation *loc = &g_array_index (tzdb->tz_locations, TzLocation, i);
+
+                if (tz_location_is_country_code (loc, country_code))
+                        g_array_append_vals (ar, loc, 1);
+        }
 }
 
 WeatherTzDB *
 weather_tz_db_new (void)
 {
-        GList *cities;
-        GWeatherLocation *world;
         WeatherTzDB *tzdb;
 
-        world = gweather_location_get_world ();
-        cities = location_get_cities (world);
-
         tzdb = g_new0 (WeatherTzDB, 1);
-        tzdb->tz_locations = load_timezones (cities);
-
-        g_list_free (cities);
+        tzdb->tz_locations = g_array_new (FALSE, FALSE, sizeof (TzLocation));
+        load_timezones (tzdb->tz_locations, gweather_location_get_world ());
 
         return tzdb;
 }
@@ -130,7 +103,6 @@ weather_tz_db_new (void)
 void
 weather_tz_db_free (WeatherTzDB *tzdb)
 {
-        g_list_free_full (tzdb->tz_locations, (GDestroyNotify) tz_location_free);
-
+        g_array_unref (tzdb->tz_locations);
         g_free (tzdb);
 }
diff --git a/plugins/datetime/weather-tz.h b/plugins/datetime/weather-tz.h
index 14de4493..be020820 100644
--- a/plugins/datetime/weather-tz.h
+++ b/plugins/datetime/weather-tz.h
@@ -24,8 +24,10 @@
 
 typedef struct _WeatherTzDB WeatherTzDB;
 
-WeatherTzDB     *weather_tz_db_new              (void);
-GList           *weather_tz_db_get_locations    (WeatherTzDB *db);
-void             weather_tz_db_free             (WeatherTzDB *db);
+WeatherTzDB *weather_tz_db_new                (void);
+void         weather_tz_db_populate_locations (WeatherTzDB *tzdb,
+                                               GArray      *ar,
+                                               const char  *country_code);
+void         weather_tz_db_free               (WeatherTzDB *db);
 
 #endif /* __WEATHER_TZ_H */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]