[libgweather/benzea/wip-variant-backend: 2/2] WIP: Use GVariant as backend



commit 4ec71ff186dbfee9b1af82a6da9d935197a4b13c
Author: Benjamin Berg <bberg redhat com>
Date:   Sat Apr 25 23:23:23 2020 +0200

    WIP: Use GVariant as backend

 data/gen-locations-variant.py   |  195 ++++
 libgweather/gweather-db.h       | 2431 +++++++++++++++++++++++++++++++++++++++
 libgweather/gweather-location.c |  683 ++++++-----
 libgweather/gweather-location.h |    8 +
 libgweather/gweather-parser.c   |  223 ----
 libgweather/gweather-parser.h   |   46 -
 libgweather/gweather-private.h  |   46 +-
 libgweather/gweather-timezone.c |   80 +-
 libgweather/meson.build         |    3 +-
 9 files changed, 3132 insertions(+), 583 deletions(-)
---
diff --git a/data/gen-locations-variant.py b/data/gen-locations-variant.py
new file mode 100755
index 0000000..28bd9c4
--- /dev/null
+++ b/data/gen-locations-variant.py
@@ -0,0 +1,195 @@
+#!/usr/bin/python3
+
+import sys
+from gi.repository import GLib
+from collections import OrderedDict
+import math
+import struct
+
+import xml.etree.ElementTree as ET
+tree = ET.parse(sys.argv[1])
+root = tree.getroot()
+
+assert root.tag == "gweather"
+assert root.attrib['format'] == "1.0"
+
+levels = {
+  'gweather' : 0,
+  'region' : 1,
+  'country' : 2,
+  'state' : 3,
+  'city' : 4,
+  'location' : 5,
+  'named-timezone' : 7,
+}
+
+locations = []
+timezones = []
+loc_by_metar = []
+loc_by_country = []
+
+all_ccodes = set()
+
+def get_name(elem):
+    name = elem.find('_name')
+    if name is None:
+        name = elem.find('name')
+        msgctx = ''
+    else:
+        msgctx = name.get('msgctx', default='')
+    if name is None:
+        return '', ''
+    else:
+        return name.text, msgctx
+
+def get_coordinates(elem):
+    coordinates = elem.findtext('coordinates')
+    if coordinates:
+        return tuple(float(c) * math.pi / 180.0 for c in coordinates.split())
+    else:
+        return None
+
+def calc_distance(loc_a, loc_b):
+    # average earth radius
+    radius = 6372.795
+
+    c_a = get_coordinates(loc_a)
+    c_b = get_coordinates(loc_b)
+    if c_a is None or c_b is None:
+        return float("inf")
+
+    if c_a == c_b:
+        return 0
+
+    return math.acos(math.cos(c_a[0]) * math.cos(c_b[0]) * math.cos(c_a[1] - c_b[1]) +
+                     math.sin(c_a[0]) * math.sin(c_b[0])) * radius;
+
+def tz_variant(tz):
+    obsoletes = []
+    for item in tz.findall('obsoletes'):
+        obsoletes.append(item.text)
+
+    return GLib.Variant('((ss)as)', (
+            get_name(loc),
+            obsoletes
+        ))
+
+parent_map = {c:p for p in root.iter() for c in p}
+def loc_variant(loc):
+    children = []
+    # find direct children
+    for child in loc.find('.'):
+        if child.tag in levels:
+            children.append(locations.index(child))
+
+    # Sort by distance from self; this is assumed for the locations inside a city
+    children.sort(key=lambda c: calc_distance(loc, locations[c]))
+
+    zones = []
+    for tz in loc.findall('timezone'):
+        zones.append(timezones.index(tz))
+
+    coordinate = get_coordinates(loc)
+
+    tz_hint = loc.findtext('tz-hint')
+    if tz_hint:
+        for i, tz in enumerate(timezones):
+            if tz.get('id') == tz_hint:
+                tz_hint = (i,)
+                break
+        else:
+            assert "Should not be reached"
+
+    name = get_name(loc)
+
+    parent = parent_map.get(loc)
+    try:
+        parent_idx = locations.index(parent)
+    except:
+        # point to self
+        parent_idx = locations.index(loc)
+
+    nearest_idx = None
+    if loc.tag == 'city' and len(children) == 0:
+        # Try to lookup the nearest sibbling location
+        nearest = None
+        nearest_dist = -1
+        for l in parent.findall('location'):
+            dist = calc_distance(loc, l)
+            if dist > 100:
+                continue
+            if nearest is None or dist < nearest_dist:
+                nearest = l
+                nearest_dist = dist
+
+        if nearest:
+            nearest_idx = (locations.index(nearest), )
+
+    return GLib.Variant('((ss)ssm(dd)ssm(q)ym(q)(q)a(q)a(q))', (
+            name,
+            loc.findtext('zone', default=''),
+            loc.findtext('radar', default=''),
+            coordinate,
+            loc.findtext('iso-code', default=''),
+            loc.findtext('code', default=''),
+            tz_hint,
+            levels[loc.tag],
+            nearest_idx,
+            (parent_idx, ),
+            [(c,) for c in children],
+            [(z,) for z in zones]
+        ))
+
+locations.append(root)
+# Pre-populate the lists to be able to generate indices
+for loc in root.iter('named-timezone'):
+    locations.append(loc)
+    loc_by_metar.append(loc)
+    assert loc.findtext('code') is not None
+for loc in root.iter('region'):
+    locations.append(loc)
+for loc in root.iter('country'):
+    locations.append(loc)
+    loc_by_country.append(loc)
+    c = loc.findtext('iso-code')
+    assert c is not None
+    assert c not in all_ccodes
+    all_ccodes.add(c)
+for loc in root.iter('state'):
+    locations.append(loc)
+for loc in root.iter('city'):
+    locations.append(loc)
+for loc in root.iter('location'):
+    locations.append(loc)
+    loc_by_metar.append(loc)
+    assert loc.findtext('code') is not None
+
+for tz in root.iter('timezone'):
+    timezones.append(tz)
+    assert tz.get('id') is not None
+
+timezones.sort(key=lambda tz: tz.get('id'))
+loc_by_country.sort(key=lambda loc: loc.findtext('iso-code'))
+loc_by_metar.sort(key=lambda loc: loc.findtext('code'))
+
+loc_by_country_var = [(loc.findtext('iso-code'), (locations.index(loc),)) for loc in loc_by_country]
+loc_by_metar_var = [(loc.findtext('code'), (locations.index(loc),)) for loc in loc_by_metar]
+
+timezones_var = [(tz.get('id'), tz_variant(tz)) for tz in timezones]
+locations_var = [loc_variant(loc) for loc in locations]
+
+res = GLib.Variant("(a{s(q)}a{s(q)}a{s((ss)as)}a((ss)ssm(dd)ssm(q)ym(q)(q)a(q)a(q)))", (
+    loc_by_country_var,
+    loc_by_metar_var,
+    timezones_var,
+    locations_var
+    ))
+
+if struct.pack('h', 0x01)[0]:
+    # byteswap on little endian
+    res = res.byteswap()
+
+data = res.get_data_as_bytes().get_data()
+open(sys.argv[2], 'bw').write(data)
+
+
diff --git a/libgweather/gweather-db.h b/libgweather/gweather-db.h
new file mode 100644
index 0000000..37386ba
--- /dev/null
+++ b/libgweather/gweather-db.h
@@ -0,0 +1,2431 @@
+#ifndef __DB___GWEATHER_GV__H__
+#define __DB___GWEATHER_GV__H__
+/* generated code for gweather.gv */
+#include <string.h>
+#include <glib.h>
+
+/********** Basic types *****************/
+
+typedef struct {
+ gconstpointer base;
+ gsize size;
+} DbRef;
+
+#define DB_REF_READ_FRAME_OFFSET(_v, _index) db_ref_read_unaligned_le ((guchar*)((_v).base) + (_v).size - 
(offset_size * ((_index) + 1)), offset_size)
+#define DB_REF_ALIGN(_offset, _align_to) ((_offset + _align_to - 1) & ~(gsize)(_align_to - 1))
+
+/* Note: clz is undefinded for 0, so never call this size == 0 */
+G_GNUC_CONST static inline guint
+db_ref_get_offset_size (gsize size)
+{
+#if defined(__GNUC__) && (__GNUC__ >= 4) && defined(__OPTIMIZE__) && defined(__LP64__)
+  /* Instead of using a lookup table we use nibbles in a lookup word */
+  guint32 v = (guint32)0x88884421;
+  return (v >> (((__builtin_clzl(size) ^ 63) / 8) * 4)) & 0xf;
+#else
+  if (size > G_MAXUINT16)
+    {
+      if (size > G_MAXUINT32)
+        return 8;
+      else
+        return 4;
+    }
+  else
+    {
+      if (size > G_MAXUINT8)
+         return 2;
+      else
+         return 1;
+    }
+#endif
+}
+
+G_GNUC_PURE static inline guint64
+db_ref_read_unaligned_le (guchar *bytes, guint   size)
+{
+  union
+  {
+    guchar bytes[8];
+    guint64 integer;
+  } tmpvalue;
+
+  tmpvalue.integer = 0;
+  /* we unroll the size checks here so that memcpy gets constant args */
+  if (size >= 4)
+    {
+      if (size == 8)
+        memcpy (&tmpvalue.bytes, bytes, 8);
+      else
+        memcpy (&tmpvalue.bytes, bytes, 4);
+    }
+  else
+    {
+      if (size == 2)
+        memcpy (&tmpvalue.bytes, bytes, 2);
+      else
+        memcpy (&tmpvalue.bytes, bytes, 1);
+    }
+
+  return GUINT64_FROM_LE (tmpvalue.integer);
+}
+
+static inline void
+__db_gstring_append_double (GString *string, double d)
+{
+  gchar buffer[100];
+  gint i;
+
+  g_ascii_dtostr (buffer, sizeof buffer, d);
+  for (i = 0; buffer[i]; i++)
+    if (buffer[i] == '.' || buffer[i] == 'e' ||
+        buffer[i] == 'n' || buffer[i] == 'N')
+      break;
+
+  /* if there is no '.' or 'e' in the float then add one */
+  if (buffer[i] == '\0')
+    {
+      buffer[i++] = '.';
+      buffer[i++] = '0';
+      buffer[i++] = '\0';
+    }
+   g_string_append (string, buffer);
+}
+
+static inline void
+__db_gstring_append_string (GString *string, const char *str)
+{
+  gunichar quote = strchr (str, '\'') ? '"' : '\'';
+
+  g_string_append_c (string, quote);
+  while (*str)
+    {
+      gunichar c = g_utf8_get_char (str);
+
+      if (c == quote || c == '\\')
+        g_string_append_c (string, '\\');
+
+      if (g_unichar_isprint (c))
+        g_string_append_unichar (string, c);
+      else
+        {
+          g_string_append_c (string, '\\');
+          if (c < 0x10000)
+            switch (c)
+              {
+              case '\a':
+                g_string_append_c (string, 'a');
+                break;
+
+              case '\b':
+                g_string_append_c (string, 'b');
+                break;
+
+              case '\f':
+                g_string_append_c (string, 'f');
+                break;
+
+              case '\n':
+                g_string_append_c (string, 'n');
+                break;
+
+              case '\r':
+                g_string_append_c (string, 'r');
+                break;
+
+              case '\t':
+                g_string_append_c (string, 't');
+                break;
+
+              case '\v':
+                g_string_append_c (string, 'v');
+                break;
+
+              default:
+                g_string_append_printf (string, "u%04x", c);
+                break;
+              }
+           else
+             g_string_append_printf (string, "U%08x", c);
+        }
+
+      str = g_utf8_next_char (str);
+    }
+
+  g_string_append_c (string, quote);
+}
+
+/************** DbVariantRef *******************/
+
+typedef struct {
+ gconstpointer base;
+ gsize size;
+} DbVariantRef;
+
+static inline DbRef
+db_variant_get_child (DbVariantRef v, const GVariantType **out_type)
+{
+  if (v.size)
+    {
+      guchar *base = (guchar *)v.base;
+      gsize size = v.size - 1;
+
+      /* find '\0' character */
+      while (size > 0 && base[size] != 0)
+        size--;
+
+      /* ensure we didn't just hit the start of the string */
+      if (base[size] == 0)
+       {
+          const char *type_string = (char *) base + size + 1;
+          const char *limit = (char *)base + v.size;
+          const char *end;
+
+          if (g_variant_type_string_scan (type_string, limit, &end) && end == limit)
+            {
+              if (out_type)
+                *out_type = (const GVariantType *)type_string;
+              return (DbRef) { v.base, size };
+            }
+       }
+    }
+  if (out_type)
+    *out_type = G_VARIANT_TYPE_UNIT;
+  return  (DbRef) { "\0", 1 };
+}
+
+static inline const GVariantType *
+db_variant_get_type (DbVariantRef v)
+{
+  if (v.size)
+    {
+      guchar *base = (guchar *)v.base;
+      gsize size = v.size - 1;
+
+      /* find '\0' character */
+      while (size > 0 && base[size] != 0)
+        size--;
+
+      /* ensure we didn't just hit the start of the string */
+      if (base[size] == 0)
+       {
+          const char *type_string = (char *) base + size + 1;
+          const char *limit = (char *)base + v.size;
+          const char *end;
+
+          if (g_variant_type_string_scan (type_string, limit, &end) && end == limit)
+             return (const GVariantType *)type_string;
+       }
+    }
+  return  G_VARIANT_TYPE_UNIT;
+}
+
+static inline gboolean
+db_variant_is_type (DbVariantRef v, const GVariantType *type)
+{
+   return g_variant_type_equal (db_variant_get_type (v), type);
+}
+
+static inline DbVariantRef
+db_variant_from_gvariant (GVariant *v)
+{
+  g_assert (g_variant_type_equal (g_variant_get_type (v), G_VARIANT_TYPE_VARIANT));
+  return (DbVariantRef) { g_variant_get_data (v), g_variant_get_size (v) };
+}
+
+static inline DbVariantRef
+db_variant_from_bytes (GBytes *b)
+{
+  return (DbVariantRef) { g_bytes_get_data (b, NULL), g_bytes_get_size (b) };
+}
+
+static inline DbVariantRef
+db_variant_from_data (gconstpointer data, gsize size)
+{
+  return (DbVariantRef) { data, size };
+}
+
+static inline GVariant *
+db_variant_dup_to_gvariant (DbVariantRef v)
+{
+  return g_variant_new_from_data (G_VARIANT_TYPE_VARIANT, g_memdup (v.base, v.size), v.size, TRUE, g_free, 
NULL);
+}
+
+static inline GVariant *
+db_variant_to_gvariant (DbVariantRef v,
+                              GDestroyNotify      notify,
+                              gpointer            user_data)
+{
+  return g_variant_new_from_data (G_VARIANT_TYPE_VARIANT, g_memdup (v.base, v.size), v.size, TRUE, notify, 
user_data);
+}
+
+static inline GVariant *
+db_variant_to_owned_gvariant (DbVariantRef v,
+                                     GVariant *base)
+{
+  return db_variant_to_gvariant (v, (GDestroyNotify)g_variant_unref, g_variant_ref (base));
+}
+
+static inline GVariant *
+db_variant_peek_as_variant (DbVariantRef v)
+{
+  return g_variant_new_from_data (G_VARIANT_TYPE_VARIANT, v.base, v.size, TRUE, NULL, NULL);
+}
+
+static inline DbVariantRef
+db_variant_from_variant (DbVariantRef v)
+{
+  const GVariantType  *type;
+  DbRef child = db_variant_get_child (v, &type);
+  g_assert (g_variant_type_equal(type, G_VARIANT_TYPE_VARIANT));
+  return db_variant_from_data (child.base, child.size);
+}
+
+static inline GVariant *
+db_variant_dup_child_to_gvariant (DbVariantRef v)
+{
+  const GVariantType  *type;
+  DbRef child = db_variant_get_child (v, &type);
+  return g_variant_new_from_data (type, g_memdup (child.base, child.size), child.size, TRUE, g_free, NULL);
+}
+
+static inline GVariant *
+db_variant_peek_child_as_variant (DbVariantRef v)
+{
+  const GVariantType  *type;
+  DbRef child = db_variant_get_child (v, &type);
+  return g_variant_new_from_data (type, child.base, child.size, TRUE, NULL, NULL);
+}
+
+static inline GString *
+db_variant_format (DbVariantRef v, GString *s, gboolean type_annotate)
+{
+#ifdef DB_DEEP_VARIANT_FORMAT
+  GVariant *gv = db_variant_peek_as_variant (v);
+  return g_variant_print_string (gv, s, TRUE);
+#else
+  const GVariantType  *type = db_variant_get_type (v);
+  g_string_append_printf (s, "<@%.*s>", (int)g_variant_type_get_string_length (type), (const char *)type);
+  return s;
+#endif
+}
+
+static inline char *
+db_variant_print (DbVariantRef v, gboolean type_annotate)
+{
+  GString *s = g_string_new ("");
+  db_variant_format (v, s, type_annotate);
+  return g_string_free (s, FALSE);
+}
+static inline gboolean
+db_variant_get_boolean (DbVariantRef v)
+{
+  return (gboolean)*((guint8 *)v.base);
+}
+static inline guint8
+db_variant_get_byte (DbVariantRef v)
+{
+  return (guint8)*((guint8 *)v.base);
+}
+static inline gint16
+db_variant_get_int16 (DbVariantRef v)
+{
+  return (gint16)*((gint16 *)v.base);
+}
+static inline guint16
+db_variant_get_uint16 (DbVariantRef v)
+{
+  return (guint16)*((guint16 *)v.base);
+}
+static inline gint32
+db_variant_get_int32 (DbVariantRef v)
+{
+  return (gint32)*((gint32 *)v.base);
+}
+static inline guint32
+db_variant_get_uint32 (DbVariantRef v)
+{
+  return (guint32)*((guint32 *)v.base);
+}
+static inline gint64
+db_variant_get_int64 (DbVariantRef v)
+{
+  return (gint64)*((gint64 *)v.base);
+}
+static inline guint64
+db_variant_get_uint64 (DbVariantRef v)
+{
+  return (guint64)*((guint64 *)v.base);
+}
+static inline guint32
+db_variant_get_handle (DbVariantRef v)
+{
+  return (guint32)*((guint32 *)v.base);
+}
+static inline double
+db_variant_get_double (DbVariantRef v)
+{
+  return (double)*((double *)v.base);
+}
+static inline const char *
+db_variant_get_string (DbVariantRef v)
+{
+  return (const char *)v.base;
+}
+static inline const char *
+db_variant_get_objectpath (DbVariantRef v)
+{
+  return (const char *)v.base;
+}
+static inline const char *
+db_variant_get_signature (DbVariantRef v)
+{
+  return (const char *)v.base;
+}
+
+/************** DbIdx *******************/
+#define DB_IDX_TYPESTRING "(q)"
+#define DB_IDX_TYPEFORMAT ((const GVariantType *) DB_IDX_TYPESTRING)
+
+typedef struct {
+ gconstpointer base;
+ gsize size;
+} DbIdxRef;
+
+typedef struct {
+  guint16 idx;/* big endian */
+} DbIdx;
+
+static inline DbIdxRef
+db_idx_from_gvariant (GVariant *v)
+{
+  g_assert (g_variant_type_equal (g_variant_get_type (v), DB_IDX_TYPESTRING));
+  return (DbIdxRef) { g_variant_get_data (v), g_variant_get_size (v) };
+}
+
+static inline DbIdxRef
+db_idx_from_bytes (GBytes *b)
+{
+  g_assert (g_bytes_get_size (b) == 2);
+
+  return (DbIdxRef) { g_bytes_get_data (b, NULL), g_bytes_get_size (b) };
+}
+
+static inline DbIdxRef
+db_idx_from_data (gconstpointer data, gsize size)
+{
+  g_assert (size == 2);
+
+  return (DbIdxRef) { data, size };
+}
+
+static inline GVariant *
+db_idx_dup_to_gvariant (DbIdxRef v)
+{
+  return g_variant_new_from_data (DB_IDX_TYPEFORMAT, g_memdup (v.base, v.size), v.size, TRUE, g_free, NULL);
+}
+
+static inline GVariant *
+db_idx_to_gvariant (DbIdxRef v,
+                             GDestroyNotify      notify,
+                             gpointer            user_data)
+{
+  return g_variant_new_from_data (DB_IDX_TYPEFORMAT, v.base, v.size, TRUE, notify, user_data);
+}
+
+static inline GVariant *
+db_idx_to_owned_gvariant (DbIdxRef v, GVariant *base)
+{
+  return db_idx_to_gvariant (v, (GDestroyNotify)g_variant_unref, g_variant_ref (base));
+}
+
+static inline GVariant *
+db_idx_peek_as_gvariant (DbIdxRef v)
+{
+  return g_variant_new_from_data (DB_IDX_TYPEFORMAT, v.base, v.size, TRUE, NULL, NULL);
+}
+
+static inline DbIdxRef
+db_idx_from_variant (DbVariantRef v)
+{
+  const GVariantType  *type;
+  DbRef child = db_variant_get_child (v, &type);
+  g_assert (g_variant_type_equal(type, DB_IDX_TYPESTRING));
+  return db_idx_from_data (child.base, child.size);
+}
+
+static inline const DbIdx *
+db_idx_peek (DbIdxRef v) {
+  return (const DbIdx *)v.base;
+}
+
+#define DB_IDX_INDEXOF_IDX 0
+
+static inline guint16
+db_idx_get_idx (DbIdxRef v)
+{
+  guint offset = ((1) & (~(gsize)1)) + 0;
+  return GUINT16_FROM_BE((guint16)G_STRUCT_MEMBER(guint16, v.base, offset));
+}
+
+static inline GString *
+db_idx_format (DbIdxRef v, GString *s, gboolean type_annotate)
+{
+  g_string_append_printf (s, "(%s%"G_GUINT16_FORMAT",)",
+                   type_annotate ? "uint16 " : "",
+                   db_idx_get_idx (v));
+  return s;
+}
+
+static inline char *
+db_idx_print (DbIdxRef v, gboolean type_annotate)
+{
+  GString *s = g_string_new ("");
+  db_idx_format (v, s, type_annotate);
+  return g_string_free (s, FALSE);
+}
+
+/************** DbI18n *******************/
+#define DB_I18N_TYPESTRING "(ss)"
+#define DB_I18N_TYPEFORMAT ((const GVariantType *) DB_I18N_TYPESTRING)
+
+typedef struct {
+ gconstpointer base;
+ gsize size;
+} DbI18nRef;
+
+
+static inline DbI18nRef
+db_i18n_from_gvariant (GVariant *v)
+{
+  g_assert (g_variant_type_equal (g_variant_get_type (v), DB_I18N_TYPESTRING));
+  return (DbI18nRef) { g_variant_get_data (v), g_variant_get_size (v) };
+}
+
+static inline DbI18nRef
+db_i18n_from_bytes (GBytes *b)
+{
+  return (DbI18nRef) { g_bytes_get_data (b, NULL), g_bytes_get_size (b) };
+}
+
+static inline DbI18nRef
+db_i18n_from_data (gconstpointer data, gsize size)
+{
+  return (DbI18nRef) { data, size };
+}
+
+static inline GVariant *
+db_i18n_dup_to_gvariant (DbI18nRef v)
+{
+  return g_variant_new_from_data (DB_I18N_TYPEFORMAT, g_memdup (v.base, v.size), v.size, TRUE, g_free, NULL);
+}
+
+static inline GVariant *
+db_i18n_to_gvariant (DbI18nRef v,
+                             GDestroyNotify      notify,
+                             gpointer            user_data)
+{
+  return g_variant_new_from_data (DB_I18N_TYPEFORMAT, v.base, v.size, TRUE, notify, user_data);
+}
+
+static inline GVariant *
+db_i18n_to_owned_gvariant (DbI18nRef v, GVariant *base)
+{
+  return db_i18n_to_gvariant (v, (GDestroyNotify)g_variant_unref, g_variant_ref (base));
+}
+
+static inline GVariant *
+db_i18n_peek_as_gvariant (DbI18nRef v)
+{
+  return g_variant_new_from_data (DB_I18N_TYPEFORMAT, v.base, v.size, TRUE, NULL, NULL);
+}
+
+static inline DbI18nRef
+db_i18n_from_variant (DbVariantRef v)
+{
+  const GVariantType  *type;
+  DbRef child = db_variant_get_child (v, &type);
+  g_assert (g_variant_type_equal(type, DB_I18N_TYPESTRING));
+  return db_i18n_from_data (child.base, child.size);
+}
+
+#define DB_I18N_INDEXOF_STR 0
+
+static inline const char *
+db_i18n_get_str (DbI18nRef v)
+{
+  guint offset_size = db_ref_get_offset_size (v.size);
+  guint offset = ((0) & (~(gsize)0)) + 0;
+  const char *base = (const char *)v.base;
+  gsize start = offset;
+  G_GNUC_UNUSED gsize end = DB_REF_READ_FRAME_OFFSET(v, 0);
+  g_assert (start <= end);
+  g_assert (end <= v.size);
+  g_assert (base[end-1] == 0);
+  return &G_STRUCT_MEMBER(const char, v.base, start);
+}
+
+#define DB_I18N_INDEXOF_MSGCTXT 1
+
+static inline const char *
+db_i18n_get_msgctxt (DbI18nRef v)
+{
+  guint offset_size = db_ref_get_offset_size (v.size);
+  gsize last_end = DB_REF_READ_FRAME_OFFSET(v, 0);
+  guint offset = ((last_end + 0) & (~(gsize)0)) + 0;
+  const char *base = (const char *)v.base;
+  gsize start = offset;
+  G_GNUC_UNUSED gsize end = v.size - offset_size * 1;
+  g_assert (start <= end);
+  g_assert (end <= v.size);
+  g_assert (base[end-1] == 0);
+  return &G_STRUCT_MEMBER(const char, v.base, start);
+}
+
+static inline GString *
+db_i18n_format (DbI18nRef v, GString *s, gboolean type_annotate)
+{
+  g_string_append (s, "(");
+  __db_gstring_append_string (s, db_i18n_get_str (v));
+  g_string_append (s, ", ");
+  __db_gstring_append_string (s, db_i18n_get_msgctxt (v));
+  g_string_append (s, ")");
+  return s;
+}
+
+static inline char *
+db_i18n_print (DbI18nRef v, gboolean type_annotate)
+{
+  GString *s = g_string_new ("");
+  db_i18n_format (v, s, type_annotate);
+  return g_string_free (s, FALSE);
+}
+
+/************** DbArrayofstring *******************/
+#define DB_ARRAYOFSTRING_TYPESTRING "as"
+#define DB_ARRAYOFSTRING_TYPEFORMAT ((const GVariantType *) DB_ARRAYOFSTRING_TYPESTRING)
+
+typedef struct {
+ gconstpointer base;
+ gsize size;
+} DbArrayofstringRef;
+
+
+static inline DbArrayofstringRef
+db_arrayofstring_from_gvariant (GVariant *v)
+{
+  g_assert (g_variant_type_equal (g_variant_get_type (v), DB_ARRAYOFSTRING_TYPESTRING));
+  return (DbArrayofstringRef) { g_variant_get_data (v), g_variant_get_size (v) };
+}
+
+static inline DbArrayofstringRef
+db_arrayofstring_from_bytes (GBytes *b)
+{
+  return (DbArrayofstringRef) { g_bytes_get_data (b, NULL), g_bytes_get_size (b) };
+}
+
+static inline DbArrayofstringRef
+db_arrayofstring_from_data (gconstpointer data, gsize size)
+{
+  return (DbArrayofstringRef) { data, size };
+}
+
+static inline GVariant *
+db_arrayofstring_dup_to_gvariant (DbArrayofstringRef v)
+{
+  return g_variant_new_from_data (DB_ARRAYOFSTRING_TYPEFORMAT, g_memdup (v.base, v.size), v.size, TRUE, 
g_free, NULL);
+}
+
+static inline GVariant *
+db_arrayofstring_to_gvariant (DbArrayofstringRef v,
+                             GDestroyNotify      notify,
+                             gpointer            user_data)
+{
+  return g_variant_new_from_data (DB_ARRAYOFSTRING_TYPEFORMAT, v.base, v.size, TRUE, notify, user_data);
+}
+
+static inline GVariant *
+db_arrayofstring_to_owned_gvariant (DbArrayofstringRef v, GVariant *base)
+{
+  return db_arrayofstring_to_gvariant (v, (GDestroyNotify)g_variant_unref, g_variant_ref (base));
+}
+
+static inline GVariant *
+db_arrayofstring_peek_as_gvariant (DbArrayofstringRef v)
+{
+  return g_variant_new_from_data (DB_ARRAYOFSTRING_TYPEFORMAT, v.base, v.size, TRUE, NULL, NULL);
+}
+
+static inline DbArrayofstringRef
+db_arrayofstring_from_variant (DbVariantRef v)
+{
+  const GVariantType  *type;
+  DbRef child = db_variant_get_child (v, &type);
+  g_assert (g_variant_type_equal(type, DB_ARRAYOFSTRING_TYPESTRING));
+  return db_arrayofstring_from_data (child.base, child.size);
+}
+
+static inline gsize
+db_arrayofstring_get_length (DbArrayofstringRef v)
+{
+  if (v.size == 0)
+    return 0;
+  guint offset_size = db_ref_get_offset_size (v.size);
+  gsize last_end = DB_REF_READ_FRAME_OFFSET(v, 0);
+  gsize offsets_array_size;
+  if (last_end > v.size)
+    return 0;
+  offsets_array_size = v.size - last_end;
+  if (offsets_array_size % offset_size != 0)
+    return 0;
+  gsize length  = offsets_array_size / offset_size;
+  return length;
+}
+
+static inline const char *
+db_arrayofstring_get_at (DbArrayofstringRef v, gsize index)
+{
+  guint offset_size = db_ref_get_offset_size (v.size);
+  gsize last_end = DB_REF_READ_FRAME_OFFSET(v, 0);
+  gsize len = (v.size - last_end) / offset_size;
+  gsize start = (index > 0) ? DB_REF_ALIGN(DB_REF_READ_FRAME_OFFSET(v, len - index), 1) : 0;
+  G_GNUC_UNUSED gsize end = DB_REF_READ_FRAME_OFFSET(v, len - index - 1);
+  g_assert (start <= end);
+  g_assert (end <= last_end);
+  const char *base = (const char *)v.base;
+  g_assert (base[end-1] == 0);
+  return base + start;
+}
+
+static inline const char **
+db_arrayofstring_to_strv (DbArrayofstringRef v, gsize *length_out)
+{
+  gsize length = db_arrayofstring_get_length (v);
+  gsize i;
+  const char **resv = g_new (const char *, length + 1);
+
+  for (i = 0; i < length; i++)
+    resv[i] = db_arrayofstring_get_at (v, i);
+  resv[i] = NULL;
+
+  if (length_out)
+    *length_out = length;
+
+  return resv;
+}
+
+static inline GString *
+db_arrayofstring_format (DbArrayofstringRef v, GString *s, gboolean type_annotate)
+{
+  gsize len = db_arrayofstring_get_length (v);
+  gsize i;
+  if (len == 0 && type_annotate)
+    g_string_append_printf (s, "@%s ", DB_ARRAYOFSTRING_TYPESTRING);
+  g_string_append_c (s, '[');
+  for (i = 0; i < len; i++)
+    {
+      if (i != 0)
+        g_string_append (s, ", ");
+      __db_gstring_append_string (s, db_arrayofstring_get_at (v, i));
+    }
+  g_string_append_c (s, ']');
+  return s;
+}
+
+static inline char *
+db_arrayofstring_print (DbArrayofstringRef v, gboolean type_annotate)
+{
+  GString *s = g_string_new ("");
+  db_arrayofstring_format (v, s, type_annotate);
+  return g_string_free (s, FALSE);
+}
+
+/************** DbTimezone *******************/
+#define DB_TIMEZONE_TYPESTRING "((ss)as)"
+#define DB_TIMEZONE_TYPEFORMAT ((const GVariantType *) DB_TIMEZONE_TYPESTRING)
+
+typedef struct {
+ gconstpointer base;
+ gsize size;
+} DbTimezoneRef;
+
+
+static inline DbTimezoneRef
+db_timezone_from_gvariant (GVariant *v)
+{
+  g_assert (g_variant_type_equal (g_variant_get_type (v), DB_TIMEZONE_TYPESTRING));
+  return (DbTimezoneRef) { g_variant_get_data (v), g_variant_get_size (v) };
+}
+
+static inline DbTimezoneRef
+db_timezone_from_bytes (GBytes *b)
+{
+  return (DbTimezoneRef) { g_bytes_get_data (b, NULL), g_bytes_get_size (b) };
+}
+
+static inline DbTimezoneRef
+db_timezone_from_data (gconstpointer data, gsize size)
+{
+  return (DbTimezoneRef) { data, size };
+}
+
+static inline GVariant *
+db_timezone_dup_to_gvariant (DbTimezoneRef v)
+{
+  return g_variant_new_from_data (DB_TIMEZONE_TYPEFORMAT, g_memdup (v.base, v.size), v.size, TRUE, g_free, 
NULL);
+}
+
+static inline GVariant *
+db_timezone_to_gvariant (DbTimezoneRef v,
+                             GDestroyNotify      notify,
+                             gpointer            user_data)
+{
+  return g_variant_new_from_data (DB_TIMEZONE_TYPEFORMAT, v.base, v.size, TRUE, notify, user_data);
+}
+
+static inline GVariant *
+db_timezone_to_owned_gvariant (DbTimezoneRef v, GVariant *base)
+{
+  return db_timezone_to_gvariant (v, (GDestroyNotify)g_variant_unref, g_variant_ref (base));
+}
+
+static inline GVariant *
+db_timezone_peek_as_gvariant (DbTimezoneRef v)
+{
+  return g_variant_new_from_data (DB_TIMEZONE_TYPEFORMAT, v.base, v.size, TRUE, NULL, NULL);
+}
+
+static inline DbTimezoneRef
+db_timezone_from_variant (DbVariantRef v)
+{
+  const GVariantType  *type;
+  DbRef child = db_variant_get_child (v, &type);
+  g_assert (g_variant_type_equal(type, DB_TIMEZONE_TYPESTRING));
+  return db_timezone_from_data (child.base, child.size);
+}
+
+#define DB_TIMEZONE_INDEXOF_NAME 0
+
+static inline DbI18nRef
+db_timezone_get_name (DbTimezoneRef v)
+{
+  guint offset_size = db_ref_get_offset_size (v.size);
+  guint offset = ((0) & (~(gsize)0)) + 0;
+  gsize start = offset;
+  gsize end = DB_REF_READ_FRAME_OFFSET(v, 0);
+  g_assert (start <= end);
+  g_assert (end <= v.size);
+  return (DbI18nRef) { G_STRUCT_MEMBER_P(v.base, start), end - start };
+}
+
+#define DB_TIMEZONE_INDEXOF_OBSOLETES 1
+
+static inline DbArrayofstringRef
+db_timezone_get_obsoletes (DbTimezoneRef v)
+{
+  guint offset_size = db_ref_get_offset_size (v.size);
+  gsize last_end = DB_REF_READ_FRAME_OFFSET(v, 0);
+  guint offset = ((last_end + 0) & (~(gsize)0)) + 0;
+  gsize start = offset;
+  gsize end = v.size - offset_size * 1;
+  g_assert (start <= end);
+  g_assert (end <= v.size);
+  return (DbArrayofstringRef) { G_STRUCT_MEMBER_P(v.base, start), end - start };
+}
+
+static inline GString *
+db_timezone_format (DbTimezoneRef v, GString *s, gboolean type_annotate)
+{
+  g_string_append (s, "(");
+  db_i18n_format (db_timezone_get_name (v), s, type_annotate);
+  g_string_append (s, ", ");
+  db_arrayofstring_format (db_timezone_get_obsoletes (v), s, type_annotate);
+  g_string_append (s, ")");
+  return s;
+}
+
+static inline char *
+db_timezone_print (DbTimezoneRef v, gboolean type_annotate)
+{
+  GString *s = g_string_new ("");
+  db_timezone_format (v, s, type_annotate);
+  return g_string_free (s, FALSE);
+}
+
+/************** DbCoordinate *******************/
+#define DB_COORDINATE_TYPESTRING "(dd)"
+#define DB_COORDINATE_TYPEFORMAT ((const GVariantType *) DB_COORDINATE_TYPESTRING)
+
+typedef struct {
+ gconstpointer base;
+ gsize size;
+} DbCoordinateRef;
+
+typedef struct {
+  double lat;/* big endian */
+  double lon;/* big endian */
+} DbCoordinate;
+
+static inline DbCoordinateRef
+db_coordinate_from_gvariant (GVariant *v)
+{
+  g_assert (g_variant_type_equal (g_variant_get_type (v), DB_COORDINATE_TYPESTRING));
+  return (DbCoordinateRef) { g_variant_get_data (v), g_variant_get_size (v) };
+}
+
+static inline DbCoordinateRef
+db_coordinate_from_bytes (GBytes *b)
+{
+  g_assert (g_bytes_get_size (b) == 16);
+
+  return (DbCoordinateRef) { g_bytes_get_data (b, NULL), g_bytes_get_size (b) };
+}
+
+static inline DbCoordinateRef
+db_coordinate_from_data (gconstpointer data, gsize size)
+{
+  g_assert (size == 16);
+
+  return (DbCoordinateRef) { data, size };
+}
+
+static inline GVariant *
+db_coordinate_dup_to_gvariant (DbCoordinateRef v)
+{
+  return g_variant_new_from_data (DB_COORDINATE_TYPEFORMAT, g_memdup (v.base, v.size), v.size, TRUE, g_free, 
NULL);
+}
+
+static inline GVariant *
+db_coordinate_to_gvariant (DbCoordinateRef v,
+                             GDestroyNotify      notify,
+                             gpointer            user_data)
+{
+  return g_variant_new_from_data (DB_COORDINATE_TYPEFORMAT, v.base, v.size, TRUE, notify, user_data);
+}
+
+static inline GVariant *
+db_coordinate_to_owned_gvariant (DbCoordinateRef v, GVariant *base)
+{
+  return db_coordinate_to_gvariant (v, (GDestroyNotify)g_variant_unref, g_variant_ref (base));
+}
+
+static inline GVariant *
+db_coordinate_peek_as_gvariant (DbCoordinateRef v)
+{
+  return g_variant_new_from_data (DB_COORDINATE_TYPEFORMAT, v.base, v.size, TRUE, NULL, NULL);
+}
+
+static inline DbCoordinateRef
+db_coordinate_from_variant (DbVariantRef v)
+{
+  const GVariantType  *type;
+  DbRef child = db_variant_get_child (v, &type);
+  g_assert (g_variant_type_equal(type, DB_COORDINATE_TYPESTRING));
+  return db_coordinate_from_data (child.base, child.size);
+}
+
+static inline const DbCoordinate *
+db_coordinate_peek (DbCoordinateRef v) {
+  return (const DbCoordinate *)v.base;
+}
+
+#define DB_COORDINATE_INDEXOF_LAT 0
+
+static inline double
+db_coordinate_get_lat (DbCoordinateRef v)
+{
+  guint offset = ((7) & (~(gsize)7)) + 0;
+  return DOUBLE_FROM_BE((double)G_STRUCT_MEMBER(double, v.base, offset));
+}
+
+#define DB_COORDINATE_INDEXOF_LON 1
+
+static inline double
+db_coordinate_get_lon (DbCoordinateRef v)
+{
+  guint offset = ((7) & (~(gsize)7)) + 8;
+  return DOUBLE_FROM_BE((double)G_STRUCT_MEMBER(double, v.base, offset));
+}
+
+static inline GString *
+db_coordinate_format (DbCoordinateRef v, GString *s, gboolean type_annotate)
+{
+  g_string_append (s, "(");
+  __db_gstring_append_double (s, db_coordinate_get_lat (v));
+  g_string_append (s, ", ");
+  __db_gstring_append_double (s, db_coordinate_get_lon (v));
+  g_string_append (s, ")");
+  return s;
+}
+
+static inline char *
+db_coordinate_print (DbCoordinateRef v, gboolean type_annotate)
+{
+  GString *s = g_string_new ("");
+  db_coordinate_format (v, s, type_annotate);
+  return g_string_free (s, FALSE);
+}
+
+/************** DbMaybeCoordinate *******************/
+#define DB_MAYBE_COORDINATE_TYPESTRING "m(dd)"
+#define DB_MAYBE_COORDINATE_TYPEFORMAT ((const GVariantType *) DB_MAYBE_COORDINATE_TYPESTRING)
+
+typedef struct {
+ gconstpointer base;
+ gsize size;
+} DbMaybeCoordinateRef;
+
+
+static inline DbMaybeCoordinateRef
+db_maybe_coordinate_from_gvariant (GVariant *v)
+{
+  g_assert (g_variant_type_equal (g_variant_get_type (v), DB_MAYBE_COORDINATE_TYPESTRING));
+  return (DbMaybeCoordinateRef) { g_variant_get_data (v), g_variant_get_size (v) };
+}
+
+static inline DbMaybeCoordinateRef
+db_maybe_coordinate_from_bytes (GBytes *b)
+{
+  return (DbMaybeCoordinateRef) { g_bytes_get_data (b, NULL), g_bytes_get_size (b) };
+}
+
+static inline DbMaybeCoordinateRef
+db_maybe_coordinate_from_data (gconstpointer data, gsize size)
+{
+  return (DbMaybeCoordinateRef) { data, size };
+}
+
+static inline GVariant *
+db_maybe_coordinate_dup_to_gvariant (DbMaybeCoordinateRef v)
+{
+  return g_variant_new_from_data (DB_MAYBE_COORDINATE_TYPEFORMAT, g_memdup (v.base, v.size), v.size, TRUE, 
g_free, NULL);
+}
+
+static inline GVariant *
+db_maybe_coordinate_to_gvariant (DbMaybeCoordinateRef v,
+                             GDestroyNotify      notify,
+                             gpointer            user_data)
+{
+  return g_variant_new_from_data (DB_MAYBE_COORDINATE_TYPEFORMAT, v.base, v.size, TRUE, notify, user_data);
+}
+
+static inline GVariant *
+db_maybe_coordinate_to_owned_gvariant (DbMaybeCoordinateRef v, GVariant *base)
+{
+  return db_maybe_coordinate_to_gvariant (v, (GDestroyNotify)g_variant_unref, g_variant_ref (base));
+}
+
+static inline GVariant *
+db_maybe_coordinate_peek_as_gvariant (DbMaybeCoordinateRef v)
+{
+  return g_variant_new_from_data (DB_MAYBE_COORDINATE_TYPEFORMAT, v.base, v.size, TRUE, NULL, NULL);
+}
+
+static inline DbMaybeCoordinateRef
+db_maybe_coordinate_from_variant (DbVariantRef v)
+{
+  const GVariantType  *type;
+  DbRef child = db_variant_get_child (v, &type);
+  g_assert (g_variant_type_equal(type, DB_MAYBE_COORDINATE_TYPESTRING));
+  return db_maybe_coordinate_from_data (child.base, child.size);
+}
+
+static inline gboolean
+db_maybe_coordinate_has_value(DbMaybeCoordinateRef v)
+{
+  return v.size != 0;
+}
+static inline DbCoordinateRef
+db_maybe_coordinate_get_value (DbMaybeCoordinateRef v)
+{
+  g_assert (v.size == 16);
+  return (DbCoordinateRef) { v.base, v.size };
+}
+static inline GString *
+db_maybe_coordinate_format (DbMaybeCoordinateRef v, GString *s, gboolean type_annotate)
+{
+  if (type_annotate)
+    g_string_append_printf (s, "@%s ", DB_MAYBE_COORDINATE_TYPESTRING);
+  if (v.size != 0)
+    {
+      db_coordinate_format (db_maybe_coordinate_get_value (v), s, FALSE);
+    }
+  else
+    {
+      g_string_append (s, "nothing");
+    }
+  return s;
+}
+
+static inline char *
+db_maybe_coordinate_print (DbMaybeCoordinateRef v, gboolean type_annotate)
+{
+  GString *s = g_string_new ("");
+  db_maybe_coordinate_format (v, s, type_annotate);
+  return g_string_free (s, FALSE);
+}
+
+/************** DbMaybeIdx *******************/
+#define DB_MAYBE_IDX_TYPESTRING "m(q)"
+#define DB_MAYBE_IDX_TYPEFORMAT ((const GVariantType *) DB_MAYBE_IDX_TYPESTRING)
+
+typedef struct {
+ gconstpointer base;
+ gsize size;
+} DbMaybeIdxRef;
+
+
+static inline DbMaybeIdxRef
+db_maybe_idx_from_gvariant (GVariant *v)
+{
+  g_assert (g_variant_type_equal (g_variant_get_type (v), DB_MAYBE_IDX_TYPESTRING));
+  return (DbMaybeIdxRef) { g_variant_get_data (v), g_variant_get_size (v) };
+}
+
+static inline DbMaybeIdxRef
+db_maybe_idx_from_bytes (GBytes *b)
+{
+  return (DbMaybeIdxRef) { g_bytes_get_data (b, NULL), g_bytes_get_size (b) };
+}
+
+static inline DbMaybeIdxRef
+db_maybe_idx_from_data (gconstpointer data, gsize size)
+{
+  return (DbMaybeIdxRef) { data, size };
+}
+
+static inline GVariant *
+db_maybe_idx_dup_to_gvariant (DbMaybeIdxRef v)
+{
+  return g_variant_new_from_data (DB_MAYBE_IDX_TYPEFORMAT, g_memdup (v.base, v.size), v.size, TRUE, g_free, 
NULL);
+}
+
+static inline GVariant *
+db_maybe_idx_to_gvariant (DbMaybeIdxRef v,
+                             GDestroyNotify      notify,
+                             gpointer            user_data)
+{
+  return g_variant_new_from_data (DB_MAYBE_IDX_TYPEFORMAT, v.base, v.size, TRUE, notify, user_data);
+}
+
+static inline GVariant *
+db_maybe_idx_to_owned_gvariant (DbMaybeIdxRef v, GVariant *base)
+{
+  return db_maybe_idx_to_gvariant (v, (GDestroyNotify)g_variant_unref, g_variant_ref (base));
+}
+
+static inline GVariant *
+db_maybe_idx_peek_as_gvariant (DbMaybeIdxRef v)
+{
+  return g_variant_new_from_data (DB_MAYBE_IDX_TYPEFORMAT, v.base, v.size, TRUE, NULL, NULL);
+}
+
+static inline DbMaybeIdxRef
+db_maybe_idx_from_variant (DbVariantRef v)
+{
+  const GVariantType  *type;
+  DbRef child = db_variant_get_child (v, &type);
+  g_assert (g_variant_type_equal(type, DB_MAYBE_IDX_TYPESTRING));
+  return db_maybe_idx_from_data (child.base, child.size);
+}
+
+static inline gboolean
+db_maybe_idx_has_value(DbMaybeIdxRef v)
+{
+  return v.size != 0;
+}
+static inline DbIdxRef
+db_maybe_idx_get_value (DbMaybeIdxRef v)
+{
+  g_assert (v.size == 2);
+  return (DbIdxRef) { v.base, v.size };
+}
+static inline GString *
+db_maybe_idx_format (DbMaybeIdxRef v, GString *s, gboolean type_annotate)
+{
+  if (type_annotate)
+    g_string_append_printf (s, "@%s ", DB_MAYBE_IDX_TYPESTRING);
+  if (v.size != 0)
+    {
+      db_idx_format (db_maybe_idx_get_value (v), s, FALSE);
+    }
+  else
+    {
+      g_string_append (s, "nothing");
+    }
+  return s;
+}
+
+static inline char *
+db_maybe_idx_print (DbMaybeIdxRef v, gboolean type_annotate)
+{
+  GString *s = g_string_new ("");
+  db_maybe_idx_format (v, s, type_annotate);
+  return g_string_free (s, FALSE);
+}
+
+/************** DbArrayofIdx *******************/
+#define DB_ARRAYOF_IDX_TYPESTRING "a(q)"
+#define DB_ARRAYOF_IDX_TYPEFORMAT ((const GVariantType *) DB_ARRAYOF_IDX_TYPESTRING)
+
+typedef struct {
+ gconstpointer base;
+ gsize size;
+} DbArrayofIdxRef;
+
+
+static inline DbArrayofIdxRef
+db_arrayof_idx_from_gvariant (GVariant *v)
+{
+  g_assert (g_variant_type_equal (g_variant_get_type (v), DB_ARRAYOF_IDX_TYPESTRING));
+  return (DbArrayofIdxRef) { g_variant_get_data (v), g_variant_get_size (v) };
+}
+
+static inline DbArrayofIdxRef
+db_arrayof_idx_from_bytes (GBytes *b)
+{
+  return (DbArrayofIdxRef) { g_bytes_get_data (b, NULL), g_bytes_get_size (b) };
+}
+
+static inline DbArrayofIdxRef
+db_arrayof_idx_from_data (gconstpointer data, gsize size)
+{
+  return (DbArrayofIdxRef) { data, size };
+}
+
+static inline GVariant *
+db_arrayof_idx_dup_to_gvariant (DbArrayofIdxRef v)
+{
+  return g_variant_new_from_data (DB_ARRAYOF_IDX_TYPEFORMAT, g_memdup (v.base, v.size), v.size, TRUE, 
g_free, NULL);
+}
+
+static inline GVariant *
+db_arrayof_idx_to_gvariant (DbArrayofIdxRef v,
+                             GDestroyNotify      notify,
+                             gpointer            user_data)
+{
+  return g_variant_new_from_data (DB_ARRAYOF_IDX_TYPEFORMAT, v.base, v.size, TRUE, notify, user_data);
+}
+
+static inline GVariant *
+db_arrayof_idx_to_owned_gvariant (DbArrayofIdxRef v, GVariant *base)
+{
+  return db_arrayof_idx_to_gvariant (v, (GDestroyNotify)g_variant_unref, g_variant_ref (base));
+}
+
+static inline GVariant *
+db_arrayof_idx_peek_as_gvariant (DbArrayofIdxRef v)
+{
+  return g_variant_new_from_data (DB_ARRAYOF_IDX_TYPEFORMAT, v.base, v.size, TRUE, NULL, NULL);
+}
+
+static inline DbArrayofIdxRef
+db_arrayof_idx_from_variant (DbVariantRef v)
+{
+  const GVariantType  *type;
+  DbRef child = db_variant_get_child (v, &type);
+  g_assert (g_variant_type_equal(type, DB_ARRAYOF_IDX_TYPESTRING));
+  return db_arrayof_idx_from_data (child.base, child.size);
+}
+
+static inline gsize
+db_arrayof_idx_get_length (DbArrayofIdxRef v)
+{
+  gsize length = v.size / 2;
+  return length;
+}
+
+static inline DbIdxRef
+db_arrayof_idx_get_at (DbArrayofIdxRef v, gsize index)
+{
+  return (DbIdxRef) { G_STRUCT_MEMBER_P(v.base, index * 2), 2};
+}
+
+static inline const DbIdx *
+db_arrayof_idx_peek (DbArrayofIdxRef v)
+{
+  return (const DbIdx *)v.base;
+}
+
+static inline GString *
+db_arrayof_idx_format (DbArrayofIdxRef v, GString *s, gboolean type_annotate)
+{
+  gsize len = db_arrayof_idx_get_length (v);
+  gsize i;
+  if (len == 0 && type_annotate)
+    g_string_append_printf (s, "@%s ", DB_ARRAYOF_IDX_TYPESTRING);
+  g_string_append_c (s, '[');
+  for (i = 0; i < len; i++)
+    {
+      if (i != 0)
+        g_string_append (s, ", ");
+      db_idx_format (db_arrayof_idx_get_at (v, i), s, ((i == 0) ? type_annotate : FALSE));
+    }
+  g_string_append_c (s, ']');
+  return s;
+}
+
+static inline char *
+db_arrayof_idx_print (DbArrayofIdxRef v, gboolean type_annotate)
+{
+  GString *s = g_string_new ("");
+  db_arrayof_idx_format (v, s, type_annotate);
+  return g_string_free (s, FALSE);
+}
+
+/************** DbLocation *******************/
+#define DB_LOCATION_TYPESTRING "((ss)ssm(dd)ssm(q)ym(q)(q)a(q)a(q))"
+#define DB_LOCATION_TYPEFORMAT ((const GVariantType *) DB_LOCATION_TYPESTRING)
+
+typedef struct {
+ gconstpointer base;
+ gsize size;
+} DbLocationRef;
+
+
+static inline DbLocationRef
+db_location_from_gvariant (GVariant *v)
+{
+  g_assert (g_variant_type_equal (g_variant_get_type (v), DB_LOCATION_TYPESTRING));
+  return (DbLocationRef) { g_variant_get_data (v), g_variant_get_size (v) };
+}
+
+static inline DbLocationRef
+db_location_from_bytes (GBytes *b)
+{
+  return (DbLocationRef) { g_bytes_get_data (b, NULL), g_bytes_get_size (b) };
+}
+
+static inline DbLocationRef
+db_location_from_data (gconstpointer data, gsize size)
+{
+  return (DbLocationRef) { data, size };
+}
+
+static inline GVariant *
+db_location_dup_to_gvariant (DbLocationRef v)
+{
+  return g_variant_new_from_data (DB_LOCATION_TYPEFORMAT, g_memdup (v.base, v.size), v.size, TRUE, g_free, 
NULL);
+}
+
+static inline GVariant *
+db_location_to_gvariant (DbLocationRef v,
+                             GDestroyNotify      notify,
+                             gpointer            user_data)
+{
+  return g_variant_new_from_data (DB_LOCATION_TYPEFORMAT, v.base, v.size, TRUE, notify, user_data);
+}
+
+static inline GVariant *
+db_location_to_owned_gvariant (DbLocationRef v, GVariant *base)
+{
+  return db_location_to_gvariant (v, (GDestroyNotify)g_variant_unref, g_variant_ref (base));
+}
+
+static inline GVariant *
+db_location_peek_as_gvariant (DbLocationRef v)
+{
+  return g_variant_new_from_data (DB_LOCATION_TYPEFORMAT, v.base, v.size, TRUE, NULL, NULL);
+}
+
+static inline DbLocationRef
+db_location_from_variant (DbVariantRef v)
+{
+  const GVariantType  *type;
+  DbRef child = db_variant_get_child (v, &type);
+  g_assert (g_variant_type_equal(type, DB_LOCATION_TYPESTRING));
+  return db_location_from_data (child.base, child.size);
+}
+
+#define DB_LOCATION_INDEXOF_NAME 0
+
+static inline DbI18nRef
+db_location_get_name (DbLocationRef v)
+{
+  guint offset_size = db_ref_get_offset_size (v.size);
+  guint offset = ((0) & (~(gsize)0)) + 0;
+  gsize start = offset;
+  gsize end = DB_REF_READ_FRAME_OFFSET(v, 0);
+  g_assert (start <= end);
+  g_assert (end <= v.size);
+  return (DbI18nRef) { G_STRUCT_MEMBER_P(v.base, start), end - start };
+}
+
+#define DB_LOCATION_INDEXOF_FORECAST_ZONE 1
+
+static inline const char *
+db_location_get_forecast_zone (DbLocationRef v)
+{
+  guint offset_size = db_ref_get_offset_size (v.size);
+  gsize last_end = DB_REF_READ_FRAME_OFFSET(v, 0);
+  guint offset = ((last_end + 0) & (~(gsize)0)) + 0;
+  const char *base = (const char *)v.base;
+  gsize start = offset;
+  G_GNUC_UNUSED gsize end = DB_REF_READ_FRAME_OFFSET(v, 1);
+  g_assert (start <= end);
+  g_assert (end <= v.size);
+  g_assert (base[end-1] == 0);
+  return &G_STRUCT_MEMBER(const char, v.base, start);
+}
+
+#define DB_LOCATION_INDEXOF_RADAR 2
+
+static inline const char *
+db_location_get_radar (DbLocationRef v)
+{
+  guint offset_size = db_ref_get_offset_size (v.size);
+  gsize last_end = DB_REF_READ_FRAME_OFFSET(v, 1);
+  guint offset = ((last_end + 0) & (~(gsize)0)) + 0;
+  const char *base = (const char *)v.base;
+  gsize start = offset;
+  G_GNUC_UNUSED gsize end = DB_REF_READ_FRAME_OFFSET(v, 2);
+  g_assert (start <= end);
+  g_assert (end <= v.size);
+  g_assert (base[end-1] == 0);
+  return &G_STRUCT_MEMBER(const char, v.base, start);
+}
+
+#define DB_LOCATION_INDEXOF_COORDINATES 3
+
+static inline DbMaybeCoordinateRef
+db_location_get_coordinates (DbLocationRef v)
+{
+  guint offset_size = db_ref_get_offset_size (v.size);
+  gsize last_end = DB_REF_READ_FRAME_OFFSET(v, 2);
+  guint offset = ((last_end + 7) & (~(gsize)7)) + 0;
+  gsize start = offset;
+  gsize end = DB_REF_READ_FRAME_OFFSET(v, 3);
+  g_assert (start <= end);
+  g_assert (end <= v.size);
+  return (DbMaybeCoordinateRef) { G_STRUCT_MEMBER_P(v.base, start), end - start };
+}
+
+#define DB_LOCATION_INDEXOF_COUNTRY_CODE 4
+
+static inline const char *
+db_location_get_country_code (DbLocationRef v)
+{
+  guint offset_size = db_ref_get_offset_size (v.size);
+  gsize last_end = DB_REF_READ_FRAME_OFFSET(v, 3);
+  guint offset = ((last_end + 0) & (~(gsize)0)) + 0;
+  const char *base = (const char *)v.base;
+  gsize start = offset;
+  G_GNUC_UNUSED gsize end = DB_REF_READ_FRAME_OFFSET(v, 4);
+  g_assert (start <= end);
+  g_assert (end <= v.size);
+  g_assert (base[end-1] == 0);
+  return &G_STRUCT_MEMBER(const char, v.base, start);
+}
+
+#define DB_LOCATION_INDEXOF_METAR_CODE 5
+
+static inline const char *
+db_location_get_metar_code (DbLocationRef v)
+{
+  guint offset_size = db_ref_get_offset_size (v.size);
+  gsize last_end = DB_REF_READ_FRAME_OFFSET(v, 4);
+  guint offset = ((last_end + 0) & (~(gsize)0)) + 0;
+  const char *base = (const char *)v.base;
+  gsize start = offset;
+  G_GNUC_UNUSED gsize end = DB_REF_READ_FRAME_OFFSET(v, 5);
+  g_assert (start <= end);
+  g_assert (end <= v.size);
+  g_assert (base[end-1] == 0);
+  return &G_STRUCT_MEMBER(const char, v.base, start);
+}
+
+#define DB_LOCATION_INDEXOF_TZ_HINT 6
+
+static inline DbMaybeIdxRef
+db_location_get_tz_hint (DbLocationRef v)
+{
+  guint offset_size = db_ref_get_offset_size (v.size);
+  gsize last_end = DB_REF_READ_FRAME_OFFSET(v, 5);
+  guint offset = ((last_end + 1) & (~(gsize)1)) + 0;
+  gsize start = offset;
+  gsize end = DB_REF_READ_FRAME_OFFSET(v, 6);
+  g_assert (start <= end);
+  g_assert (end <= v.size);
+  return (DbMaybeIdxRef) { G_STRUCT_MEMBER_P(v.base, start), end - start };
+}
+
+#define DB_LOCATION_INDEXOF_LEVEL 7
+
+static inline guint8
+db_location_get_level (DbLocationRef v)
+{
+  guint offset_size = db_ref_get_offset_size (v.size);
+  gsize last_end = DB_REF_READ_FRAME_OFFSET(v, 6);
+  guint offset = ((last_end + 0) & (~(gsize)0)) + 0;
+  g_assert (offset + 1 < v.size);
+  return (guint8)G_STRUCT_MEMBER(guint8, v.base, offset);
+}
+
+#define DB_LOCATION_INDEXOF_NEAREST 8
+
+static inline DbMaybeIdxRef
+db_location_get_nearest (DbLocationRef v)
+{
+  guint offset_size = db_ref_get_offset_size (v.size);
+  gsize last_end = DB_REF_READ_FRAME_OFFSET(v, 6);
+  guint offset = ((last_end + 2) & (~(gsize)1)) + 0;
+  gsize start = offset;
+  gsize end = DB_REF_READ_FRAME_OFFSET(v, 7);
+  g_assert (start <= end);
+  g_assert (end <= v.size);
+  return (DbMaybeIdxRef) { G_STRUCT_MEMBER_P(v.base, start), end - start };
+}
+
+#define DB_LOCATION_INDEXOF_PARENT 9
+
+static inline DbIdxRef
+db_location_get_parent (DbLocationRef v)
+{
+  guint offset_size = db_ref_get_offset_size (v.size);
+  gsize last_end = DB_REF_READ_FRAME_OFFSET(v, 7);
+  guint offset = ((last_end + 1) & (~(gsize)1)) + 0;
+  g_assert (offset + 2 < v.size);
+  return (DbIdxRef) { G_STRUCT_MEMBER_P(v.base, offset), 2 };
+}
+
+static inline const DbIdx *
+db_location_peek_parent (DbLocationRef v) {
+  return (DbIdx *)db_location_get_parent (v).base;
+}
+
+#define DB_LOCATION_INDEXOF_CHILDREN 10
+
+static inline DbArrayofIdxRef
+db_location_get_children (DbLocationRef v)
+{
+  guint offset_size = db_ref_get_offset_size (v.size);
+  gsize last_end = DB_REF_READ_FRAME_OFFSET(v, 7);
+  guint offset = ((last_end + 1) & (~(gsize)1)) + 2;
+  gsize start = offset;
+  gsize end = DB_REF_READ_FRAME_OFFSET(v, 8);
+  g_assert (start <= end);
+  g_assert (end <= v.size);
+  return (DbArrayofIdxRef) { G_STRUCT_MEMBER_P(v.base, start), end - start };
+}
+
+static inline const DbIdx *
+db_location_peek_children (DbLocationRef v, gsize *len) {
+  DbArrayofIdxRef a = db_location_get_children (v);
+  if (len != NULL)
+    *len = db_arrayof_idx_get_length (a);
+  return (const DbIdx *)a.base;
+}
+
+#define DB_LOCATION_INDEXOF_TIMEZONES 11
+
+static inline DbArrayofIdxRef
+db_location_get_timezones (DbLocationRef v)
+{
+  guint offset_size = db_ref_get_offset_size (v.size);
+  gsize last_end = DB_REF_READ_FRAME_OFFSET(v, 8);
+  guint offset = ((last_end + 1) & (~(gsize)1)) + 0;
+  gsize start = offset;
+  gsize end = v.size - offset_size * 9;
+  g_assert (start <= end);
+  g_assert (end <= v.size);
+  return (DbArrayofIdxRef) { G_STRUCT_MEMBER_P(v.base, start), end - start };
+}
+
+static inline const DbIdx *
+db_location_peek_timezones (DbLocationRef v, gsize *len) {
+  DbArrayofIdxRef a = db_location_get_timezones (v);
+  if (len != NULL)
+    *len = db_arrayof_idx_get_length (a);
+  return (const DbIdx *)a.base;
+}
+
+static inline GString *
+db_location_format (DbLocationRef v, GString *s, gboolean type_annotate)
+{
+  g_string_append (s, "(");
+  db_i18n_format (db_location_get_name (v), s, type_annotate);
+  g_string_append (s, ", ");
+  __db_gstring_append_string (s, db_location_get_forecast_zone (v));
+  g_string_append (s, ", ");
+  __db_gstring_append_string (s, db_location_get_radar (v));
+  g_string_append (s, ", ");
+  db_maybe_coordinate_format (db_location_get_coordinates (v), s, type_annotate);
+  g_string_append (s, ", ");
+  __db_gstring_append_string (s, db_location_get_country_code (v));
+  g_string_append (s, ", ");
+  __db_gstring_append_string (s, db_location_get_metar_code (v));
+  g_string_append (s, ", ");
+  db_maybe_idx_format (db_location_get_tz_hint (v), s, type_annotate);
+  g_string_append (s, ", ");
+  g_string_append_printf (s, "%s0x%02x, ",
+                   type_annotate ? "byte " : "",
+                   db_location_get_level (v));
+  db_maybe_idx_format (db_location_get_nearest (v), s, type_annotate);
+  g_string_append (s, ", ");
+  db_idx_format (db_location_get_parent (v), s, type_annotate);
+  g_string_append (s, ", ");
+  db_arrayof_idx_format (db_location_get_children (v), s, type_annotate);
+  g_string_append (s, ", ");
+  db_arrayof_idx_format (db_location_get_timezones (v), s, type_annotate);
+  g_string_append (s, ")");
+  return s;
+}
+
+static inline char *
+db_location_print (DbLocationRef v, gboolean type_annotate)
+{
+  GString *s = g_string_new ("");
+  db_location_format (v, s, type_annotate);
+  return g_string_free (s, FALSE);
+}
+
+/************** DbWorldLocByCountry *******************/
+#define DB_WORLD_LOC_BY_COUNTRY_TYPESTRING "a{s(q)}"
+#define DB_WORLD_LOC_BY_COUNTRY_TYPEFORMAT ((const GVariantType *) DB_WORLD_LOC_BY_COUNTRY_TYPESTRING)
+
+typedef struct {
+ gconstpointer base;
+ gsize size;
+} DbWorldLocByCountryRef;
+
+typedef struct {
+ gconstpointer base;
+ gsize size;
+} DbWorldLocByCountryEntryRef;
+
+
+static inline DbWorldLocByCountryRef
+db_world_loc_by_country_from_gvariant (GVariant *v)
+{
+  g_assert (g_variant_type_equal (g_variant_get_type (v), DB_WORLD_LOC_BY_COUNTRY_TYPESTRING));
+  return (DbWorldLocByCountryRef) { g_variant_get_data (v), g_variant_get_size (v) };
+}
+
+static inline DbWorldLocByCountryRef
+db_world_loc_by_country_from_bytes (GBytes *b)
+{
+  return (DbWorldLocByCountryRef) { g_bytes_get_data (b, NULL), g_bytes_get_size (b) };
+}
+
+static inline DbWorldLocByCountryRef
+db_world_loc_by_country_from_data (gconstpointer data, gsize size)
+{
+  return (DbWorldLocByCountryRef) { data, size };
+}
+
+static inline GVariant *
+db_world_loc_by_country_dup_to_gvariant (DbWorldLocByCountryRef v)
+{
+  return g_variant_new_from_data (DB_WORLD_LOC_BY_COUNTRY_TYPEFORMAT, g_memdup (v.base, v.size), v.size, 
TRUE, g_free, NULL);
+}
+
+static inline GVariant *
+db_world_loc_by_country_to_gvariant (DbWorldLocByCountryRef v,
+                             GDestroyNotify      notify,
+                             gpointer            user_data)
+{
+  return g_variant_new_from_data (DB_WORLD_LOC_BY_COUNTRY_TYPEFORMAT, v.base, v.size, TRUE, notify, 
user_data);
+}
+
+static inline GVariant *
+db_world_loc_by_country_to_owned_gvariant (DbWorldLocByCountryRef v, GVariant *base)
+{
+  return db_world_loc_by_country_to_gvariant (v, (GDestroyNotify)g_variant_unref, g_variant_ref (base));
+}
+
+static inline GVariant *
+db_world_loc_by_country_peek_as_gvariant (DbWorldLocByCountryRef v)
+{
+  return g_variant_new_from_data (DB_WORLD_LOC_BY_COUNTRY_TYPEFORMAT, v.base, v.size, TRUE, NULL, NULL);
+}
+
+static inline DbWorldLocByCountryRef
+db_world_loc_by_country_from_variant (DbVariantRef v)
+{
+  const GVariantType  *type;
+  DbRef child = db_variant_get_child (v, &type);
+  g_assert (g_variant_type_equal(type, DB_WORLD_LOC_BY_COUNTRY_TYPESTRING));
+  return db_world_loc_by_country_from_data (child.base, child.size);
+}
+
+
+static inline gsize
+db_world_loc_by_country_get_length (DbWorldLocByCountryRef v)
+{
+  if (v.size == 0)
+    return 0;
+  guint offset_size = db_ref_get_offset_size (v.size);
+  gsize last_end = DB_REF_READ_FRAME_OFFSET(v, 0);
+  gsize offsets_array_size;
+  if (last_end > v.size)
+    return 0;
+  offsets_array_size = v.size - last_end;
+  if (offsets_array_size % offset_size != 0)
+    return 0;
+  gsize length = offsets_array_size / offset_size;
+  return length;
+}
+
+static inline DbWorldLocByCountryEntryRef
+db_world_loc_by_country_get_at (DbWorldLocByCountryRef v, gsize index)
+{
+  DbWorldLocByCountryEntryRef res;
+  guint offset_size = db_ref_get_offset_size (v.size);
+  gsize last_end = DB_REF_READ_FRAME_OFFSET(v, 0);
+  gsize len = (v.size - last_end) / offset_size;
+  gsize start = (index > 0) ? DB_REF_ALIGN(DB_REF_READ_FRAME_OFFSET(v, len - index), 2) : 0;
+  gsize end = DB_REF_READ_FRAME_OFFSET(v, len - index - 1);
+  g_assert (start <= end);
+  g_assert (end <= last_end);
+  res = (DbWorldLocByCountryEntryRef) { ((const char *)v.base) + start, end - start };
+  return res;
+}
+
+static inline const char *
+db_world_loc_by_country_entry_get_key (DbWorldLocByCountryEntryRef v)
+{
+  guint offset_size = db_ref_get_offset_size (v.size);
+  G_GNUC_UNUSED gsize end = DB_REF_READ_FRAME_OFFSET(v, 0);
+  const char *base = (const char *)v.base;
+  g_assert (end < v.size);
+  g_assert (base[end-1] == 0);
+  return base;
+}
+
+static inline DbIdxRef
+db_world_loc_by_country_entry_get_value (DbWorldLocByCountryEntryRef v)
+{
+  guint offset_size = db_ref_get_offset_size (v.size);
+  gsize end = DB_REF_READ_FRAME_OFFSET(v, 0);
+  gsize offset = DB_REF_ALIGN(end, 2);
+  g_assert (offset == v.size - offset_size - 2);
+  return (DbIdxRef) { (char *)v.base + offset, (v.size - offset_size) - offset };
+}
+
+static inline gboolean
+db_world_loc_by_country_lookup (DbWorldLocByCountryRef v, const char * key, gsize *index_out, DbIdxRef *out)
+{
+  const char * canonical_key = key;
+  guint offset_size = db_ref_get_offset_size (v.size);
+  gsize last_end = DB_REF_READ_FRAME_OFFSET(v, 0);
+  if (last_end > v.size)
+    return FALSE;
+  gsize offsets_array_size = v.size - last_end;
+  if (offsets_array_size % offset_size != 0)
+    return FALSE;
+  gsize len = offsets_array_size / offset_size;
+  gsize start = 0;
+  gsize end = len;
+
+  while (start < end)
+    {
+      gsize mid = (end + start) / 2;
+      gsize mid_end = DB_REF_READ_FRAME_OFFSET(v, len - mid - 1);
+      gsize mid_start = mid == 0 ? 0 : DB_REF_ALIGN(DB_REF_READ_FRAME_OFFSET(v, len - mid), 2);
+      g_assert (mid_start <= mid_end);
+      g_assert (mid_end <= last_end);
+      DbWorldLocByCountryEntryRef e = { ((const char *)v.base) + mid_start, mid_end - mid_start };
+      const char * e_key = db_world_loc_by_country_entry_get_key (e);
+      gint32 cmp = strcmp(canonical_key, e_key);
+      if (cmp == 0)
+        {
+           if (index_out)
+             *index_out = mid;
+           if (out)
+             *out = db_world_loc_by_country_entry_get_value (e);
+           return TRUE;
+        }
+      if (cmp < 0)
+        end = mid; /* canonical_key < e_key */
+      else
+        start = mid + 1; /* canonical_key > e_key */
+    }
+    return FALSE;
+}
+
+static inline GString *
+db_world_loc_by_country_format (DbWorldLocByCountryRef v, GString *s, gboolean type_annotate)
+{
+  gsize len = db_world_loc_by_country_get_length (v);
+  gsize i;
+
+  if (len == 0 && type_annotate)
+    g_string_append_printf (s, "@%s ", DB_WORLD_LOC_BY_COUNTRY_TYPESTRING);
+
+  g_string_append_c (s, '{');
+  for (i = 0; i < len; i++)
+    {
+      DbWorldLocByCountryEntryRef entry = db_world_loc_by_country_get_at (v, i);
+      if (i != 0)
+        g_string_append (s, ", ");
+      __db_gstring_append_string (s, db_world_loc_by_country_entry_get_key (entry));
+      g_string_append (s, ": ");
+      db_idx_format (db_world_loc_by_country_entry_get_value (entry), s, type_annotate);
+    }
+  g_string_append_c (s, '}');
+  return s;
+}
+
+static inline char *
+db_world_loc_by_country_print (DbWorldLocByCountryRef v, gboolean type_annotate)
+{
+  GString *s = g_string_new ("");
+  db_world_loc_by_country_format (v, s, type_annotate);
+  return g_string_free (s, FALSE);
+}
+
+/************** DbWorldLocByMetar *******************/
+#define DB_WORLD_LOC_BY_METAR_TYPESTRING "a{s(q)}"
+#define DB_WORLD_LOC_BY_METAR_TYPEFORMAT ((const GVariantType *) DB_WORLD_LOC_BY_METAR_TYPESTRING)
+
+typedef struct {
+ gconstpointer base;
+ gsize size;
+} DbWorldLocByMetarRef;
+
+typedef struct {
+ gconstpointer base;
+ gsize size;
+} DbWorldLocByMetarEntryRef;
+
+
+static inline DbWorldLocByMetarRef
+db_world_loc_by_metar_from_gvariant (GVariant *v)
+{
+  g_assert (g_variant_type_equal (g_variant_get_type (v), DB_WORLD_LOC_BY_METAR_TYPESTRING));
+  return (DbWorldLocByMetarRef) { g_variant_get_data (v), g_variant_get_size (v) };
+}
+
+static inline DbWorldLocByMetarRef
+db_world_loc_by_metar_from_bytes (GBytes *b)
+{
+  return (DbWorldLocByMetarRef) { g_bytes_get_data (b, NULL), g_bytes_get_size (b) };
+}
+
+static inline DbWorldLocByMetarRef
+db_world_loc_by_metar_from_data (gconstpointer data, gsize size)
+{
+  return (DbWorldLocByMetarRef) { data, size };
+}
+
+static inline GVariant *
+db_world_loc_by_metar_dup_to_gvariant (DbWorldLocByMetarRef v)
+{
+  return g_variant_new_from_data (DB_WORLD_LOC_BY_METAR_TYPEFORMAT, g_memdup (v.base, v.size), v.size, TRUE, 
g_free, NULL);
+}
+
+static inline GVariant *
+db_world_loc_by_metar_to_gvariant (DbWorldLocByMetarRef v,
+                             GDestroyNotify      notify,
+                             gpointer            user_data)
+{
+  return g_variant_new_from_data (DB_WORLD_LOC_BY_METAR_TYPEFORMAT, v.base, v.size, TRUE, notify, user_data);
+}
+
+static inline GVariant *
+db_world_loc_by_metar_to_owned_gvariant (DbWorldLocByMetarRef v, GVariant *base)
+{
+  return db_world_loc_by_metar_to_gvariant (v, (GDestroyNotify)g_variant_unref, g_variant_ref (base));
+}
+
+static inline GVariant *
+db_world_loc_by_metar_peek_as_gvariant (DbWorldLocByMetarRef v)
+{
+  return g_variant_new_from_data (DB_WORLD_LOC_BY_METAR_TYPEFORMAT, v.base, v.size, TRUE, NULL, NULL);
+}
+
+static inline DbWorldLocByMetarRef
+db_world_loc_by_metar_from_variant (DbVariantRef v)
+{
+  const GVariantType  *type;
+  DbRef child = db_variant_get_child (v, &type);
+  g_assert (g_variant_type_equal(type, DB_WORLD_LOC_BY_METAR_TYPESTRING));
+  return db_world_loc_by_metar_from_data (child.base, child.size);
+}
+
+
+static inline gsize
+db_world_loc_by_metar_get_length (DbWorldLocByMetarRef v)
+{
+  if (v.size == 0)
+    return 0;
+  guint offset_size = db_ref_get_offset_size (v.size);
+  gsize last_end = DB_REF_READ_FRAME_OFFSET(v, 0);
+  gsize offsets_array_size;
+  if (last_end > v.size)
+    return 0;
+  offsets_array_size = v.size - last_end;
+  if (offsets_array_size % offset_size != 0)
+    return 0;
+  gsize length = offsets_array_size / offset_size;
+  return length;
+}
+
+static inline DbWorldLocByMetarEntryRef
+db_world_loc_by_metar_get_at (DbWorldLocByMetarRef v, gsize index)
+{
+  DbWorldLocByMetarEntryRef res;
+  guint offset_size = db_ref_get_offset_size (v.size);
+  gsize last_end = DB_REF_READ_FRAME_OFFSET(v, 0);
+  gsize len = (v.size - last_end) / offset_size;
+  gsize start = (index > 0) ? DB_REF_ALIGN(DB_REF_READ_FRAME_OFFSET(v, len - index), 2) : 0;
+  gsize end = DB_REF_READ_FRAME_OFFSET(v, len - index - 1);
+  g_assert (start <= end);
+  g_assert (end <= last_end);
+  res = (DbWorldLocByMetarEntryRef) { ((const char *)v.base) + start, end - start };
+  return res;
+}
+
+static inline const char *
+db_world_loc_by_metar_entry_get_key (DbWorldLocByMetarEntryRef v)
+{
+  guint offset_size = db_ref_get_offset_size (v.size);
+  G_GNUC_UNUSED gsize end = DB_REF_READ_FRAME_OFFSET(v, 0);
+  const char *base = (const char *)v.base;
+  g_assert (end < v.size);
+  g_assert (base[end-1] == 0);
+  return base;
+}
+
+static inline DbIdxRef
+db_world_loc_by_metar_entry_get_value (DbWorldLocByMetarEntryRef v)
+{
+  guint offset_size = db_ref_get_offset_size (v.size);
+  gsize end = DB_REF_READ_FRAME_OFFSET(v, 0);
+  gsize offset = DB_REF_ALIGN(end, 2);
+  g_assert (offset == v.size - offset_size - 2);
+  return (DbIdxRef) { (char *)v.base + offset, (v.size - offset_size) - offset };
+}
+
+static inline gboolean
+db_world_loc_by_metar_lookup (DbWorldLocByMetarRef v, const char * key, gsize *index_out, DbIdxRef *out)
+{
+  const char * canonical_key = key;
+  guint offset_size = db_ref_get_offset_size (v.size);
+  gsize last_end = DB_REF_READ_FRAME_OFFSET(v, 0);
+  if (last_end > v.size)
+    return FALSE;
+  gsize offsets_array_size = v.size - last_end;
+  if (offsets_array_size % offset_size != 0)
+    return FALSE;
+  gsize len = offsets_array_size / offset_size;
+  gsize start = 0;
+  gsize end = len;
+
+  while (start < end)
+    {
+      gsize mid = (end + start) / 2;
+      gsize mid_end = DB_REF_READ_FRAME_OFFSET(v, len - mid - 1);
+      gsize mid_start = mid == 0 ? 0 : DB_REF_ALIGN(DB_REF_READ_FRAME_OFFSET(v, len - mid), 2);
+      g_assert (mid_start <= mid_end);
+      g_assert (mid_end <= last_end);
+      DbWorldLocByMetarEntryRef e = { ((const char *)v.base) + mid_start, mid_end - mid_start };
+      const char * e_key = db_world_loc_by_metar_entry_get_key (e);
+      gint32 cmp = strcmp(canonical_key, e_key);
+      if (cmp == 0)
+        {
+           if (index_out)
+             *index_out = mid;
+           if (out)
+             *out = db_world_loc_by_metar_entry_get_value (e);
+           return TRUE;
+        }
+      if (cmp < 0)
+        end = mid; /* canonical_key < e_key */
+      else
+        start = mid + 1; /* canonical_key > e_key */
+    }
+    return FALSE;
+}
+
+static inline GString *
+db_world_loc_by_metar_format (DbWorldLocByMetarRef v, GString *s, gboolean type_annotate)
+{
+  gsize len = db_world_loc_by_metar_get_length (v);
+  gsize i;
+
+  if (len == 0 && type_annotate)
+    g_string_append_printf (s, "@%s ", DB_WORLD_LOC_BY_METAR_TYPESTRING);
+
+  g_string_append_c (s, '{');
+  for (i = 0; i < len; i++)
+    {
+      DbWorldLocByMetarEntryRef entry = db_world_loc_by_metar_get_at (v, i);
+      if (i != 0)
+        g_string_append (s, ", ");
+      __db_gstring_append_string (s, db_world_loc_by_metar_entry_get_key (entry));
+      g_string_append (s, ": ");
+      db_idx_format (db_world_loc_by_metar_entry_get_value (entry), s, type_annotate);
+    }
+  g_string_append_c (s, '}');
+  return s;
+}
+
+static inline char *
+db_world_loc_by_metar_print (DbWorldLocByMetarRef v, gboolean type_annotate)
+{
+  GString *s = g_string_new ("");
+  db_world_loc_by_metar_format (v, s, type_annotate);
+  return g_string_free (s, FALSE);
+}
+
+/************** DbWorldTimezones *******************/
+#define DB_WORLD_TIMEZONES_TYPESTRING "a{s((ss)as)}"
+#define DB_WORLD_TIMEZONES_TYPEFORMAT ((const GVariantType *) DB_WORLD_TIMEZONES_TYPESTRING)
+
+typedef struct {
+ gconstpointer base;
+ gsize size;
+} DbWorldTimezonesRef;
+
+typedef struct {
+ gconstpointer base;
+ gsize size;
+} DbWorldTimezonesEntryRef;
+
+
+static inline DbWorldTimezonesRef
+db_world_timezones_from_gvariant (GVariant *v)
+{
+  g_assert (g_variant_type_equal (g_variant_get_type (v), DB_WORLD_TIMEZONES_TYPESTRING));
+  return (DbWorldTimezonesRef) { g_variant_get_data (v), g_variant_get_size (v) };
+}
+
+static inline DbWorldTimezonesRef
+db_world_timezones_from_bytes (GBytes *b)
+{
+  return (DbWorldTimezonesRef) { g_bytes_get_data (b, NULL), g_bytes_get_size (b) };
+}
+
+static inline DbWorldTimezonesRef
+db_world_timezones_from_data (gconstpointer data, gsize size)
+{
+  return (DbWorldTimezonesRef) { data, size };
+}
+
+static inline GVariant *
+db_world_timezones_dup_to_gvariant (DbWorldTimezonesRef v)
+{
+  return g_variant_new_from_data (DB_WORLD_TIMEZONES_TYPEFORMAT, g_memdup (v.base, v.size), v.size, TRUE, 
g_free, NULL);
+}
+
+static inline GVariant *
+db_world_timezones_to_gvariant (DbWorldTimezonesRef v,
+                             GDestroyNotify      notify,
+                             gpointer            user_data)
+{
+  return g_variant_new_from_data (DB_WORLD_TIMEZONES_TYPEFORMAT, v.base, v.size, TRUE, notify, user_data);
+}
+
+static inline GVariant *
+db_world_timezones_to_owned_gvariant (DbWorldTimezonesRef v, GVariant *base)
+{
+  return db_world_timezones_to_gvariant (v, (GDestroyNotify)g_variant_unref, g_variant_ref (base));
+}
+
+static inline GVariant *
+db_world_timezones_peek_as_gvariant (DbWorldTimezonesRef v)
+{
+  return g_variant_new_from_data (DB_WORLD_TIMEZONES_TYPEFORMAT, v.base, v.size, TRUE, NULL, NULL);
+}
+
+static inline DbWorldTimezonesRef
+db_world_timezones_from_variant (DbVariantRef v)
+{
+  const GVariantType  *type;
+  DbRef child = db_variant_get_child (v, &type);
+  g_assert (g_variant_type_equal(type, DB_WORLD_TIMEZONES_TYPESTRING));
+  return db_world_timezones_from_data (child.base, child.size);
+}
+
+
+static inline gsize
+db_world_timezones_get_length (DbWorldTimezonesRef v)
+{
+  if (v.size == 0)
+    return 0;
+  guint offset_size = db_ref_get_offset_size (v.size);
+  gsize last_end = DB_REF_READ_FRAME_OFFSET(v, 0);
+  gsize offsets_array_size;
+  if (last_end > v.size)
+    return 0;
+  offsets_array_size = v.size - last_end;
+  if (offsets_array_size % offset_size != 0)
+    return 0;
+  gsize length = offsets_array_size / offset_size;
+  return length;
+}
+
+static inline DbWorldTimezonesEntryRef
+db_world_timezones_get_at (DbWorldTimezonesRef v, gsize index)
+{
+  DbWorldTimezonesEntryRef res;
+  guint offset_size = db_ref_get_offset_size (v.size);
+  gsize last_end = DB_REF_READ_FRAME_OFFSET(v, 0);
+  gsize len = (v.size - last_end) / offset_size;
+  gsize start = (index > 0) ? DB_REF_ALIGN(DB_REF_READ_FRAME_OFFSET(v, len - index), 1) : 0;
+  gsize end = DB_REF_READ_FRAME_OFFSET(v, len - index - 1);
+  g_assert (start <= end);
+  g_assert (end <= last_end);
+  res = (DbWorldTimezonesEntryRef) { ((const char *)v.base) + start, end - start };
+  return res;
+}
+
+static inline const char *
+db_world_timezones_entry_get_key (DbWorldTimezonesEntryRef v)
+{
+  guint offset_size = db_ref_get_offset_size (v.size);
+  G_GNUC_UNUSED gsize end = DB_REF_READ_FRAME_OFFSET(v, 0);
+  const char *base = (const char *)v.base;
+  g_assert (end < v.size);
+  g_assert (base[end-1] == 0);
+  return base;
+}
+
+static inline DbTimezoneRef
+db_world_timezones_entry_get_value (DbWorldTimezonesEntryRef v)
+{
+  guint offset_size = db_ref_get_offset_size (v.size);
+  gsize end = DB_REF_READ_FRAME_OFFSET(v, 0);
+  gsize offset = DB_REF_ALIGN(end, 1);
+  g_assert (offset <= v.size);
+  return (DbTimezoneRef) { (char *)v.base + offset, (v.size - offset_size) - offset };
+}
+
+static inline gboolean
+db_world_timezones_lookup (DbWorldTimezonesRef v, const char * key, gsize *index_out, DbTimezoneRef *out)
+{
+  const char * canonical_key = key;
+  guint offset_size = db_ref_get_offset_size (v.size);
+  gsize last_end = DB_REF_READ_FRAME_OFFSET(v, 0);
+  if (last_end > v.size)
+    return FALSE;
+  gsize offsets_array_size = v.size - last_end;
+  if (offsets_array_size % offset_size != 0)
+    return FALSE;
+  gsize len = offsets_array_size / offset_size;
+  gsize start = 0;
+  gsize end = len;
+
+  while (start < end)
+    {
+      gsize mid = (end + start) / 2;
+      gsize mid_end = DB_REF_READ_FRAME_OFFSET(v, len - mid - 1);
+      gsize mid_start = mid == 0 ? 0 : DB_REF_ALIGN(DB_REF_READ_FRAME_OFFSET(v, len - mid), 1);
+      g_assert (mid_start <= mid_end);
+      g_assert (mid_end <= last_end);
+      DbWorldTimezonesEntryRef e = { ((const char *)v.base) + mid_start, mid_end - mid_start };
+      const char * e_key = db_world_timezones_entry_get_key (e);
+      gint32 cmp = strcmp(canonical_key, e_key);
+      if (cmp == 0)
+        {
+           if (index_out)
+             *index_out = mid;
+           if (out)
+             *out = db_world_timezones_entry_get_value (e);
+           return TRUE;
+        }
+      if (cmp < 0)
+        end = mid; /* canonical_key < e_key */
+      else
+        start = mid + 1; /* canonical_key > e_key */
+    }
+    return FALSE;
+}
+
+static inline GString *
+db_world_timezones_format (DbWorldTimezonesRef v, GString *s, gboolean type_annotate)
+{
+  gsize len = db_world_timezones_get_length (v);
+  gsize i;
+
+  if (len == 0 && type_annotate)
+    g_string_append_printf (s, "@%s ", DB_WORLD_TIMEZONES_TYPESTRING);
+
+  g_string_append_c (s, '{');
+  for (i = 0; i < len; i++)
+    {
+      DbWorldTimezonesEntryRef entry = db_world_timezones_get_at (v, i);
+      if (i != 0)
+        g_string_append (s, ", ");
+      __db_gstring_append_string (s, db_world_timezones_entry_get_key (entry));
+      g_string_append (s, ": ");
+      db_timezone_format (db_world_timezones_entry_get_value (entry), s, type_annotate);
+    }
+  g_string_append_c (s, '}');
+  return s;
+}
+
+static inline char *
+db_world_timezones_print (DbWorldTimezonesRef v, gboolean type_annotate)
+{
+  GString *s = g_string_new ("");
+  db_world_timezones_format (v, s, type_annotate);
+  return g_string_free (s, FALSE);
+}
+
+/************** DbArrayofLocation *******************/
+#define DB_ARRAYOF_LOCATION_TYPESTRING "a((ss)ssm(dd)ssm(q)ym(q)(q)a(q)a(q))"
+#define DB_ARRAYOF_LOCATION_TYPEFORMAT ((const GVariantType *) DB_ARRAYOF_LOCATION_TYPESTRING)
+
+typedef struct {
+ gconstpointer base;
+ gsize size;
+} DbArrayofLocationRef;
+
+
+static inline DbArrayofLocationRef
+db_arrayof_location_from_gvariant (GVariant *v)
+{
+  g_assert (g_variant_type_equal (g_variant_get_type (v), DB_ARRAYOF_LOCATION_TYPESTRING));
+  return (DbArrayofLocationRef) { g_variant_get_data (v), g_variant_get_size (v) };
+}
+
+static inline DbArrayofLocationRef
+db_arrayof_location_from_bytes (GBytes *b)
+{
+  return (DbArrayofLocationRef) { g_bytes_get_data (b, NULL), g_bytes_get_size (b) };
+}
+
+static inline DbArrayofLocationRef
+db_arrayof_location_from_data (gconstpointer data, gsize size)
+{
+  return (DbArrayofLocationRef) { data, size };
+}
+
+static inline GVariant *
+db_arrayof_location_dup_to_gvariant (DbArrayofLocationRef v)
+{
+  return g_variant_new_from_data (DB_ARRAYOF_LOCATION_TYPEFORMAT, g_memdup (v.base, v.size), v.size, TRUE, 
g_free, NULL);
+}
+
+static inline GVariant *
+db_arrayof_location_to_gvariant (DbArrayofLocationRef v,
+                             GDestroyNotify      notify,
+                             gpointer            user_data)
+{
+  return g_variant_new_from_data (DB_ARRAYOF_LOCATION_TYPEFORMAT, v.base, v.size, TRUE, notify, user_data);
+}
+
+static inline GVariant *
+db_arrayof_location_to_owned_gvariant (DbArrayofLocationRef v, GVariant *base)
+{
+  return db_arrayof_location_to_gvariant (v, (GDestroyNotify)g_variant_unref, g_variant_ref (base));
+}
+
+static inline GVariant *
+db_arrayof_location_peek_as_gvariant (DbArrayofLocationRef v)
+{
+  return g_variant_new_from_data (DB_ARRAYOF_LOCATION_TYPEFORMAT, v.base, v.size, TRUE, NULL, NULL);
+}
+
+static inline DbArrayofLocationRef
+db_arrayof_location_from_variant (DbVariantRef v)
+{
+  const GVariantType  *type;
+  DbRef child = db_variant_get_child (v, &type);
+  g_assert (g_variant_type_equal(type, DB_ARRAYOF_LOCATION_TYPESTRING));
+  return db_arrayof_location_from_data (child.base, child.size);
+}
+
+static inline gsize
+db_arrayof_location_get_length (DbArrayofLocationRef v)
+{
+  if (v.size == 0)
+    return 0;
+  guint offset_size = db_ref_get_offset_size (v.size);
+  gsize last_end = DB_REF_READ_FRAME_OFFSET(v, 0);
+  gsize offsets_array_size;
+  if (last_end > v.size)
+    return 0;
+  offsets_array_size = v.size - last_end;
+  if (offsets_array_size % offset_size != 0)
+    return 0;
+  gsize length  = offsets_array_size / offset_size;
+  return length;
+}
+
+static inline DbLocationRef
+db_arrayof_location_get_at (DbArrayofLocationRef v, gsize index)
+{
+  guint offset_size = db_ref_get_offset_size (v.size);
+  gsize last_end = DB_REF_READ_FRAME_OFFSET(v, 0);
+  gsize len = (v.size - last_end) / offset_size;
+  gsize start = (index > 0) ? DB_REF_ALIGN(DB_REF_READ_FRAME_OFFSET(v, len - index), 8) : 0;
+  G_GNUC_UNUSED gsize end = DB_REF_READ_FRAME_OFFSET(v, len - index - 1);
+  g_assert (start <= end);
+  g_assert (end <= last_end);
+  return (DbLocationRef) { ((const char *)v.base) + start, end - start };
+}
+
+static inline GString *
+db_arrayof_location_format (DbArrayofLocationRef v, GString *s, gboolean type_annotate)
+{
+  gsize len = db_arrayof_location_get_length (v);
+  gsize i;
+  if (len == 0 && type_annotate)
+    g_string_append_printf (s, "@%s ", DB_ARRAYOF_LOCATION_TYPESTRING);
+  g_string_append_c (s, '[');
+  for (i = 0; i < len; i++)
+    {
+      if (i != 0)
+        g_string_append (s, ", ");
+      db_location_format (db_arrayof_location_get_at (v, i), s, ((i == 0) ? type_annotate : FALSE));
+    }
+  g_string_append_c (s, ']');
+  return s;
+}
+
+static inline char *
+db_arrayof_location_print (DbArrayofLocationRef v, gboolean type_annotate)
+{
+  GString *s = g_string_new ("");
+  db_arrayof_location_format (v, s, type_annotate);
+  return g_string_free (s, FALSE);
+}
+
+/************** DbWorld *******************/
+#define DB_WORLD_TYPESTRING "(a{s(q)}a{s(q)}a{s((ss)as)}a((ss)ssm(dd)ssm(q)ym(q)(q)a(q)a(q)))"
+#define DB_WORLD_TYPEFORMAT ((const GVariantType *) DB_WORLD_TYPESTRING)
+
+typedef struct {
+ gconstpointer base;
+ gsize size;
+} DbWorldRef;
+
+
+static inline DbWorldRef
+db_world_from_gvariant (GVariant *v)
+{
+  g_assert (g_variant_type_equal (g_variant_get_type (v), DB_WORLD_TYPESTRING));
+  return (DbWorldRef) { g_variant_get_data (v), g_variant_get_size (v) };
+}
+
+static inline DbWorldRef
+db_world_from_bytes (GBytes *b)
+{
+  return (DbWorldRef) { g_bytes_get_data (b, NULL), g_bytes_get_size (b) };
+}
+
+static inline DbWorldRef
+db_world_from_data (gconstpointer data, gsize size)
+{
+  return (DbWorldRef) { data, size };
+}
+
+static inline GVariant *
+db_world_dup_to_gvariant (DbWorldRef v)
+{
+  return g_variant_new_from_data (DB_WORLD_TYPEFORMAT, g_memdup (v.base, v.size), v.size, TRUE, g_free, 
NULL);
+}
+
+static inline GVariant *
+db_world_to_gvariant (DbWorldRef v,
+                             GDestroyNotify      notify,
+                             gpointer            user_data)
+{
+  return g_variant_new_from_data (DB_WORLD_TYPEFORMAT, v.base, v.size, TRUE, notify, user_data);
+}
+
+static inline GVariant *
+db_world_to_owned_gvariant (DbWorldRef v, GVariant *base)
+{
+  return db_world_to_gvariant (v, (GDestroyNotify)g_variant_unref, g_variant_ref (base));
+}
+
+static inline GVariant *
+db_world_peek_as_gvariant (DbWorldRef v)
+{
+  return g_variant_new_from_data (DB_WORLD_TYPEFORMAT, v.base, v.size, TRUE, NULL, NULL);
+}
+
+static inline DbWorldRef
+db_world_from_variant (DbVariantRef v)
+{
+  const GVariantType  *type;
+  DbRef child = db_variant_get_child (v, &type);
+  g_assert (g_variant_type_equal(type, DB_WORLD_TYPESTRING));
+  return db_world_from_data (child.base, child.size);
+}
+
+#define DB_WORLD_INDEXOF_LOC_BY_COUNTRY 0
+
+static inline DbWorldLocByCountryRef
+db_world_get_loc_by_country (DbWorldRef v)
+{
+  guint offset_size = db_ref_get_offset_size (v.size);
+  guint offset = ((1) & (~(gsize)1)) + 0;
+  gsize start = offset;
+  gsize end = DB_REF_READ_FRAME_OFFSET(v, 0);
+  g_assert (start <= end);
+  g_assert (end <= v.size);
+  return (DbWorldLocByCountryRef) { G_STRUCT_MEMBER_P(v.base, start), end - start };
+}
+
+#define DB_WORLD_INDEXOF_LOC_BY_METAR 1
+
+static inline DbWorldLocByMetarRef
+db_world_get_loc_by_metar (DbWorldRef v)
+{
+  guint offset_size = db_ref_get_offset_size (v.size);
+  gsize last_end = DB_REF_READ_FRAME_OFFSET(v, 0);
+  guint offset = ((last_end + 1) & (~(gsize)1)) + 0;
+  gsize start = offset;
+  gsize end = DB_REF_READ_FRAME_OFFSET(v, 1);
+  g_assert (start <= end);
+  g_assert (end <= v.size);
+  return (DbWorldLocByMetarRef) { G_STRUCT_MEMBER_P(v.base, start), end - start };
+}
+
+#define DB_WORLD_INDEXOF_TIMEZONES 2
+
+static inline DbWorldTimezonesRef
+db_world_get_timezones (DbWorldRef v)
+{
+  guint offset_size = db_ref_get_offset_size (v.size);
+  gsize last_end = DB_REF_READ_FRAME_OFFSET(v, 1);
+  guint offset = ((last_end + 0) & (~(gsize)0)) + 0;
+  gsize start = offset;
+  gsize end = DB_REF_READ_FRAME_OFFSET(v, 2);
+  g_assert (start <= end);
+  g_assert (end <= v.size);
+  return (DbWorldTimezonesRef) { G_STRUCT_MEMBER_P(v.base, start), end - start };
+}
+
+#define DB_WORLD_INDEXOF_LOCATIONS 3
+
+static inline DbArrayofLocationRef
+db_world_get_locations (DbWorldRef v)
+{
+  guint offset_size = db_ref_get_offset_size (v.size);
+  gsize last_end = DB_REF_READ_FRAME_OFFSET(v, 2);
+  guint offset = ((last_end + 7) & (~(gsize)7)) + 0;
+  gsize start = offset;
+  gsize end = v.size - offset_size * 3;
+  g_assert (start <= end);
+  g_assert (end <= v.size);
+  return (DbArrayofLocationRef) { G_STRUCT_MEMBER_P(v.base, start), end - start };
+}
+
+static inline GString *
+db_world_format (DbWorldRef v, GString *s, gboolean type_annotate)
+{
+  g_string_append (s, "(");
+  db_world_loc_by_country_format (db_world_get_loc_by_country (v), s, type_annotate);
+  g_string_append (s, ", ");
+  db_world_loc_by_metar_format (db_world_get_loc_by_metar (v), s, type_annotate);
+  g_string_append (s, ", ");
+  db_world_timezones_format (db_world_get_timezones (v), s, type_annotate);
+  g_string_append (s, ", ");
+  db_arrayof_location_format (db_world_get_locations (v), s, type_annotate);
+  g_string_append (s, ")");
+  return s;
+}
+
+static inline char *
+db_world_print (DbWorldRef v, gboolean type_annotate)
+{
+  GString *s = g_string_new ("");
+  db_world_format (v, s, type_annotate);
+  return g_string_free (s, FALSE);
+}
+#endif
diff --git a/libgweather/gweather-location.c b/libgweather/gweather-location.c
index 1477e51..bf9e174 100644
--- a/libgweather/gweather-location.c
+++ b/libgweather/gweather-location.c
@@ -30,9 +30,7 @@
 #include <libxml/xmlreader.h>
 #include <geocode-glib/geocode-glib.h>
 
