Imported Upstream version 7.53.1
[platform/upstream/curl.git] / lib / progress.c
index f147ce7..60627b2 100644 (file)
@@ -5,11 +5,11 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
+ * are also available at https://curl.haxx.se/docs/copyright.html.
  *
  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
  * copies of the Software, and permit persons to whom the Software is
@@ -25,9 +25,7 @@
 #include "urldata.h"
 #include "sendf.h"
 #include "progress.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
+#include "curl_printf.h"
 
 /* Provide a string that is 2 + 1 + 2 + 1 + 2 = 8 letters long (plus the zero
    byte) */
@@ -135,7 +133,7 @@ static char *max5data(curl_off_t bytes, char *max5)
 int Curl_pgrsDone(struct connectdata *conn)
 {
   int rc;
-  struct SessionHandle *data = conn->data;
+  struct Curl_easy *data = conn->data;
   data->progress.lastshow=0;
   rc = Curl_pgrsUpdate(conn); /* the final (forced) update */
   if(rc)
@@ -152,7 +150,7 @@ int Curl_pgrsDone(struct connectdata *conn)
 }
 
 /* reset all times except redirect, and reset the known transfer sizes */
-void Curl_pgrsResetTimesSizes(struct SessionHandle *data)
+void Curl_pgrsResetTimesSizes(struct Curl_easy *data)
 {
   data->progress.t_nslookup = 0.0;
   data->progress.t_connect = 0.0;
@@ -163,7 +161,7 @@ void Curl_pgrsResetTimesSizes(struct SessionHandle *data)
   Curl_pgrsSetUploadSize(data, -1);
 }
 
-void Curl_pgrsTime(struct SessionHandle *data, timerid timer)
+void Curl_pgrsTime(struct Curl_easy *data, timerid timer)
 {
   struct timeval now = Curl_tvnow();
 
@@ -214,25 +212,102 @@ void Curl_pgrsTime(struct SessionHandle *data, timerid timer)
   }
 }
 
-void Curl_pgrsStartNow(struct SessionHandle *data)
+void Curl_pgrsStartNow(struct Curl_easy *data)
 {
   data->progress.speeder_c = 0; /* reset the progress meter display */
   data->progress.start = Curl_tvnow();
+  data->progress.ul_limit_start.tv_sec = 0;
+  data->progress.ul_limit_start.tv_usec = 0;
+  data->progress.dl_limit_start.tv_sec = 0;
+  data->progress.dl_limit_start.tv_usec = 0;
   /* clear all bits except HIDE and HEADERS_OUT */
   data->progress.flags &= PGRS_HIDE|PGRS_HEADERS_OUT;
 }
 
-void Curl_pgrsSetDownloadCounter(struct SessionHandle *data, curl_off_t size)
+/*
+ * This is used to handle speed limits, calculating how much milliseconds we
+ * need to wait until we're back under the speed limit, if needed.
+ *
+ * The way it works is by having a "starting point" (time & amount of data
+ * transfered by then) used in the speed computation, to be used instead of the
+ * start of the transfer.
+ * This starting point is regularly moved as transfer goes on, to keep getting
+ * accurate values (instead of average over the entire tranfer).
+ *
+ * This function takes the current amount of data transfered, the amount at the
+ * starting point, the limit (in bytes/s), the time of the starting point and
+ * the current time.
+ *
+ * Returns -1 if no waiting is needed (not enough data transfered since
+ * starting point yet), 0 when no waiting is needed but the starting point
+ * should be reset (to current), or the number of milliseconds to wait to get
+ * back under the speed limit.
+ */
+long Curl_pgrsLimitWaitTime(curl_off_t cursize,
+                            curl_off_t startsize,
+                            curl_off_t limit,
+                            struct timeval start,
+                            struct timeval now)
+{
+  curl_off_t size = cursize - startsize;
+  time_t minimum;
+  time_t actual;
+
+  /* we don't have a starting point yet -- return 0 so it gets (re)set */
+  if(start.tv_sec == 0 && start.tv_usec == 0)
+    return 0;
+
+  /* not enough data yet */
+  if(size < limit)
+    return -1;
+
+  minimum = (time_t) (CURL_OFF_T_C(1000) * size / limit);
+  actual = Curl_tvdiff(now, start);
+
+  if(actual < minimum)
+    /* this is a conversion on some systems (64bit time_t => 32bit long) */
+    return (long)(minimum - actual);
+  else
+    return 0;
+}
+
+void Curl_pgrsSetDownloadCounter(struct Curl_easy *data, curl_off_t size)
 {
+  struct timeval now = Curl_tvnow();
+
   data->progress.downloaded = size;
+
+  /* download speed limit */
+  if((data->set.max_recv_speed > 0) &&
+     (Curl_pgrsLimitWaitTime(data->progress.downloaded,
+                             data->progress.dl_limit_size,
+                             data->set.max_recv_speed,
+                             data->progress.dl_limit_start,
+                             now) == 0)) {
+    data->progress.dl_limit_start = now;
+    data->progress.dl_limit_size = size;
+  }
 }
 
