diff options
author | 2010-06-28 15:27:43 +0300 | |
---|---|---|
committer | 2010-06-28 15:27:43 +0300 | |
commit | 52c55b11d27e0f6f9ee89abee930deee12aa30d3 (patch) | |
tree | a1ded635d8a5dea4670952d1593096cfdc04c0e9 | |
parent | Add option [connections].current_speed_time_interval_msecs to segget.conf (diff) | |
download | idfetch-52c55b11d27e0f6f9ee89abee930deee12aa30d3.tar.gz idfetch-52c55b11d27e0f6f9ee89abee930deee12aa30d3.tar.bz2 idfetch-52c55b11d27e0f6f9ee89abee930deee12aa30d3.zip |
Add avg speed measurement for connections.
connection->avg_speed=connection->total_dld_bytes/time_left_from(connection->start_time);
-rw-r--r-- | segget/connection.cpp | 64 | ||||
-rw-r--r-- | segget/connection.h | 61 | ||||
-rw-r--r-- | segget/mirror.cpp | 2 | ||||
-rw-r--r-- | segget/segget.conf | 12 | ||||
-rw-r--r-- | segget/segget.cpp | 10 | ||||
-rw-r--r-- | segget/segment.cpp | 95 | ||||
-rw-r--r-- | segget/segment.h | 93 | ||||
-rw-r--r-- | segget/stats.cpp | 10 | ||||
-rw-r--r-- | segget/tui.cpp | 25 | ||||
-rw-r--r-- | segget/utils.cpp | 42 | ||||
-rw-r--r-- | segget/utils.h | 32 |
11 files changed, 313 insertions, 133 deletions
diff --git a/segget/connection.cpp b/segget/connection.cpp index c9eaf6e..4014749 100644 --- a/segget/connection.cpp +++ b/segget/connection.cpp @@ -23,27 +23,49 @@ * License along with Segget; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef __CONNECTION_H__ -#define __CONNECTION_H__ +#include "connection.h" -#include <time.h> +void Tconnection::start(){ + try{ + total_dld_bytes=0; + bytes_per_last_interval=0; + gettimeofday(&start_time,NULL); + active=true; + }catch(...){ + error_log("Error in connection.cpp: start()"); + } +} -class Tconnection{ - private: - ulong bytes_per_last_interval; - public: - time_t start_time; - void *segment; - Tconnection(): - bytes_per_last_interval(0), - start_time(0), - segment(0){}; - void inc_bytes_per_last_interval(ulong new_bytes_count){bytes_per_last_interval+=new_bytes_count;}; - ulong get_bytes_per_last_interval(){return bytes_per_last_interval;}; - void reset_bytes_per_last_interval(){bytes_per_last_interval=0;}; -}; +void Tconnection::stop(){ + try{ + active=false; + }catch(...){ + error_log("Error in connection.cpp: stop()"); + } +} -#define MAX_CONNECTS 6 /* number of simultaneous transfers */ -time_t prev_time; -Tconnection connection_array[MAX_CONNECTS]; -#endif
\ No newline at end of file +void Tconnection::inc_bytes_per_last_interval(ulong new_bytes_count){ + try{ + total_dld_bytes+=new_bytes_count; + bytes_per_last_interval+=new_bytes_count; + }catch(...){ + error_log("Error in connection.cpp: inc_bytes_per_last_interval()"); + } +} + +void Tconnection::show_connection_progress(ulong time_diff){ + try{ + if (active){ + stats.total_bytes_per_last_interval+=bytes_per_last_interval; + msg_segment_progress(segment->connection_num, + segment->segment_num, segment->try_num, + segment->downloaded_bytes, + segment->segment_size, + (bytes_per_last_interval*1000)/time_diff, + (total_dld_bytes*1000)/time_left_from(start_time)); + bytes_per_last_interval=0; + } + }catch(...){ + error_log("Error in connection.cpp: show_connection_progress()"); + } +}
\ No newline at end of file diff --git a/segget/connection.h b/segget/connection.h new file mode 100644 index 0000000..f9bf314 --- /dev/null +++ b/segget/connection.h @@ -0,0 +1,61 @@ +/* +* Copyright (C) 2010 Robin H.Johnson, Ovechko Kostyantyn <fastinetserver@gmail.com>. +* +* Project: IDFetch. +* Developer: Ovechko Kostyantyn Olexandrovich (Kharkiv State Technical University of Construction and Architecture, Ukraine). +* Mentor: Robin H. Johnson (Gentoo Linux: Developer, Trustee & Infrastructure Lead). +* Mentoring organization: Gentoo Linux. +* Sponsored by GSOC 2010. +* +* This file is part of Segget. +* +* Segget is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* Segget is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with Segget; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef __CONNECTION_H__ +#define __CONNECTION_H__ + +#include <time.h> +class Tsegment; +#include "segment.h" +#include "utils.h" + +using namespace std; + +class Tconnection{ + private: + bool active; + ulong total_dld_bytes; + ulong bytes_per_last_interval; + public: + timeval start_time; + Tsegment *segment; + Tconnection(): + active(0), + total_dld_bytes(0), + bytes_per_last_interval(0), + start_time(), + segment(0){}; + void start(); + void stop(); + void inc_bytes_per_last_interval(ulong new_bytes_count); + void show_connection_progress(ulong time_diff); +}; + +#define MAX_CONNECTS 6 /* number of simultaneous transfers */ +time_t prev_time; + +Tconnection connection_array[MAX_CONNECTS]; +#endif
\ No newline at end of file diff --git a/segget/mirror.cpp b/segget/mirror.cpp index 8ef8c92..4d66ea8 100644 --- a/segget/mirror.cpp +++ b/segget/mirror.cpp @@ -77,7 +77,7 @@ void Tmirror::start(){ void Tmirror::stop(ulong time, uint size){ try{ - dld_time+=time; + dld_time+=time/1000; dld_size+=size; honesty=1; debug(toString(time)+"---"+toString(size)); diff --git a/segget/segget.conf b/segget/segget.conf index 6f10e25..2218cec 100644 --- a/segget/segget.conf +++ b/segget/segget.conf @@ -28,7 +28,7 @@ max_connection_num_per_distfile=3 # Define maximum segment size in bytes. # Default: # max_segment_size=500000 -max_segment_size=adf500000 +max_segment_size=500000 # SYNOPSIS: resume_on=0 | 1 # - If resume_on set to 1: @@ -40,7 +40,7 @@ max_segment_size=adf500000 # is downloaded or not. # Default: # resume_on=1 -resume_on=false +resume_on=1 # MAX_TRIES # If segment download was unsuccessful, new attempts are made. When attempts @@ -111,13 +111,13 @@ max_connection_speed=3000 # CURRENT_SPEED_TIME_INTERVAL_MSECS # segget transfers may have bursty nature of their traffic. Therefore, while -# measuring current speed segget actually calculates average speed during -# current_speed_time_interval_msecs, defined in milliseconds. +# measuring current speed, segget actually calculates average speed during +# current_speed_time_interval_msecs time interval, defined in milliseconds. # Min limit:100 # Max limit: 60000 # Default: # current_speed_time_interval_msecs=1000 -current_speed_time_interval_msecs=3000 +current_speed_time_interval_msecs=1000 # NOT IMPLEMENTED YET: max_total_speed=50000 @@ -150,7 +150,7 @@ bind_interface=none # max_connections_num_per_mirror active downloads. # Default: # max_connections_num_per_mirror=1 -max_connections_num_per_mirror=1 +max_connections_num_per_mirror=2 # SYNOPSIS: collect_benchmark_stats_on=0 | 1 # - If set to 1, stats on mirrors performance will be collected. diff --git a/segget/segget.cpp b/segget/segget.cpp index c6aa8ab..5fcc987 100644 --- a/segget/segget.cpp +++ b/segget/segget.cpp @@ -31,6 +31,8 @@ #include <json/json.h> #include <ncurses.h> #include "pkg.cpp" +#include "connection.cpp" +#include "utils.cpp" //#include "settings.cpp" using namespace std; @@ -213,15 +215,17 @@ int download_pkgs(){ curl_multi_remove_handle(cm, e); current_segment->segment_file.close(); Tmirror *Pcurr_mirror=find_mirror(strip_mirror_name(current_segment->url)); - time_t now_time = time((time_t *)NULL); + timeval now_time; + gettimeofday(&now_time,NULL); Tdistfile* prnt_distfile; prnt_distfile=(Tdistfile*)current_segment->parent_distfile; + connection_array[current_segment->connection_num].stop(); prnt_distfile->active_connections_num--; if (result!=0){ // error -> start downloading again msg_status2(current_segment->connection_num, toString(result)+"]- Failed download "+current_segment->file_name); debug(toString(result)+"]- Failed download "+current_segment->url); - Pcurr_mirror->stop(now_time-connection_array[current_segment->connection_num].start_time,0); + Pcurr_mirror->stop(time_left_from(connection_array[current_segment->connection_num].start_time),0); if (current_segment->try_num>=settings.max_tries){ current_segment->status=FAILED; error_log("Segment:"+current_segment->file_name+" has reached max_tries limit - segment.status set to FAILED"); @@ -234,7 +238,7 @@ int download_pkgs(){ // no error => count this one and start new log("Succesfully downloaded "+current_segment->file_name+" on connection#"+toString(current_segment->connection_num)); debug(" Successful download "+current_segment->url); - Pcurr_mirror->stop(now_time-connection_array[current_segment->connection_num].start_time,current_segment->segment_size); + Pcurr_mirror->stop(time_left_from(connection_array[current_segment->connection_num].start_time),current_segment->segment_size); current_segment->status=DOWNLOADED; prnt_distfile->inc_dld_segments_count(current_segment); }; diff --git a/segget/segment.cpp b/segget/segment.cpp index 08ab575..d70d226 100644 --- a/segget/segment.cpp +++ b/segget/segment.cpp @@ -24,69 +24,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef __SEGMENT_H__ -#define __SEGMENT_H__ -#include <sstream> -#include <fstream> -#include <cstring> -#include <string> -#include <iostream> -#include <stdio.h> -#include <cstdio> -#include <ncurses.h> -#include <curl/curl.h> -#include "settings.h" -#include "stats.cpp" - -using namespace std; - -extern Tsettings settings; -unsigned long downloaded_bytes=0; -size_t write_data(void *buffer, size_t size, size_t nmemb, void *cur_segment); - -enum Tstatus{WAITING, DOWNLOADING, DOWNLOADED, FAILED}; - -class Tsegment{ - private: - CURL *easyhandle; - char* urllist; - public: - string file_name; - Tstatus status; - uint try_num; - void* parent_distfile; - uint connection_num; - uint segment_num; - uint segment_size; - unsigned long downloaded_bytes; - string url; - string range; - ofstream segment_file; - Tsegment(): - easyhandle(0), - urllist(0), - file_name(""), - status(WAITING), - try_num(0), - parent_distfile(0), - connection_num(0), - segment_num(0), - segment_size(1000), - downloaded_bytes(0), - url(""), - range(""), - segment_file(0) - {}; - Tsegment(const Tsegment &L); // copy constructor - Tsegment & operator=(const Tsegment &L); - ~Tsegment(); - void set_segment(void *prnt_distfile, uint seg_num, string distfile_name, ulong default_seg_size, ulong range_end); - void prepare_for_connection(CURLM *cm, uint con_num, uint distfile_num, string segment_url); - string get_file_name(){return file_name;}; - int add_easy_handle_to_multi(CURLM *cm); -}; - -Tsegment *segments_in_progress[MAX_CONNECTS]={0}; +#include "segment.h" void Tsegment::set_segment(void *prnt_distfile, uint seg_num, string distfile_name, ulong default_seg_size, ulong range_end){ try{ @@ -125,7 +63,7 @@ void Tsegment::prepare_for_connection(CURLM *cm, uint con_num, uint distfile_num status=DOWNLOADING; downloaded_bytes=0; connection_num=con_num; - connection_array[con_num].start_time=time((time_t *)NULL); + connection_array[con_num].start(); url=segment_url; try_num++; add_easy_handle_to_multi(cm); @@ -198,14 +136,7 @@ void show_progress(double time_diff){ for (uint con_num=0; con_num<MAX_CONNECTS; con_num++){ // ulong speed=bytes_written*1000/(diff_sec+diff_milli); //if connection is not NULL - if (connection_array[con_num].segment){ - Tsegment* segment=(Tsegment*)connection_array[con_num].segment; - stats.total_bytes_per_last_interval+=connection_array[con_num].get_bytes_per_last_interval(); - msg_segment_progress(con_num,segment->segment_num, segment->try_num, - segment->downloaded_bytes,segment->segment_size, - (connection_array[con_num].get_bytes_per_last_interval()*1000)/time_diff); - connection_array[con_num].reset_bytes_per_last_interval(); - } + connection_array[con_num].show_connection_progress(time_diff); } stats.last_time_interval=time_diff; stats.show_totals(); @@ -221,7 +152,6 @@ size_t write_data(void *buffer, size_t size, size_t nmemb, void *cur_segment){ Tsegment *segment; segment =(Tsegment*)cur_segment; segment->downloaded_bytes+=nmemb; - try{ segment->segment_file.write((char*)buffer,nmemb*size); } @@ -229,27 +159,14 @@ size_t write_data(void *buffer, size_t size, size_t nmemb, void *cur_segment){ error_log("Can't write segment file:"+segment->file_name); } connection_array[segment->connection_num].inc_bytes_per_last_interval(bytes_written); - - timeval now_time; - gettimeofday(&now_time,NULL); -// ulong diff_sec = difftime(now_time.tv_sec, prev_time.tv_sec) * 1000000; -// ulong diff_milli = difftime(now_time.tv_usec, prev_time.tv_usec) + diff_sec; - - double time_diff_msecs=(now_time.tv_sec-stats.previous_time.tv_sec)*1000+(now_time.tv_usec-stats.previous_time.tv_usec)/1000; -// debug(segment->file_name+"==="+toString((ulong)now_time)+"=="+toString(now_time)); + ulong time_diff_msecs=time_left_from(stats.previous_time); if (time_diff_msecs >= settings.current_speed_time_interval_msecs){ -// debug(segment->file_name+"--->"+toString((ulong)())); show_progress(time_diff_msecs); - stats.previous_time=now_time; + stats.reset_previous_time(); }; -// else -// debug(segment->file_name+"==="+toString(prev_time.tv_sec)+"=="+toString(prev_time.tv_usec)+"==="+toString((ulong)(diff_milli))); - //toString(diff_milli)); - //refresh(); } catch(...){ error_log("Error in segment.cpp: write_data()"); } return bytes_written; -} -#endif
\ No newline at end of file +}
\ No newline at end of file diff --git a/segget/segment.h b/segget/segment.h new file mode 100644 index 0000000..34ca383 --- /dev/null +++ b/segget/segment.h @@ -0,0 +1,93 @@ +/* +* Copyright (C) 2010 Robin H.Johnson, Ovechko Kostyantyn <fastinetserver@gmail.com>. +* +* Project: IDFetch. +* Developer: Ovechko Kostyantyn Olexandrovich (Kharkiv State Technical University of Construction and Architecture, Ukraine). +* Mentor: Robin H. Johnson (Gentoo Linux: Developer, Trustee & Infrastructure Lead). +* Mentoring organization: Gentoo Linux. +* Sponsored by GSOC 2010. +* +* This file is part of Segget. +* +* Segget is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* Segget is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with Segget; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef __SEGMENT_H__ +#define __SEGMENT_H__ +#include <sstream> +#include <fstream> +#include <cstring> +#include <string> +#include <iostream> +#include <stdio.h> +#include <cstdio> +#include <ncurses.h> +#include <curl/curl.h> +#include "settings.h" +#include "stats.cpp" + +using namespace std; + +extern Tsettings settings; +unsigned long downloaded_bytes=0; +size_t write_data(void *buffer, size_t size, size_t nmemb, void *cur_segment); + +enum Tstatus{WAITING, DOWNLOADING, DOWNLOADED, FAILED}; + +class Tsegment{ + private: + CURL *easyhandle; + char* urllist; + public: + string file_name; + Tstatus status; + uint try_num; + void* parent_distfile; + uint connection_num; + uint segment_num; + uint segment_size; + unsigned long downloaded_bytes; + string url; + string range; + ofstream segment_file; + Tsegment(): + easyhandle(0), + urllist(0), + file_name(""), + status(WAITING), + try_num(0), + parent_distfile(0), + connection_num(0), + segment_num(0), + segment_size(1000), + downloaded_bytes(0), + url(""), + range(""), + segment_file(0) + {}; + Tsegment(const Tsegment &L); // copy constructor + Tsegment & operator=(const Tsegment &L); + ~Tsegment(); + void set_segment(void *prnt_distfile, uint seg_num, string distfile_name, ulong default_seg_size, ulong range_end); + void prepare_for_connection(CURLM *cm, uint con_num, uint distfile_num, string segment_url); + string get_file_name(){return file_name;}; + int add_easy_handle_to_multi(CURLM *cm); +}; + +Tsegment *segments_in_progress[MAX_CONNECTS]={0}; + +void show_progress(double time_diff); +size_t write_data(void *buffer, size_t size, size_t nmemb, void *cur_segment); +#endif
\ No newline at end of file diff --git a/segget/stats.cpp b/segget/stats.cpp index 0fb61c7..f6b0ec5 100644 --- a/segget/stats.cpp +++ b/segget/stats.cpp @@ -27,7 +27,7 @@ #ifndef __STATS_H__ #define __STATS_H__ -#include "connection.cpp" +#include "connection.h" #include "tui.h" #include <sys/time.h> using namespace std; @@ -60,6 +60,7 @@ class Tstats{ void inc_total_size(ulong more_bytes){ total_size+=more_bytes;}; ulong get_total_size(){return total_size;}; void show_totals(); + void reset_previous_time(); }; void Tstats::show_totals(){ @@ -93,5 +94,12 @@ void Tstats::show_totals(){ } } +void Tstats::reset_previous_time(){ + try{ + gettimeofday(&previous_time,NULL); + }catch(...){ + error_log_no_msg("Error in stats.cpp: reset_previous_time()"); + } +} Tstats stats; #endif
\ No newline at end of file diff --git a/segget/tui.cpp b/segget/tui.cpp index d2a4646..1ddf42a 100644 --- a/segget/tui.cpp +++ b/segget/tui.cpp @@ -63,27 +63,28 @@ void msg_connecting(uint connection_num, uint distfile_num, uint segment_num, st } } -void msg_segment_progress(uint connection_num, uint segment_num, uint try_num, ulong dld_bytes, ulong total_bytes, ulong speed){ +void msg_segment_progress(uint connection_num, uint segment_num, uint try_num, ulong dld_bytes, ulong total_bytes, ulong speed, ulong avg_speed){ try{ - int percent=dld_bytes*100/total_bytes; + string speed_str; + string avg_speed_str; if (speed<1000) - msg(connection_num*CONNECTION_LINES,0, - field("[",connection_num,2)+"]" - +field(" Segment:",segment_num, 5) - +field(" Try:",try_num,4) - +field(" Bytes:",dld_bytes,7) - +field(" / ",total_bytes,7) - +field(" = ",percent,3)+"%%" - +field(" Speed:",speed,7)+" b/s"); + speed_str=field(" Speed:",speed,7)+" b/s"; else - msg(connection_num*CONNECTION_LINES,0, + speed_str=field(" Speed:",speed/1000,7)+" Kb/s"; + if (avg_speed<1000) + avg_speed_str=field(" AVG speed:",avg_speed,7)+" b/s"; + else + avg_speed_str=field(" AVG speed:",avg_speed/1000,7)+" Kb/s"; + int percent=dld_bytes*100/total_bytes; + msg(connection_num*CONNECTION_LINES,0, field("[",connection_num,2)+"]" +field(" Segment:",segment_num, 5) +field(" Try:",try_num,4) +field(" Bytes:",dld_bytes,7) +field(" / ",total_bytes,7) +field(" = ",percent,3)+"%%" - +field(" Speed:",speed/1000,7)+" Kb/s"); + +speed_str + +avg_speed_str); } catch(...) { diff --git a/segget/utils.cpp b/segget/utils.cpp new file mode 100644 index 0000000..cdc2dc8 --- /dev/null +++ b/segget/utils.cpp @@ -0,0 +1,42 @@ +/* +* Copyright (C) 2010 Robin H.Johnson, Ovechko Kostyantyn <fastinetserver@gmail.com>. +* +* Project: IDFetch. +* Developer: Ovechko Kostyantyn Olexandrovich (Kharkiv State Technical University of Construction and Architecture, Ukraine). +* Mentor: Robin H. Johnson (Gentoo Linux: Developer, Trustee & Infrastructure Lead). +* Mentoring organization: Gentoo Linux. +* Sponsored by GSOC 2010. +* +* This file is part of Segget. +* +* Segget is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* Segget is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with Segget; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "utils.h" +#include "sys/time.h" +ulong time_left_from(timeval from_time){ + try{ + timeval now_time; + gettimeofday(&now_time,NULL); + ulong timeleft=(now_time.tv_sec-from_time.tv_sec)*1000+(now_time.tv_usec-from_time.tv_usec)/1000; + if (timeleft<1) + timeleft=1; + return timeleft; + }catch(...){ + error_log("Error in utils.cpp: time_left_from()"); + return 1; + } +} + diff --git a/segget/utils.h b/segget/utils.h new file mode 100644 index 0000000..60fd70c --- /dev/null +++ b/segget/utils.h @@ -0,0 +1,32 @@ +/* +* Copyright (C) 2010 Robin H.Johnson, Ovechko Kostyantyn <fastinetserver@gmail.com>. +* +* Project: IDFetch. +* Developer: Ovechko Kostyantyn Olexandrovich (Kharkiv State Technical University of Construction and Architecture, Ukraine). +* Mentor: Robin H. Johnson (Gentoo Linux: Developer, Trustee & Infrastructure Lead). +* Mentoring organization: Gentoo Linux. +* Sponsored by GSOC 2010. +* +* This file is part of Segget. +* +* Segget is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* Segget is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with Segget; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef __UTILS_H__ +#define __UTILS_H__ + +ulong time_left_from(timeval from_time); + +#endif
\ No newline at end of file |