From 912cb45928f03355ca162a2f1286ca49eb58155c Mon Sep 17 00:00:00 2001
From: Nikias Bassen <nikias@gmx.li>
Date: Mon, 19 Sep 2016 01:49:05 +0200
Subject: [PATCH] Change internal storage of PLIST_DATE values from struct
 timeval to double

This removes the timeval union member from the plist_data_t structure.
Since struct timeval is 2x64bit on 64bit platforms this member unnecessarily
grew the union size to 16 bytes while a size of 8 bytes is sufficient.
Also, on 32bit platforms struct timeval is only 2x32bit of size, limiting the
range of possible time values. In addition the binary property list format
also stores PLIST_DATE nodes as double.
---
 src/bplist.c | 11 ++---------
 src/plist.c  | 33 +++++++++++----------------------
 src/plist.h  |  1 -
 src/xplist.c |  7 +++----
 4 files changed, 16 insertions(+), 36 deletions(-)

diff --git a/src/bplist.c b/src/bplist.c
index bb73b31..fbe1b63 100644
--- a/src/bplist.c
+++ b/src/bplist.c
@@ -284,11 +284,7 @@ static plist_t parse_date_node(const char **bnode, uint8_t size)
     plist_t node = parse_real_node(bnode, size);
     plist_data_t data = plist_get_data(node);
 
-    double time_real = data->realval;
-    data->timeval.tv_sec = (long) time_real;
-    data->timeval.tv_usec = (time_real - (long) time_real) * 1000000;
     data->type = PLIST_DATE;
-    data->length = sizeof(struct timeval);
 
     return node;
 }
@@ -728,6 +724,7 @@ static unsigned int plist_data_hash(const void* key)
     case PLIST_BOOLEAN:
     case PLIST_UINT:
     case PLIST_REAL:
+    case PLIST_DATE:
     case PLIST_UID:
         buff = (char *) &data->intval;	//works also for real as we use an union
         size = 8;
@@ -744,10 +741,6 @@ static unsigned int plist_data_hash(const void* key)
         buff = (char *) &key;
         size = sizeof(const void*);
         break;
-    case PLIST_DATE:
-        buff = (char *) &(data->timeval);
-        size = data->length;
-        break;
     default:
         break;
     }
@@ -1183,7 +1176,7 @@ PLIST_API void plist_to_bin(plist_t plist, char **plist_bin, uint32_t * length)
             write_dict(bplist_buff, ptr_array_index(objects, i), ref_table, dict_param_size);
             break;
         case PLIST_DATE:
-            write_date(bplist_buff, data->timeval.tv_sec + (double) data->timeval.tv_usec / 1000000);
+            write_date(bplist_buff, data->realval);
             break;
         case PLIST_UID:
             write_uid(bplist_buff, data->intval);
diff --git a/src/plist.c b/src/plist.c
index ef1d7a2..af64ed1 100644
--- a/src/plist.c
+++ b/src/plist.c
@@ -27,6 +27,7 @@
 #include "plist.h"
 #include <stdlib.h>
 #include <stdio.h>
+#include <math.h>
 
 #include <node.h>
 #include <node_iterator.h>
@@ -214,9 +215,8 @@ PLIST_API plist_t plist_new_date(int32_t sec, int32_t usec)
 {
     plist_data_t data = plist_new_plist_data();
     data->type = PLIST_DATE;
-    data->timeval.tv_sec = sec;
-    data->timeval.tv_usec = usec;
-    data->length = sizeof(struct timeval);
+    data->realval = (double)sec + (double)usec / 1000000;
+    data->length = sizeof(double);
     return plist_new_node(data);
 }
 