-void Curl_pgrsSetUploadCounter(struct SessionHandle *data, curl_off_t size)
+void Curl_pgrsSetUploadCounter(struct Curl_easy *data, curl_off_t size)
 {
+  struct timeval now = Curl_tvnow();
+
   data->progress.uploaded = size;
+
+  /* upload speed limit */
+  if((data->set.max_send_speed > 0) &&
+     (Curl_pgrsLimitWaitTime(data->progress.uploaded,
+                             data->progress.ul_limit_size,
+                             data->set.max_send_speed,
+                             data->progress.ul_limit_start,
+                             now) == 0)) {
+    data->progress.ul_limit_start = now;
+    data->progress.ul_limit_size = size;
+  }
 }
 
-void Curl_pgrsSetDownloadSize(struct SessionHandle *data, curl_off_t size)
+void Curl_pgrsSetDownloadSize(struct Curl_easy *data, curl_off_t size)
 {
   if(size >= 0) {
     data->progress.size_dl = size;
@@ -244,7 +319,7 @@ void Curl_pgrsSetDownloadSize(struct SessionHandle *data, curl_off_t size)
   }
 }
 
-void Curl_pgrsSetUploadSize(struct SessionHandle *data, curl_off_t size)
+void Curl_pgrsSetUploadSize(struct Curl_easy *data, curl_off_t size)
 {
   if(size >= 0) {
     data->progress.size_ul = size;
@@ -271,7 +346,7 @@ int Curl_pgrsUpdate(struct connectdata *conn)
   curl_off_t total_transfer;
   curl_off_t total_expected_transfer;
   curl_off_t timespent;
-  struct SessionHandle *data = conn->data;
+  struct Curl_easy *data = conn->data;
   int nowindex = data->progress.speeder_c% CURR_TIME;
   int checkindex;
   int countindex; /* amount of seconds stored in the speeder array */
@@ -286,9 +361,7 @@ int Curl_pgrsUpdate(struct connectdata *conn)
   now = Curl_tvnow(); /* what time is it */
 
   /* The time spent so far (from the start) */
-  data->progress.timespent =
-    (double)(now.tv_sec - data->progress.start.tv_sec) +
-    (double)(now.tv_usec - data->progress.start.tv_usec)/1000000.0;
+  data->progress.timespent = curlx_tvdiff_secs(now, data->progress.start);
   timespent = (curl_off_t)data->progress.timespent;
 
   /* The average download speed this far */
@@ -302,7 +375,7 @@ int Curl_pgrsUpdate(struct connectdata *conn)
      (data->progress.timespent>0?data->progress.timespent:1));
 
   /* Calculations done at most once a second, unless end is reached */
-  if(data->progress.lastshow != (long)now.tv_sec) {
+  if(data->progress.lastshow != now.tv_sec) {
     shownow = TRUE;
 
     data->progress.lastshow = now.tv_sec;
@@ -329,7 +402,7 @@ int Curl_pgrsUpdate(struct connectdata *conn)
 
     /* first of all, we don't do this if there's no counted seconds yet */
     if(countindex) {
-      long span_ms;
+      time_t span_ms;
 
       /* Get the index position to compare with the 'nowindex' position.
          Get the oldest entry possible. While we have less than CURR_TIME