-#include "gweather-location.h"
 #include "gweather-timezone.h"
-#include "gweather-parser.h"
 #include "gweather-private.h"
 
 /* This is the precision of coordinates in the database */
@@ -112,21 +110,6 @@ sort_locations_by_distance (gconstpointer a, gconstpointer b, gpointer user_data
        return 0;
 }
 
-static gboolean
-parse_coordinates (const char *coordinates,
-                  double *latitude, double *longitude)
-{
-    char *p;
-
-    *latitude = g_ascii_strtod (coordinates, &p) * M_PI / 180.0;
-    if (p == (char *)coordinates)
-       return FALSE;
-    if (*p++ != ' ')
-       return FALSE;
-    *longitude = g_ascii_strtod (p, &p) * M_PI / 180.0;
-    return !*p;
-}
-
 static GWeatherLocation *
 location_new (GWeatherLocationLevel level)
 {
@@ -142,6 +125,7 @@ location_new (GWeatherLocationLevel level)
 
 static void add_timezones (GWeatherLocation *loc, GPtrArray *zones);
 
+#if 0
 static void
 add_nearest_weather_station (GWeatherLocation *location)
 {
@@ -204,7 +188,7 @@ add_nearest_weather_station (GWeatherLocation *location)
     station->parent = location;
     station->level = GWEATHER_LOCATION_WEATHER_STATION;
     station->country_code = g_strdup (closest->country_code);
-    station->tz_hint = g_strdup (closest->tz_hint);
+    station->tz_hint_idx = closest->tz_hint_idx;
     station->station_code = g_strdup (closest->station_code);
     station->forecast_zone = g_strdup (closest->forecast_zone);
     station->radar = g_strdup (closest->radar);
@@ -219,259 +203,210 @@ add_nearest_weather_station (GWeatherLocation *location)
 
     station->ref_count = 1;
 }
+#endif
 
-static void
-add_nearest_weather_stations (GWeatherLocation *location)
+static GWeatherLocation *
+location_ref_for_idx (GWeatherDb       *db,
+                     guint             idx,
+                     GWeatherLocation *nearest_of)
 {
-    GWeatherLocation **children;
-    guint i;
+    GWeatherLocation *loc;
+    DbLocationRef ref;
+    char *normalized;
 
-    /* For each city without a <location>, add the nearest airport in the
-     * same country or state to it */
-    children = gweather_location_get_children (location);
-    for (i = 0; children[i] != NULL; i++) {
-        if (gweather_location_get_level (children[i]) == GWEATHER_LOCATION_CITY)
-            add_nearest_weather_station (children[i]);
-        else
-            add_nearest_weather_stations (children[i]);
+    g_assert (db);
+    g_assert (idx < db->locations->len);
+    if (!nearest_of) {
+       loc = g_ptr_array_index (db->locations, idx);
+       if (loc) {
+           return gweather_location_ref (loc);
+       }
     }
-}
 
-static GWeatherLocation *
-location_new_from_xml (GWeatherParser *parser, GWeatherLocationLevel level,
-                      GWeatherLocation *parent)
-{
-    GWeatherLocation *loc, *child;
-    GPtrArray *children = NULL;
-    const char *tagname;
-    char *value, *normalized;
-    int tagtype;
-    unsigned int i;
-
-    loc = location_new (level);
-    loc->parent = parent;
-    if (level == GWEATHER_LOCATION_WORLD) {
-       loc->metar_code_cache = g_hash_table_ref (parser->metar_code_cache);
-       loc->country_code_cache = g_hash_table_ref (parser->country_code_cache);
-       loc->timezone_cache = g_hash_table_ref (parser->timezone_cache);
-    }
-    children = g_ptr_array_new ();
-
-    if (xmlTextReaderRead (parser->xml) != 1)
-       goto error_out;
-    while ((tagtype = xmlTextReaderNodeType (parser->xml)) !=
-          XML_READER_TYPE_END_ELEMENT) {
-       if (tagtype != XML_READER_TYPE_ELEMENT) {
-           if (xmlTextReaderRead (parser->xml) != 1)
-               goto error_out;
-           continue;
+    ref = db_arrayof_location_get_at (db->locations_ref, idx);
+    loc = location_new (db_location_get_level (ref));
+    loc->db = db;
+    loc->db_idx = idx;
+    loc->ref = ref;
+
+    loc->english_name = g_strdup (EMPTY_TO_NULL (db_i18n_get_str (db_location_get_name (ref))));
+    loc->msgctxt = g_strdup (EMPTY_TO_NULL (db_i18n_get_msgctxt (db_location_get_name (ref))));
+
+    if (loc->english_name) {
+       if (loc->msgctxt) {
+           loc->local_name = g_strdup (g_dpgettext2 ("libgweather-locations",
+                                                     loc->msgctxt, loc->english_name));
+       } else {
+           loc->local_name = g_strdup (g_dgettext ("libgweather-locations", loc->english_name));
        }
 
-       tagname = (const char *) xmlTextReaderConstName (parser->xml);
-       if ((!strcmp (tagname, "name") || !strcmp (tagname, "_name")) && !loc->english_name) {
-            loc->msgctxt = _gweather_parser_get_msgctxt_value (parser);
-           value = _gweather_parser_get_value (parser);
-           if (!value)
-               goto error_out;
+       normalized = g_utf8_normalize (loc->local_name, -1, G_NORMALIZE_ALL);
+       loc->local_sort_name = g_utf8_casefold (normalized, -1);
+       g_free (normalized);
 
-           loc->english_name = g_strdup (value);
+       normalized = g_utf8_normalize (loc->english_name, -1, G_NORMALIZE_ALL);
+       loc->english_sort_name = g_utf8_casefold (normalized, -1);
+       g_free (normalized);
+    }
 
-           if (loc->msgctxt) {
-               loc->local_name = g_strdup (g_dpgettext2 ("libgweather-locations",
-                                                         (char*) loc->msgctxt, value));
-           } else {
-               loc->local_name = g_strdup (g_dgettext ("libgweather-locations", value));
-           }
+    loc->country_code = g_strdup (EMPTY_TO_NULL (db_location_get_country_code (ref)));
+    if (db_maybe_idx_has_value (db_location_get_tz_hint (ref)))
+        loc->tz_hint_idx = db_idx_get_idx (db_maybe_idx_get_value (db_location_get_tz_hint (ref)));
+    else
+       loc->tz_hint_idx = -1;
 
-           normalized = g_utf8_normalize (loc->local_name, -1, G_NORMALIZE_ALL);
-           loc->local_sort_name = g_utf8_casefold (normalized, -1);
-           g_free (normalized);
-
-           normalized = g_utf8_normalize (loc->english_name, -1, G_NORMALIZE_ALL);
-           loc->english_sort_name = g_utf8_casefold (normalized, -1);
-           g_free (normalized);
-           xmlFree (value);
-       } else if (!strcmp (tagname, "iso-code") && !loc->country_code) {
-           value = _gweather_parser_get_value (parser);
-           if (!value)
-               goto error_out;
-           loc->country_code = g_strdup (value);
-           xmlFree (value);
-       } else if (!strcmp (tagname, "tz-hint") && !loc->tz_hint) {
-           value = _gweather_parser_get_value (parser);
-           if (!value)
-               goto error_out;
-           loc->tz_hint = g_strdup (value);
-           xmlFree (value);
-       } else if (!strcmp (tagname, "code") && !loc->station_code) {
-           value = _gweather_parser_get_value (parser);
-           if (!value)
-               goto error_out;
-           loc->station_code = g_strdup (value);
-           xmlFree (value);
-       } else if (!strcmp (tagname, "coordinates") && !loc->latlon_valid) {
-           value = _gweather_parser_get_value (parser);
-           if (!value)
-               goto error_out;
-           if (parse_coordinates (value, &loc->latitude, &loc->longitude))
-               loc->latlon_valid = TRUE;
-           else
-               g_warning ("Coordinates could not be parsed: '%s'", value);
-           xmlFree (value);
-       } else if (!strcmp (tagname, "zone") && !loc->forecast_zone) {
-           value = _gweather_parser_get_value (parser);
-           if (!value)
-               goto error_out;
-           loc->forecast_zone = g_strdup (value);
-           xmlFree (value);
-       } else if (!strcmp (tagname, "radar") && !loc->radar) {
-           value = _gweather_parser_get_value (parser);
-           if (!value)
-               goto error_out;
-           loc->radar = g_strdup (value);
-           xmlFree (value);
-
-       } else if (!strcmp (tagname, "region")) {
-           child = location_new_from_xml (parser, GWEATHER_LOCATION_REGION, loc);
-           if (!child)
-               goto error_out;
-           g_ptr_array_add (children, child);
-       } else if (!strcmp (tagname, "country")) {
-           child = location_new_from_xml (parser, GWEATHER_LOCATION_COUNTRY, loc);
-           if (!child)
-               goto error_out;
-           g_ptr_array_add (children, child);
-       } else if (!strcmp (tagname, "state")) {
-           child = location_new_from_xml (parser, GWEATHER_LOCATION_ADM1, loc);
-           if (!child)
-               goto error_out;
-           g_ptr_array_add (children, child);
-       } else if (!strcmp (tagname, "city")) {
-           child = location_new_from_xml (parser, GWEATHER_LOCATION_CITY, loc);
-           if (!child)
-               goto error_out;
-           g_ptr_array_add (children, child);
-       } else if (!strcmp (tagname, "location")) {
-           child = location_new_from_xml (parser, GWEATHER_LOCATION_WEATHER_STATION, loc);
-           if (!child)
-               goto error_out;
-           g_ptr_array_add (children, child);
-
-       } else if (!strcmp (tagname, "named-timezone")) {
-           child = location_new_from_xml (parser, GWEATHER_LOCATION_NAMED_TIMEZONE, loc);
-           if (!child)
-               goto error_out;
-           g_ptr_array_add (children, child);
-
-       } else if (!strcmp (tagname, "timezones")) {
-           loc->zones = _gweather_timezones_parse_xml (parser);
-           if (!loc->zones)
-               goto error_out;
+    loc->station_code = g_strdup (EMPTY_TO_NULL (db_location_get_metar_code (ref)));
 
-       } else {
-           if (xmlTextReaderNext (parser->xml) != 1)
-               goto error_out;
-       }
-    }
-    if (xmlTextReaderRead (parser->xml) != 1 && parent)
-       goto error_out;
-
-    if (level == GWEATHER_LOCATION_WEATHER_STATION ||
-       level == GWEATHER_LOCATION_NAMED_TIMEZONE) {
-       /* Cache weather stations by METAR code */
-       GList *a, *b;
-
-       a = g_hash_table_lookup (parser->metar_code_cache, loc->station_code);
-       b = g_list_append (a, gweather_location_ref (loc));
-       if (b != a)
-           g_hash_table_replace (parser->metar_code_cache, loc->station_code, b);
+    loc->latlon_valid = db_maybe_coordinate_has_value (db_location_get_coordinates (ref));
+    if (loc->latlon_valid) {
+       loc->latitude = db_coordinate_get_lat (db_maybe_coordinate_get_value (db_location_get_coordinates 
(ref)));
+       loc->longitude = db_coordinate_get_lon (db_maybe_coordinate_get_value (db_location_get_coordinates 
(ref)));
     }
 
-    if (level == GWEATHER_LOCATION_COUNTRY) {
-       if (loc->country_code) {
-           GWeatherLocation *existing;
+    loc->forecast_zone = g_strdup (EMPTY_TO_NULL (db_location_get_forecast_zone (ref)));
+    loc->radar = g_strdup (EMPTY_TO_NULL (db_location_get_radar (ref)));
 
-           existing = g_hash_table_lookup (parser->country_code_cache, loc->country_code);
-           if (existing)
-               g_warning ("A country with country code '%s' is already in the database",
-                          loc->country_code);
-           g_hash_table_replace (parser->country_code_cache, loc->country_code,
-                                 gweather_location_ref (loc));
-       }
+    /* Fill in the magic nearest child if we need to and should. */
+    if (!g_getenv ("LIBGWEATHER_LOCATIONS_NO_NEAREST") &&
+        db_maybe_idx_has_value (db_location_get_nearest (ref))) {
+       guint nearest_idx = db_idx_get_idx (db_maybe_idx_get_value (db_location_get_nearest (ref)));
+
+       loc->children = g_new0 (GWeatherLocation*, 2);
+       loc->children[0] = location_ref_for_idx (db, nearest_idx, loc);
     }
 
-    if (children->len) {
-       if (level == GWEATHER_LOCATION_CITY)
-           g_ptr_array_sort_with_data (children, sort_locations_by_distance, loc);
-       else
-           g_ptr_array_sort (children, sort_locations_by_name);
+    /* Note, we used to sort locations by distance (for cities) and name;
+     * Distance sorting is done in the variant already,
+     * name sorting however needs translations and is not done anymore. */
 
-       g_ptr_array_add (children, NULL);
-       loc->children = (GWeatherLocation **)g_ptr_array_free (children, FALSE);
-    } else
-       g_ptr_array_free (children, TRUE);
+    /* Add a weak reference to the cache or the parent in the case of nearest_of . */
+    if (!nearest_of)
+        g_ptr_array_index (db->locations, idx) = loc;
+    else
+       loc->parent = nearest_of;
 
     return loc;
-
-error_out:
-    gweather_location_unref (loc);
-    for (i = 0; i < children->len; i++)
-       gweather_location_unref (children->pdata[i]);
-    g_ptr_array_free (children, TRUE);
-
-    return NULL;
 }
 
-static GWeatherLocation *global_world = NULL;
+static GWeatherDb *world_db;
 
 static void _gweather_location_unref_no_check (GWeatherLocation *loc);
 
 GWEATHER_EXTERN void
 _gweather_location_reset_world (void)
 {
-       g_clear_pointer (&global_world, _gweather_location_unref_no_check);
+       g_return_if_fail (world_db);
+
+       /* Clear objects that need to be kept alive for the old API. */
+       g_ptr_array_set_size (world_db->locations_keepalive, 0);
+       g_ptr_array_set_size (world_db->timezones_keepalive, 0);
+}
+
+static GWeatherLocation *
+location_sink_keep_alive (GWeatherLocation *loc)
+{
+       g_assert (loc->db);
+
+       if (g_ptr_array_find (loc->db->locations_keepalive, loc, NULL)) {
+               gweather_location_unref (loc);
+               return loc;
+       }
+
+       g_ptr_array_add (loc->db->locations_keepalive, loc);
+
+       return loc;
 }
 
 /**
- * gweather_location_get_world:
+ * gweather_location_ref_world:
  *
  * Obtains the shared #GWeatherLocation of type %GWEATHER_LOCATION_WORLD,
- * representing a hierarchy containing all of the locations from
- * Locations.xml.
+ * representing a hierarchy containing all of the known locations.
  *
- * Return value: (allow-none) (transfer none): a %GWEATHER_LOCATION_WORLD
+ * Return value: (allow-none) (transfer full): a %GWEATHER_LOCATION_WORLD
  * location, or %NULL if Locations.xml could not be found or could not be parsed.
- * The return value is owned by libgweather and should not be modified or freed.
+ * The return value should be unref'ed after use.
  **/
 GWeatherLocation *
-gweather_location_get_world (void)
+gweather_location_ref_world ()
 {
-    GWeatherParser *parser;
+    g_autoptr(GError) error = NULL;
+    GMappedFile *map;
 
-    if (!global_world) {
+    if (!world_db) {
         const char *locations_path;
+        g_autofree char *filename = NULL;
+        time_t now;
+        struct tm tm;
 
         locations_path = g_getenv ("LIBGWEATHER_LOCATIONS_PATH");
         if (locations_path) {
-            parser = _gweather_parser_new_for_path (locations_path);
-            if (!parser) {
-                g_warning ("Failed to open '%s' as LIBGWEATHER_LOCATIONS_PATH",
-                           locations_path);
-                parser = _gweather_parser_new ();
-            }
-        } else {
-            parser = _gweather_parser_new ();
+            filename = g_strconcat (locations_path, ".bin", NULL);
+            if (!g_file_test (filename, G_FILE_TEST_IS_REGULAR))
+               g_clear_pointer (&filename, g_free);
         }
-       if (!parser)
+
+        if (!filename)
+           filename = g_build_filename (GWEATHER_XML_LOCATION_DIR, "Locations.xml.bin", NULL);
+
+       map = g_mapped_file_new (filename, FALSE, &error);
+       if (!map) {
+           g_warning ("Faile to open database %s: %s", filename, error->message);
            return NULL;
+       }
+
+       world_db = g_new0 (GWeatherDb, 1);
+       world_db->map = map;
+       world_db->world = db_world_from_data (g_mapped_file_get_contents (map), g_mapped_file_get_length 
(map));
+
+       world_db->locations_keepalive = g_ptr_array_new_with_free_func ((GDestroyNotify) 
_gweather_location_unref_no_check);
+       world_db->timezones_keepalive = g_ptr_array_new_with_free_func ((GDestroyNotify) 
gweather_timezone_unref);
 
-       global_world = location_new_from_xml (parser, GWEATHER_LOCATION_WORLD, NULL);
-       if (!g_getenv ("LIBGWEATHER_LOCATIONS_NO_NEAREST"))
-           add_nearest_weather_stations (global_world);
-       _gweather_parser_free (parser);
+       world_db->locations_ref = db_world_get_locations (world_db->world);
+       world_db->timezones_ref = db_world_get_timezones (world_db->world);
+
+       world_db->locations = g_ptr_array_new ();
+       world_db->timezones = g_ptr_array_new ();
+
+       g_ptr_array_set_size (world_db->locations, db_arrayof_location_get_length (world_db->locations_ref));
+       g_ptr_array_set_size (world_db->timezones, db_world_timezones_get_length (world_db->timezones_ref));
+
+       /* Get timestamps for the start and end of this year */
+       now = time (NULL);
+       tm = *gmtime (&now);
+       tm.tm_mon = 0;
+       tm.tm_mday = 1;
+       tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
+       world_db->year_start = mktime (&tm);
+       tm.tm_year++;
+       world_db->year_end = mktime (&tm);
     }
 
-    return global_world;
+    return location_ref_for_idx (world_db, 0, NULL);
+}
+
+/**
+ * gweather_location_get_world:
+ *
+ * Obtains the shared #GWeatherLocation of type %GWEATHER_LOCATION_WORLD,
+ * representing a hierarchy containing all of the locations from
+ * Locations.xml.
+ *
+ * Return value: (allow-none) (transfer none): a %GWEATHER_LOCATION_WORLD
+ * location, or %NULL if Locations.xml could not be found or could not be parsed.
+ * The return value is owned by libgweather and should not be modified or freed.
+ *
+ * Deprecated: XXX: Use gweather_location_ref_world() instead to avoid high
+ * memory consumptoin
+ **/
+GWeatherLocation *
+gweather_location_get_world (void)
+{
+    GWeatherLocation *loc;
+
+    loc = gweather_location_ref_world ();
+
+    return location_sink_keep_alive (loc);
 }
 
 /**
@@ -499,13 +434,17 @@ _gweather_location_unref_no_check (GWeatherLocation *loc)
     if (--loc->ref_count)
        return;
 
+    /* Remove weak reference from DB object; but only if it points to us.
+     * It may point elsewhere if we are an implicit nearest child. */
+    if (loc->db && g_ptr_array_index (loc->db->locations, loc->db_idx) == loc)
+        g_ptr_array_index (loc->db->locations, loc->db_idx) = NULL;
+
     g_free (loc->english_name);
     g_free (loc->local_name);
     g_free (loc->msgctxt);
     g_free (loc->local_sort_name);
     g_free (loc->english_sort_name);
     g_free (loc->country_code);
-    g_free (loc->tz_hint);
     g_free (loc->station_code);
     g_free (loc->forecast_zone);
     g_free (loc->radar);
@@ -524,12 +463,7 @@ _gweather_location_unref_no_check (GWeatherLocation *loc)
        g_free (loc->zones);
     }
 
-    if (loc->metar_code_cache)
-       g_hash_table_unref (loc->metar_code_cache);
-    if (loc->timezone_cache)
-       g_hash_table_unref (loc->timezone_cache);
-    if (loc->country_code_cache)
-       g_hash_table_unref (loc->country_code_cache);
+    g_clear_pointer (&loc->timezone, gweather_timezone_unref);
 
     g_slice_free (GWeatherLocation, loc);
 }
@@ -545,7 +479,7 @@ void
 gweather_location_unref (GWeatherLocation *loc)
 {
     g_return_if_fail (loc != NULL);
-    g_return_if_fail (loc->level != GWEATHER_LOCATION_WORLD || loc->ref_count > 1);
+    g_return_if_fail (loc->ref_count > 1 || (!loc->db || !g_ptr_array_find (loc->db->locations_keepalive, 
loc, NULL)));
 
     _gweather_location_unref_no_check (loc);
 }
@@ -668,6 +602,38 @@ gweather_location_level_to_string (GWeatherLocationLevel level)
     return NULL;
 }
 
+/**
+ * gweather_location_ref_parent:
+ * @loc: a #GWeatherLocation
+ *
+ * Gets @loc's parent location.
+ *
+ * Return value: (transfer full) (allow-none): @loc's parent, or %NULL
+ * if @loc is a %GWEATHER_LOCATION_WORLD node.
+ **/
+GWeatherLocation *
+gweather_location_ref_parent (GWeatherLocation *loc)
+{
+    guint16 idx;
+    g_return_val_if_fail (loc != NULL, NULL);
+
+    if (loc->parent)
+       return gweather_location_ref (loc->parent);
+
+    if (loc->level == GWEATHER_LOCATION_WORLD) {
+       return NULL;
+    }
+
+    /* No database or root object */
+    if (!loc->db || loc->db_idx == 0)
+       return NULL;
+
+    /* Not self-referencing */
+    idx = db_idx_get_idx (db_location_get_parent (loc->ref));
+    g_assert (idx != loc->db_idx);
+    return location_ref_for_idx (loc->db, idx, NULL);
+}
+
 /**
  * gweather_location_get_parent:
  * @loc: a #GWeatherLocation
@@ -676,11 +642,17 @@ gweather_location_level_to_string (GWeatherLocationLevel level)
  *
  * Return value: (transfer none) (allow-none): @loc's parent, or %NULL
  * if @loc is a %GWEATHER_LOCATION_WORLD node.
+ *
+ * Deprecated: XXX. Use gweather_location_ref_parent() instead
  **/
 GWeatherLocation *
 gweather_location_get_parent (GWeatherLocation *loc)
 {
     g_return_val_if_fail (loc != NULL, NULL);
+    if (loc->parent)
+       return loc->parent;
+
+    loc->parent = gweather_location_ref_parent (loc);
     return loc->parent;
 }
 
@@ -693,18 +665,37 @@ gweather_location_get_parent (GWeatherLocation *loc)
  *
  * Return value: (transfer none) (array zero-terminated=1): @loc's
  * children. (May be empty, but will not be %NULL.)
+ *
+ * Deprecated: XXX. Use XXX() instead?
  **/
 GWeatherLocation **
 gweather_location_get_children (GWeatherLocation *loc)
 {
     static GWeatherLocation *no_children = NULL;
+    DbArrayofIdxRef children_ref;
+    gsize length;
+    gsize i;
 
-    g_return_val_if_fail (loc != NULL, NULL);
+    g_return_val_if_fail (loc != NULL, &no_children);
 
     if (loc->children)
        return loc->children;
-    else
+
+    if (!loc->db)
+       return &no_children;
+
+    children_ref = db_location_get_children (loc->ref);
+    length = db_arrayof_idx_get_length (children_ref);
+    if (length == 0)
        return &no_children;
+
+    loc->children = g_new0 (GWeatherLocation*, length + 1);
+    for (i = 0; i < length; i++)
+       loc->children[i] = location_ref_for_idx (loc->db,
+                                                db_idx_get_idx (db_arrayof_idx_get_at (children_ref, i)),
+                                                NULL);
+
+    return loc->children;
 }
 
 static void
@@ -732,6 +723,24 @@ foreach_city (GWeatherLocation  *loc,
         int i;
         for (i = 0; loc->children[i]; i++)
             foreach_city (loc->children[i], callback, user_data, country_code, func, user_data_func);
+    } else if (loc->db) {
+       DbArrayofIdxRef children_ref;
+       gsize length;
+       gsize i;
+       /* Also try non-cached iteration */
+
+       children_ref = db_location_get_children (loc->ref);
+       length = db_arrayof_idx_get_length (children_ref);
+
+       for (i = 0; i < length; i++) {
+           g_autoptr(GWeatherLocation) child = NULL;
+
+           child = location_ref_for_idx (loc->db,
+                                         db_idx_get_idx (db_arrayof_idx_get_at (children_ref, i)),
+                                         NULL);
+
+           foreach_city (child, callback, user_data, country_code, func, user_data_func);
+       }
     }
 }
 
@@ -770,11 +779,15 @@ find_nearest_city (GWeatherLocation *location,
                   gpointer          user_data) {
     struct FindNearestCityData *data = user_data;
 
+    if (!location->latlon_valid)
+       return;
+
     double distance = location_distance (location->latitude, location->longitude,
                                         data->latitude, data->longitude);
 
     if (data->location == NULL || data->distance > distance) {
-       data->location = location;
+       g_clear_pointer (&data->location, gweather_location_unref);
+       data->location = gweather_location_ref (location);
        data->distance = distance;
     }
 }
@@ -824,7 +837,7 @@ gweather_location_find_nearest_city (GWeatherLocation *loc,
 
     foreach_city (loc, (GFunc) find_nearest_city, &data, NULL, NULL, NULL);
 
-    return gweather_location_ref (data.location);
+    return data.location;
 }
 
 /**
@@ -1074,11 +1087,22 @@ gweather_location_get_distance (GWeatherLocation *loc, GWeatherLocation *loc2)
 const char *
 gweather_location_get_country (GWeatherLocation *loc)
 {
+    g_autoptr(GWeatherLocation) s = NULL;
     g_return_val_if_fail (loc != NULL, NULL);
 
-    while (loc->parent && !loc->country_code)
-       loc = loc->parent;
-    return loc->country_code;
+    if (loc->country_code)
+       return loc->country_code;
+
+    s = gweather_location_ref (loc);
+    while (s && !s->country_code) {
+       GWeatherLocation *parent = gweather_location_ref_parent (s);
+       gweather_location_unref (s);
+       s = parent;
+    }
+    if (!s)
+       return NULL;
+
+    return s->country_code;
 }
 
 /**
@@ -1096,30 +1120,24 @@ gweather_location_get_country (GWeatherLocation *loc)
 GWeatherTimezone *
 gweather_location_get_timezone (GWeatherLocation *loc)
 {
-    const char *tz_hint;
-    int i;
+    g_autoptr(GWeatherLocation) s = NULL;
 
     g_return_val_if_fail (loc != NULL, NULL);
 
-    while (loc && !loc->tz_hint)
-       loc = loc->parent;
-    if (!loc)
-       return NULL;
-    tz_hint = loc->tz_hint;
+    if (loc->timezone)
+       return loc->timezone;
 
-    while (loc) {
-       while (loc && !loc->zones)
-           loc = loc->parent;
-       if (!loc)
-           return NULL;
-       for (i = 0; loc->zones[i]; i++) {
-           if (!strcmp (tz_hint, gweather_timezone_get_tzid (loc->zones[i])))
-               return loc->zones[i];
-       }
-       loc = loc->parent;
+    s = gweather_location_ref (loc);
+    while (s && s->tz_hint_idx < 0) {
+       GWeatherLocation *parent = gweather_location_ref_parent (s);
+       gweather_location_unref (s);
+       s = parent;
     }
+    if (!s)
+       return NULL;
 
-    return NULL;
+    loc->timezone = _gweather_timezone_ref_for_idx (s->db, s->tz_hint_idx);
+    return loc->timezone;
 }
 
 /**
@@ -1137,17 +1155,23 @@ gweather_location_get_timezone (GWeatherLocation *loc)
 const char *
 gweather_location_get_timezone_str (GWeatherLocation *loc)
 {
-    const char *tz_hint;
+    g_autoptr(GWeatherLocation) s = NULL;
 
     g_return_val_if_fail (loc != NULL, NULL);
 
-    while (loc && !loc->tz_hint)
-       loc = loc->parent;
-    if (!loc)
+    if (loc->timezone)
+       return gweather_timezone_get_tzid (loc->timezone);
+
+    s = gweather_location_ref (loc);
+    while (s && s->tz_hint_idx < 0) {
+       GWeatherLocation *parent = gweather_location_ref_parent (s);
+       gweather_location_unref (s);
+       s = parent;
+    }
+    if (!s)
        return NULL;
-    tz_hint = loc->tz_hint;
 
-    return tz_hint;
+    return db_world_timezones_entry_get_key (db_world_timezones_get_at (s->db->timezones_ref, 
s->tz_hint_idx));
 }
 
 static void
@@ -1289,24 +1313,28 @@ _gweather_location_update_weather_location (GWeatherLocation *gloc,
                                            WeatherLocation  *loc)
 {
     const char *code = NULL, *zone = NULL, *radar = NULL, *tz_hint = NULL, *country = NULL;
+    gint tz_hint_idx = -1;
     gboolean latlon_valid = FALSE;
     gdouble lat = DBL_MAX, lon = DBL_MAX;
     GWeatherLocation *l;
+    GWeatherDb *db = NULL;
 
     if (gloc->level == GWEATHER_LOCATION_CITY && gloc->children)
        l = gloc->children[0];
     else
        l = gloc;
 
-    while (l && (!code || !zone || !radar || !tz_hint || !latlon_valid || !country)) {
+    while (l && (!db || !code || !zone || !radar || tz_hint_idx < 0 || !latlon_valid || !country)) {
+       if (!db && l->db)
+           db = l->db;
        if (!code && l->station_code)
            code = l->station_code;
        if (!zone && l->forecast_zone)
            zone = l->forecast_zone;
        if (!radar && l->radar)
            radar = l->radar;
-       if (!tz_hint && l->tz_hint)
-           tz_hint = l->tz_hint;
+       if (tz_hint_idx < 0)
+           tz_hint_idx = l->tz_hint_idx;
        if (!country && l->country_code)
            country = l->country_code;
        if (!latlon_valid && l->latlon_valid) {
@@ -1322,13 +1350,46 @@ _gweather_location_update_weather_location (GWeatherLocation *gloc,
     loc->zone = g_strdup (zone);
     loc->radar = g_strdup (radar);
     loc->country_code = g_strdup (country);
-    loc->tz_hint = g_strdup (tz_hint);
+    if (tz_hint_idx >= 0)
+        loc->tz_hint = g_strdup (db_world_timezones_entry_get_key (db_world_timezones_get_at 
(db->timezones_ref, tz_hint_idx)));
 
     loc->latlon_valid = latlon_valid;
     loc->latitude = lat;
     loc->longitude = lon;
 }
 
+/**
+ * gweather_location_ref_from_station_code:
+ * @world: a #GWeatherLocation at the world level
+ * @station_code: a 4 letter METAR code
+ *
+ * Retrieves the weather station identifier by @station_code.
+ * Note that multiple instances of the same weather station can exist
+ * in the database, and this function will return any of them, so this
+ * not usually what you want.
+ *
+ * See gweather_location_deserialize() to recover a stored #GWeatherLocation.
+ *
+ * Returns: (transfer full): a weather station level #GWeatherLocation for @station_code,
+ *          or %NULL if none exists in the database.
+ */
+GWeatherLocation *
+gweather_location_ref_from_station_code (GWeatherLocation *world,
+                                        const gchar      *station_code)
+{
+       DbWorldLocByMetarRef loc_by_metar;
+       DbIdxRef idx_ref;
+
+       if (!world->db)
+               return NULL;
+
+       loc_by_metar = db_world_get_loc_by_metar (world->db->world);
+       if (!db_world_loc_by_metar_lookup (loc_by_metar, station_code, NULL, &idx_ref))
+               return NULL;
+
+       return location_ref_for_idx (world->db, db_idx_get_idx (idx_ref), NULL);
+}
+
 /**
  * gweather_location_find_by_station_code:
  * @world: a #GWeatherLocation at the world level
@@ -1343,15 +1404,14 @@ _gweather_location_update_weather_location (GWeatherLocation *gloc,
  *
  * Returns: (transfer none): a weather station level #GWeatherLocation for @station_code,
  *          or %NULL if none exists in the database.
+ *
+ * Deprecated: XXX: Use gweather_location_ref_from_station_code() instead.
  */
 GWeatherLocation *
 gweather_location_find_by_station_code (GWeatherLocation *world,
                                        const gchar      *station_code)
 {
-    GList *l;
-
-    l = g_hash_table_lookup (world->metar_code_cache, station_code);
-    return l ? l->data : NULL;
+       return location_sink_keep_alive (gweather_location_ref_from_station_code (world, station_code));
 }
 
 /**
@@ -1368,7 +1428,17 @@ GWeatherLocation *
 gweather_location_find_by_country_code (GWeatherLocation *world,
                                         const gchar      *country_code)
 {
-       return g_hash_table_lookup (world->country_code_cache, country_code);
+       DbWorldLocByCountryRef loc_by_country;
+       DbIdxRef idx_ref;
+
+       if (!world->db)
+               return NULL;
+
+       loc_by_country = db_world_get_loc_by_country (world->db->world);
+       if (!db_world_loc_by_country_lookup (loc_by_country, country_code, NULL, &idx_ref))
+               return NULL;
+
+       return location_sink_keep_alive (location_ref_for_idx (world->db, db_idx_get_idx (idx_ref), NULL));
 }
 
 /**
@@ -1442,7 +1512,7 @@ gweather_location_format_two_serialize (GWeatherLocation *location)
     GVariantBuilder latlon_builder;
     GVariantBuilder parent_latlon_builder;
 
-    name = location->english_name;
+    name = gweather_location_get_english_name (location);
 
     /* Normalize location to be a weather station or detached */
     if (location->level == GWEATHER_LOCATION_CITY) {
@@ -1476,9 +1546,7 @@ _gweather_location_new_detached (GWeatherLocation *nearest_station,
     GWeatherLocation *self;
     char *normalized;
 
-    self = g_slice_new0 (GWeatherLocation);
-    self->ref_count = 1;
-    self->level = GWEATHER_LOCATION_DETACHED;
+    self = location_new (GWEATHER_LOCATION_DETACHED);
     if (name != NULL) {
        self->english_name = g_strdup (name);
        self->local_name = g_strdup (name);
@@ -1494,6 +1562,7 @@ _gweather_location_new_detached (GWeatherLocation *nearest_station,
        self->local_sort_name = g_strdup (nearest_station->local_sort_name);
     }
 
+    self->tz_hint_idx = -1;
     self->parent = nearest_station;
     self->children = NULL;
 
@@ -1527,8 +1596,9 @@ gweather_location_common_deserialize (GWeatherLocation *world,
                                      gdouble           parent_latitude,
                                      gdouble           parent_longitude)
 {
-    GList *candidates, *l;
+    DbWorldLocByMetarRef loc_by_metar;
     GWeatherLocation *found;
+    gsize i;
 
     /* Since weather stations are no longer attached to cities, first try to
        find what claims to be a city by name and coordinates */
@@ -1536,6 +1606,7 @@ gweather_location_common_deserialize (GWeatherLocation *world,
         found = gweather_location_find_nearest_city (world,
                                                      latitude / M_PI * 180.0,
                                                      longitude / M_PI * 180.0);
+
         if (found && (g_strcmp0 (name, found->english_name) == 0 ||
                       g_strcmp0 (name, found->local_name) == 0))
            return g_steal_pointer (&found);
@@ -1545,28 +1616,33 @@ gweather_location_common_deserialize (GWeatherLocation *world,
     if (station_code[0] == '\0')
         return _gweather_location_new_detached (NULL, name, latlon_valid, latitude, longitude);
 
-    /* First find the list of candidate locations */
-    candidates = g_hash_table_lookup (world->metar_code_cache, station_code);
-
     /* A station code beginning with @ indicates a named timezone entry, just
      * return it directly
      */
     if (station_code[0] == '@')
-       return gweather_location_ref (candidates->data);
+       return gweather_location_ref_from_station_code (world, station_code);
+
+
 
     /* If we don't have coordinates, fallback immediately to making up
      * a location
      */
+    /* XXX: Return NULL if code not found? */
     if (!latlon_valid)
-       return candidates ? _gweather_location_new_detached (candidates->data, name, FALSE, 0, 0) : NULL;
+       return _gweather_location_new_detached (gweather_location_ref_from_station_code (world, station_code),
+                                               name, FALSE, 0, 0);
 
     found = NULL;
+    loc_by_metar = db_world_get_loc_by_metar (world->db->world);
 
-    /* First try weather stations directly. */
-    for (l = candidates; l; l = l->next) {
-       GWeatherLocation *ws, *city;
+    for (i = 0; i < db_world_loc_by_metar_get_length (loc_by_metar); i++) {
+       g_autoptr(GWeatherLocation) ws = NULL, city = NULL;
+       /* Skip if the metar code does not match */
+       if (!g_str_equal (station_code, db_world_loc_by_metar_entry_get_key (db_world_loc_by_metar_get_at 
(loc_by_metar, i))))
+           continue;
 
-       ws = l->data;
+       /* Be lazy and allocate the location */
+       ws = location_ref_for_idx (world->db, db_idx_get_idx (db_world_loc_by_metar_entry_get_value 
(db_world_loc_by_metar_get_at (loc_by_metar, i))), NULL);
 
        if (!ws->latlon_valid ||
            ABS(ws->latitude - latitude) >= EPSILON ||
@@ -1574,45 +1650,42 @@ gweather_location_common_deserialize (GWeatherLocation *world,
            /* Not what we're looking for... */
            continue;
 
+       city = gweather_location_ref_parent (ws);
+
        /* If we can't check for the latitude and longitude
           of the parent, we just assume we found what we needed
        */
-       if ((!parent_latlon_valid || !ws->parent || !ws->parent->latlon_valid) ||
-           (ABS(parent_latitude - ws->parent->latitude) < EPSILON &&
-            ABS(parent_longitude - ws->parent->longitude) < EPSILON)) {
+       if ((!parent_latlon_valid || !city || !city->latlon_valid) ||
+           (ABS(parent_latitude - city->latitude) < EPSILON &&
+            ABS(parent_longitude - city->longitude) < EPSILON)) {
 
            /* Found! Now check which one we want (ws or city) and the name... */
            if (is_city)
-               city = ws->parent;
+               found = city;
            else
-               city = ws;
+               found = ws;
 
-           if (city == NULL) {
+           if (found == NULL) {
                /* Oops! There is no city for this weather station! */
                continue;
            }
 
            if (name == NULL ||
-               g_strcmp0 (name, city->english_name) == 0 ||
-               g_strcmp0 (name, city->local_name) == 0)
-               found = gweather_location_ref (city);
+               g_str_equal (name, gweather_location_get_english_name (found)) ||
+               g_str_equal (name, gweather_location_get_name (found)))
+               found = gweather_location_ref (found);
            else
-               found = _gweather_location_new_detached (ws, name, TRUE, latitude, longitude);
+               found = _gweather_location_new_detached (gweather_location_ref (ws), name, TRUE, latitude, 
longitude);
 
-           break;
+           return found;
        }
     }
 
-    if (found)
-       return found;
-
     /* No weather station matches the serialized data, let's pick
        one at random from the station code list */
-    if (candidates)
-       return _gweather_location_new_detached (candidates->data,
-                                               name, TRUE, latitude, longitude);
-    else
-       return NULL;
+    /* XXX: Return NULL if code not found? */
+    return _gweather_location_new_detached (gweather_location_ref_from_station_code (world, station_code),
+                                           name, TRUE, latitude, longitude);
 }
 
 static GWeatherLocation *
diff --git a/libgweather/gweather-location.h b/libgweather/gweather-location.h
index b33afb5..30b794e 100644
--- a/libgweather/gweather-location.h
+++ b/libgweather/gweather-location.h
@@ -52,6 +52,8 @@ GType gweather_location_get_type (void);
 
 GWEATHER_EXTERN
 GWeatherLocation      *gweather_location_get_world      (void);
+GWEATHER_EXTERN
+GWeatherLocation      *gweather_location_ref_world      (void);
 
 GWEATHER_EXTERN
 GWeatherLocation      *gweather_location_ref            (GWeatherLocation  *loc);
@@ -124,6 +126,9 @@ char                  *gweather_location_get_city_name  (GWeatherLocation  *loc)
 GWEATHER_EXTERN
 char                  *gweather_location_get_country_name (GWeatherLocation  *loc);
 
+GWEATHER_EXTERN
+GWeatherLocation      *gweather_location_ref_from_station_code (GWeatherLocation *world,
+                                                                 const gchar      *station_code);
 GWEATHER_EXTERN
 GWeatherLocation      *gweather_location_find_by_station_code (GWeatherLocation *world,
                                                               const gchar      *station_code);
@@ -150,6 +155,9 @@ GWeatherLocation      *gweather_location_new_detached   (const char        *name
 GWEATHER_EXTERN
 const char            *gweather_location_level_to_string (GWeatherLocationLevel level);
 
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GWeatherLocation, gweather_location_unref)
+
 G_END_DECLS
 
 #endif /* __GWEATHER_LOCATIONS_H__ */
diff --git a/libgweather/gweather-private.h b/libgweather/gweather-private.h
index 925abd8..c4d6d0c 100644
--- a/libgweather/gweather-private.h
+++ b/libgweather/gweather-private.h
@@ -31,20 +31,54 @@
 #include "gweather-weather.h"
 #include "gweather-location.h"
 
+static inline gdouble
+DOUBLE_FROM_BE(gdouble x)
+{
+    gint64 tmp = *((gint64*) &x);
+    tmp = GINT64_FROM_BE(tmp);
+    x = *((double*) &tmp);
+    return x;
+}
+#include "gweather-db.h"
+
+#define EMPTY_TO_NULL(s) ((s)[0] == '\0' ? NULL : (s))
+
 void        _gweather_gettext_init (void);
 
+
+typedef struct {
+    GMappedFile *map;
+    DbWorldRef world;
+    DbArrayofLocationRef locations_ref;
+    DbWorldTimezonesRef timezones_ref;
+
+    GPtrArray *locations;
+    GPtrArray *timezones;
+
+    GPtrArray *locations_keepalive;
+    GPtrArray *timezones_keepalive;
+
+    time_t year_start;
+    time_t year_end;
+} GWeatherDb;
+
 struct _GWeatherLocation {
+    GWeatherDb *db;
+    guint       db_idx;
+    DbLocationRef ref;
+
     char *english_name, *local_name, *msgctxt, *local_sort_name, *english_sort_name;
     GWeatherLocation *parent, **children;
     GWeatherLocationLevel level;
-    char *country_code, *tz_hint;
+    char *country_code;
+    gint tz_hint_idx;
     char *station_code, *forecast_zone, *radar;
     double latitude, longitude;
     gboolean latlon_valid;
     GWeatherTimezone **zones;
-    GHashTable *metar_code_cache;
-    GHashTable *timezone_cache;
-    GHashTable *country_code_cache;
+
+    /* For old API emulation, i.e. holding on to objects */
+    GWeatherTimezone *timezone;
 
     int ref_count;
 };
@@ -72,6 +106,10 @@ GWeatherLocation *_gweather_location_new_detached (GWeatherLocation *nearest_sta
 void              _gweather_location_update_weather_location (GWeatherLocation *gloc,
                                                              WeatherLocation  *loc);
 
+GWeatherTimezone * _gweather_timezone_ref_for_idx (GWeatherDb       *db,
+                                                   guint             idx);
+
+
 /*
  * Weather information.
  */
diff --git a/libgweather/gweather-timezone.c b/libgweather/gweather-timezone.c
index 3916dc5..9216209 100644
--- a/libgweather/gweather-timezone.c
+++ b/libgweather/gweather-timezone.c
@@ -25,7 +25,6 @@
 #include <string.h>
 
 #include "gweather-timezone.h"
-#include "gweather-parser.h"
 #include "gweather-private.h"
 
 /**
@@ -41,6 +40,9 @@
  */
 
 struct _GWeatherTimezone {
+    GWeatherDb *db;
+    guint db_idx;
+
     char *id, *name;
     int offset, dst_offset;
     gboolean has_dst;
@@ -149,6 +151,62 @@ parse_tzdata (const char *tz_name, time_t start, time_t end,
     return TRUE;
 }
 
+static GWeatherTimezone *
+timezone_sink_keep_alive (GWeatherTimezone *tz)
+{
+       g_assert (tz->db);
+
+       if (g_ptr_array_find (tz->db->timezones_keepalive, tz, NULL)) {
+               gweather_timezone_unref (tz);
+               return tz;
+       }
+
+       g_ptr_array_add (tz->db->timezones_keepalive, tz);
+
+       return tz;
+}
+
+GWeatherTimezone *
+_gweather_timezone_ref_for_idx (GWeatherDb       *db,
+                                guint             idx)
+{
+    GWeatherTimezone *zone;
+    DbWorldTimezonesEntryRef ref;
+    DbTimezoneRef tz_ref;
+    const char *id;
+    int offset = 0, dst_offset = 0;
+    gboolean has_dst = FALSE;
+
+    g_assert (db);
+    g_assert (idx < db->timezones->len);
+    zone = g_ptr_array_index (db->timezones, idx);
+    if (zone)
+        return gweather_timezone_ref (zone);
+
+    ref = db_world_timezones_get_at (db->timezones_ref, idx);
+    id = db_world_timezones_entry_get_key (ref);
+    tz_ref = db_world_timezones_entry_get_value (ref);
+
+    if (parse_tzdata (id, db->year_start, db->year_end,
+                     &offset, &has_dst, &dst_offset)) {
+       zone = g_slice_new0 (GWeatherTimezone);
+       zone->ref_count = 1;
+       zone->db = db;
+       zone->db_idx = idx;
+       zone->id = g_strdup (id);
+
+       zone->name = g_strdup (EMPTY_TO_NULL (db_i18n_get_str (db_timezone_get_name (tz_ref))));
+       zone->offset = offset;
+       zone->has_dst = has_dst;
+       zone->dst_offset = dst_offset;
+
+       /* Insert weak reference */
+       g_ptr_array_index (db->timezones, idx) = zone;
+    }
+
+    return zone;
+}
+
 /**
  * gweather_timezone_get_by_tzid:
  * @tzid: A timezone identifier, like "America/New_York" or "Europe/London"
@@ -159,19 +217,30 @@ parse_tzdata (const char *tz_name, time_t start, time_t end,
  * belongs to GWeather, do not unref it.
  *
  * Since: 3.12
+ *
+ * Deprecated: XXX: XXX()
  */
 GWeatherTimezone *
 gweather_timezone_get_by_tzid (const char *tzid)
 {
     GWeatherLocation *world;
+    GWeatherDb *db;
+    gsize idx;
 
     g_return_val_if_fail (tzid != NULL, NULL);
 
-    world = gweather_location_get_world ();
+    /* TODO: Get the DB directly */
+    world = gweather_location_ref_world ();
+    db = world->db;
+    gweather_location_unref (world);
 
-    return g_hash_table_lookup (world->timezone_cache, tzid);
+    if (!db_world_timezones_lookup (db->timezones_ref, tzid, &idx, NULL))
+       return NULL;
+
+    return timezone_sink_keep_alive (_gweather_timezone_ref_for_idx (db, idx));
 }
 
+#if 0
 static GWeatherTimezone *
 parse_timezone (GWeatherParser *parser)
 {
@@ -274,6 +343,7 @@ error_out:
     g_ptr_array_free (zones, TRUE);
     return NULL;
 }
+#endif
 
 /**
  * gweather_timezone_ref:
@@ -304,6 +374,10 @@ gweather_timezone_unref (GWeatherTimezone *zone)
     g_return_if_fail (zone != NULL);
 
     if (!--zone->ref_count) {
+       if (zone->db) {
+               g_ptr_array_index (zone->db->timezones, zone->db_idx) = 0;
+       }
+
        g_free (zone->id);
        g_free (zone->name);
        g_slice_free (GWeatherTimezone, zone);
diff --git a/libgweather/meson.build b/libgweather/meson.build
index 97524e0..4804ef2 100644
--- a/libgweather/meson.build
+++ b/libgweather/meson.build
@@ -52,8 +52,7 @@ gweather_c_sources = [
   'gweather-location.c',
   'gweather-timezone.c',
   'gweather-location-entry.c',
-  'gweather-timezone-menu.c',
-  'gweather-parser.c']
+  'gweather-timezone-menu.c']
 introspection_sources = gweather_c_sources + gweather_new_headers
 
 lib_libgweather = shared_library('gweather-3',


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