@@ -567,6 +567,7 @@ static void plist_get_type_and_value(plist_t node, plist_type * type, void *valu
         *((uint64_t *) value) = data->intval;
         break;
     case PLIST_REAL:
+    case PLIST_DATE:
         *((double *) value) = data->realval;
         break;
     case PLIST_KEY:
@@ -577,11 +578,6 @@ static void plist_get_type_and_value(plist_t node, plist_type * type, void *valu
         *((uint8_t **) value) = (uint8_t *) malloc(*length * sizeof(uint8_t));
         memcpy(*((uint8_t **) value), data->buff, *length * sizeof(uint8_t));
         break;
-    case PLIST_DATE:
-        //exception : here we use memory on the stack since it is just a temporary buffer
-        ((struct timeval*) value)->tv_sec = data->timeval.tv_sec;
-        ((struct timeval*) value)->tv_usec = data->timeval.tv_usec;
-        break;
     case PLIST_ARRAY:
     case PLIST_DICT:
     default:
@@ -670,12 +666,12 @@ PLIST_API void plist_get_date_val(plist_t node, int32_t * sec, int32_t * usec)
 {
     plist_type type = plist_get_node_type(node);
     uint64_t length = 0;
-    struct timeval val = { 0, 0 };
+    double val = 0;
     if (PLIST_DATE == type)
         plist_get_type_and_value(node, &type, (void *) &val, &length);
-    assert(length == sizeof(struct timeval));
-    *sec = val.tv_sec;
-    *usec = val.tv_usec;
+    assert(length == sizeof(double));
+    *sec = (int32_t)val;
+    *usec = (int32_t)fabs((val - (int64_t)val) * 1000000);
 }
 
 int plist_data_compare(const void *a, const void *b)
@@ -700,6 +696,7 @@ int plist_data_compare(const void *a, const void *b)
     case PLIST_BOOLEAN:
     case PLIST_UINT:
     case PLIST_REAL:
+    case PLIST_DATE:
     case PLIST_UID:
         if (val_a->length != val_b->length)
             return FALSE;
@@ -730,11 +727,6 @@ int plist_data_compare(const void *a, const void *b)
         else
             return FALSE;
         break;
-    case PLIST_DATE:
-        if (!memcmp(&(val_a->timeval), &(val_b->timeval), sizeof(struct timeval)))
-            return TRUE;
-        else
-            return FALSE;
     default:
         break;
     }
@@ -782,6 +774,7 @@ static void plist_set_element_val(plist_t node, plist_type type, const void *val
         data->intval = *((uint64_t *) value);
         break;
     case PLIST_REAL:
+    case PLIST_DATE:
         data->realval = *((double *) value);
         break;
     case PLIST_KEY:
@@ -792,10 +785,6 @@ static void plist_set_element_val(plist_t node, plist_type type, const void *val
         data->buff = (uint8_t *) malloc(length);
         memcpy(data->buff, value, length);
         break;
-    case PLIST_DATE:
-        data->timeval.tv_sec = ((struct timeval*) value)->tv_sec;
-        data->timeval.tv_usec = ((struct timeval*) value)->tv_usec;
-        break;
     case PLIST_ARRAY:
     case PLIST_DICT:
     default:
@@ -840,7 +829,7 @@ PLIST_API void plist_set_data_val(plist_t node, const char *val, uint64_t length
 
 void plist_set_date_val(plist_t node, int32_t sec, int32_t usec)
 {
-    struct timeval val = { sec, usec };
+    double val = (double)sec + (double)usec / 1000000;
     plist_set_element_val(node, PLIST_DATE, &val, sizeof(struct timeval));
 }
 
diff --git a/src/plist.h b/src/plist.h
index ad65dea..da8f9ca 100644
--- a/src/plist.h
+++ b/src/plist.h
@@ -56,7 +56,6 @@ struct plist_data_s
         double realval;
         char *strval;
         uint8_t *buff;
-        struct timeval timeval;
     };
     uint64_t length;
     plist_type type;
diff --git a/src/xplist.c b/src/xplist.c
index 8fe3604..e55a094 100644
--- a/src/xplist.c
+++ b/src/xplist.c
@@ -258,7 +258,7 @@ static void node_to_xml(node_t* node, void *xml_struct)
     case PLIST_DATE:
         tag = XPLIST_DATE;
         {
-            time_t timev = (time_t)node_data->timeval.tv_sec + MAC_EPOCH;
+            time_t timev = (time_t)node_data->realval + MAC_EPOCH;
             struct tm *btime = gmtime(&timev);
             if (btime) {
                 val = (char*)malloc(24);
@@ -462,10 +462,9 @@ static void xml_to_node(xmlNodePtr xml_node, plist_t * plist_node)
                 tm_utc = gmtime(&timev);
                 timev -= (mktime(tm_utc) - timev);
             }
-            data->timeval.tv_sec = (long)(timev - MAC_EPOCH);
-            data->timeval.tv_usec = 0;
+            data->realval = (double)(timev - MAC_EPOCH);
             data->type = PLIST_DATE;
-            data->length = sizeof(struct timeval);
+            data->length = sizeof(double);
             xmlFree(strval);
             continue;
         }
