[glib] datetime: Avoid excessive copies in add_full()



commit 026375b395fcdc2336666546afd2f21e7ee8bc67
Author: Emmanuele Bassi <ebassi linux intel com>
Date:   Wed Aug 25 23:00:31 2010 +0100

    datetime: Avoid excessive copies in add_full()
    
    The current implementation of g_date_time_add_full() creates multiple
    GDateTime temporary objects and unrefs them immediately; even with the
    slice allocator this could result in a performance bottleneck,
    especially if the atomic integer operations fall back to slow paths.
    
    We can isolate the components of the add_full() operation and create
    internal modifiers that operate on an existing GDateTime; this brings
    down the number of GDateTime copies created from six to one.
    
    While at it, the test suite for add_full() should have more checks for
    roll-over of months and days.
    
    Signed-off-by: Emmanuele Bassi <ebassi linux intel com>

 glib/gdatetime.c       |  133 +++++++++++++++++++++++++++++++++--------------
 glib/tests/gdatetime.c |   24 +++++---
 2 files changed, 108 insertions(+), 49 deletions(-)
---
diff --git a/glib/gdatetime.c b/glib/gdatetime.c
index 9efd435..c7972b4 100644
--- a/glib/gdatetime.c
+++ b/glib/gdatetime.c
@@ -277,6 +277,24 @@ get_weekday_name_abbr (gint day)
   return NULL;
 }
 
+static inline gint
+date_to_julian (gint year,
+                gint month,
+                gint day)
+{
+  gint a = (14 - month) / 12;
+  gint y = year + 4800 - a;
+  gint m = month + (12 * a) - 3;
+
+  return day
+       + (((153 * m) + 2) / 5)
+       + (y * 365)
+       + (y / 4)
+       - (y / 100)
+       + (y / 400)
+       - 32045;
+}
+
 static inline void
 g_date_time_add_days_internal (GDateTime *datetime,
                                gint64     days)
@@ -315,6 +333,69 @@ g_date_time_add_usec (GDateTime *datetime,
     datetime->usec = __usec % USEC_PER_DAY;
 }
 
+/*< internal >
+ * g_date_time_add_dmy:
+ * @datetime: a #GDateTime
+ * @years: years to add, in the Gregorian calendar
+ * @months: months to add, in the Gregorian calendar
+ * @days: days to add, in the Gregorian calendar
+ *
+ * Updates @datetime by adding @years, @months and @days to it
+ *
+ * This function modifies the passed #GDateTime so public accessors
+ * should make always pass a copy
+ */
+static inline void
+g_date_time_add_dmy (GDateTime *datetime,
+                     gint       years,
+                     gint       months,
+                     gint       days)
+{
+  gint __year = g_date_time_get_year (datetime);
+  gint __month = g_date_time_get_month (datetime);
+  gint __day = g_date_time_get_day_of_month (datetime);
+  gint step, i;
+  const guint16 *max_days;
+
+  /* subtract one day for leap years */
+  if (GREGORIAN_LEAP (__year) && __month == 2)
+    {
+      if (__day == 29)
+        __day -= 1;
+    }
+
+  __year += years;
+
+  /* add months */
+  step = months > 0 ? 1 : -1;
+  for (i = 0; i < ABS (months); i++)
+    {
+      __month += step;
+
+      if (__month < 1)
+        {
+          __year -= 1;
+          __month = 12;
+        }
+      else if (__month > 12)
+        {
+          __year += 1;
+          __month = 1;
+        }
+    }
+
+  /* clamp the days */
+  max_days = days_in_months[GREGORIAN_LEAP (__year) ? 1 : 0];
+  if (max_days[__month] < __day)
+    __day = max_days[__month];
+
+  /* since the add_days_internal() uses the julian date we need to
+   * update it using the new year/month/day and then add the days
+   */
+  datetime->julian = date_to_julian (__year, __month, __day);
+  g_date_time_add_days_internal (datetime, days);
+}
+
 #define ZONEINFO_DIR            "zoneinfo"
 #define TZ_MAGIC                "TZif"
 #define TZ_MAGIC_LEN            (strlen (TZ_MAGIC))
@@ -970,31 +1051,21 @@ g_date_time_add_full (const GDateTime *datetime,
                       gint             minutes,
                       gint             seconds)
 {
-  GDateTime *tmp, *dt;
+  GDateTime *dt;
+  gint64 usecs;
 
   g_return_val_if_fail (datetime != NULL, NULL);
 
-  dt = g_date_time_add_years (datetime, years);
-  tmp = dt;
-
-  dt = g_date_time_add_months (tmp, months);
-  g_date_time_unref (tmp);
-  tmp = dt;
-
-  dt = g_date_time_add_days (tmp, days);
-  g_date_time_unref (tmp);
-  tmp = dt;
-
-  dt = g_date_time_add_hours (tmp, hours);
-  g_date_time_unref (tmp);
-  tmp = dt;
+  dt = g_date_time_copy (datetime);
 
-  dt = g_date_time_add_minutes (tmp, minutes);
-  g_date_time_unref (tmp);
-  tmp = dt;
+  /* add date */
+  g_date_time_add_dmy (dt, years, months, days);
 
-  dt = g_date_time_add_seconds (tmp, seconds);
-  g_date_time_unref (tmp);
+  /* add time */
+  usecs = (hours   * USEC_PER_HOUR)
+        + (minutes * USEC_PER_MINUTE)
+        + (seconds * USEC_PER_SECOND);
+  g_date_time_add_usec (dt, usecs);
 
   return dt;
 }
@@ -1144,8 +1215,8 @@ g_date_time_equal (gconstpointer dt1,
   a = dt1;
   b = dt2;
 
-  a_utc = g_date_time_to_utc ((GDateTime *) a);
-  b_utc = g_date_time_to_utc ((GDateTime *) b);
+  a_utc = g_date_time_to_utc (a);
+  b_utc = g_date_time_to_utc (b);
 
   a_epoch = g_date_time_to_epoch (a_utc);
   b_epoch = g_date_time_to_epoch (b_utc);
@@ -1573,24 +1644,6 @@ g_date_time_is_daylight_savings (const GDateTime *datetime)
   return datetime->tz->is_dst;
 }
 
-static inline gint
-date_to_julian (gint year,
-                gint month,
-                gint day)
-{
-  gint a = (14 - month) / 12;
-  gint y = year + 4800 - a;
-  gint m = month + (12 * a) - 3;
-
-  return day
-       + (((153 * m) + 2) / 5)
-       + (y * 365)
-       + (y / 4)
-       - (y / 100)
-       + (y / 400)
-       - 32045;
-}
-
 /**
  * g_date_time_new_from_date:
  * @year: the Gregorian year
diff --git a/glib/tests/gdatetime.c b/glib/tests/gdatetime.c
index d204714..b651b5b 100644
--- a/glib/tests/gdatetime.c
+++ b/glib/tests/gdatetime.c
@@ -543,15 +543,21 @@ test_GDateTime_add_full (void)
   g_date_time_unref (dt2); \
 } G_STMT_END
 
-  TEST_ADD_FULL (2009, 10, 21, 0, 0, 0,
-                    1,  1,  1, 1, 1, 1,
-                 2010, 11, 22, 1, 1, 1);
-  TEST_ADD_FULL (2000,  1,  1, 1, 1, 1,
-                    0,  1,  0, 0, 0, 0,
-                 2000,  2,  1, 1, 1, 1);
-  TEST_ADD_FULL (2000,  1,  1, 0, 0, 0,
-                   -1,  1,  0, 0, 0, 0,
-                 1999,  2,  1, 0, 0, 0);
+  TEST_ADD_FULL (2009, 10, 21,  0,  0, 0,
+                    1,  1,  1,  1,  1, 1,
+                 2010, 11, 22,  1,  1, 1);
+  TEST_ADD_FULL (2000,  1,  1,  1,  1, 1,
+                    0,  1,  0,  0,  0, 0,
+                 2000,  2,  1,  1,  1, 1);
+  TEST_ADD_FULL (2000,  1,  1,  0,  0, 0,
+                   -1,  1,  0,  0,  0, 0,
+                 1999,  2,  1,  0,  0, 0);
+  TEST_ADD_FULL (2010, 10, 31,  0,  0, 0,
+                    0,  4,  0,  0,  0, 0,
+                 2011,  2, 28,  0,  0, 0);
+  TEST_ADD_FULL (2010,  8, 25, 22, 45, 0,
+                    0,  1,  6,  1, 25, 0,
+                 2010, 10,  2,  0, 10, 0);
 }
 
 static void



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