@@ -57,12 +57,18 @@ static std::string read_file(const std::string & fname) {
5757}
5858
5959static  void  write_file (const  std::string & fname, const  std::string & content) {
60-     std::ofstream file (fname);
60+     const  std::string fname_tmp = fname + " .tmp"  ;
61+     std::ofstream     file (fname_tmp);
6162    if  (!file) {
6263        throw  std::runtime_error (string_format (" error: failed to open file '%s'\n "  , fname.c_str ()));
6364    }
6465    file << content;
6566    file.close ();
67+ 
68+     //  Makes write atomic
69+     if  (rename (fname_tmp.c_str (), fname.c_str ()) != 0 ) {
70+         LOG_ERR (" %s: unable to rename file: %s to %s\n "  , __func__, fname_tmp.c_str (), fname.c_str ());
71+     }
6672}
6773
6874common_arg & common_arg::set_examples (std::initializer_list<enum  llama_example> examples) {
@@ -220,12 +226,25 @@ struct curl_slist_ptr {
220226#define  CURL_MAX_RETRY  3 
221227#define  CURL_RETRY_DELAY_SECONDS  2 
222228
223- static  bool  curl_perform_with_retry (const  std::string & url, CURL * curl, int  max_attempts, int  retry_delay_seconds, const  char  * method_name) {
229+ static  bool  curl_perform_with_retry (const  std::string & url,
230+                                     CURL *              curl,
231+                                     int                  max_attempts,
232+                                     int                  retry_delay_seconds,
233+                                     const  char  *        method_name,
234+                                     const  std::string & path_temporary = " "  ) {
224235    int  remaining_attempts = max_attempts;
225236
226237    while  (remaining_attempts > 0 ) {
227238        LOG_INF (" %s: %s %s (attempt %d of %d)...\n "  , __func__ , method_name, url.c_str (), max_attempts - remaining_attempts + 1 , max_attempts);
228239
240+         if  (std::filesystem::exists (path_temporary)) {
241+             const  std::string partial_size = std::to_string (std::filesystem::file_size (path_temporary));
242+             LOG_INF (" %s: server supports range requests, resuming download from byte %s\n "  , __func__,
243+                     partial_size.c_str ());
244+             const  std::string range_str = partial_size + " -"  ;
245+             curl_easy_setopt (curl, CURLOPT_RANGE, range_str.c_str ());
246+         }
247+ 
229248        CURLcode res = curl_easy_perform (curl);
230249        if  (res == CURLE_OK) {
231250            return  true ;
@@ -246,15 +265,14 @@ static bool curl_perform_with_retry(const std::string & url, CURL * curl, int ma
246265
247266//  download one single file from remote URL to local path
248267static  bool  common_download_file_single (const  std::string & url, const  std::string & path, const  std::string & bearer_token, bool  offline) {
249-     //  Check if the file already exists locally
250-     auto  file_exists = std::filesystem::exists (path);
251- 
252268    //  If the file exists, check its JSON metadata companion file.
253269    std::string metadata_path = path + " .json"  ;
254270    nlohmann::json metadata; //  TODO @ngxson : get rid of this json, use regex instead
255271    std::string etag;
256272    std::string last_modified;
257273
274+     //  Check if the file already exists locally
275+     const  auto  file_exists = std::filesystem::exists (path);
258276    if  (file_exists) {
259277        if  (offline) {
260278            LOG_INF (" %s: using cached file (offline mode): %s\n "  , __func__, path.c_str ());
@@ -289,6 +307,7 @@ static bool common_download_file_single(const std::string & url, const std::stri
289307    struct  common_load_model_from_url_headers  {
290308        std::string etag;
291309        std::string last_modified;
310+         std::string accept_ranges;
292311    };
293312
294313    common_load_model_from_url_headers headers;
@@ -328,7 +347,7 @@ static bool common_download_file_single(const std::string & url, const std::stri
328347        static  std::regex header_regex (" ([^:]+): (.*)\r\n "  );
329348        static  std::regex etag_regex (" ETag"  , std::regex_constants::icase);
330349        static  std::regex last_modified_regex (" Last-Modified"  , std::regex_constants::icase);
331- 
350+          static  std::regex  accept_ranges_regex ( " Accept-Ranges " , std::regex_constants::icase); 
332351        std::string header (buffer, n_items);
333352        std::smatch match;
334353        if  (std::regex_match (header, match, header_regex)) {
@@ -338,6 +357,8 @@ static bool common_download_file_single(const std::string & url, const std::stri
338357                headers->etag  = value;
339358            } else  if  (std::regex_match (key, match, last_modified_regex)) {
340359                headers->last_modified  = value;
360+             } else  if  (std::regex_match (key, match, accept_ranges_regex)) {
361+                 headers->accept_ranges  = value;
341362            }
342363        }
343364        return  n_items;
@@ -366,28 +387,48 @@ static bool common_download_file_single(const std::string & url, const std::stri
366387
367388    //  if head_request_ok is false, we don't have the etag or last-modified headers
368389    //  we leave should_download as-is, which is true if the file does not exist
390+     bool  should_download_from_scratch = false ;
369391    if  (head_request_ok) {
370392        //  check if ETag or Last-Modified headers are different
371393        //  if it is, we need to download the file again
372394        if  (!etag.empty () && etag != headers.etag ) {
373395            LOG_WRN (" %s: ETag header is different (%s != %s): triggering a new download\n "  , __func__, etag.c_str (), headers.etag .c_str ());
374396            should_download = true ;
397+             should_download_from_scratch = true ;
375398        } else  if  (!last_modified.empty () && last_modified != headers.last_modified ) {
376399            LOG_WRN (" %s: Last-Modified header is different (%s != %s): triggering a new download\n "  , __func__, last_modified.c_str (), headers.last_modified .c_str ());
377400            should_download = true ;
401+             should_download_from_scratch = true ;
378402        }
379403    }
380404
405+     const  bool  accept_ranges_supported = !headers.accept_ranges .empty () && headers.accept_ranges  != " none"  ;
381406    if  (should_download) {
382-         std::string path_temporary = path + " .downloadInProgress"  ;
383-         if  (file_exists) {
407+         if  (file_exists && !accept_ranges_supported) {  //  Resumable downloads not supported, delete and start again.
384408            LOG_WRN (" %s: deleting previous downloaded file: %s\n "  , __func__, path.c_str ());
385409            if  (remove (path.c_str ()) != 0 ) {
386410                LOG_ERR (" %s: unable to delete file: %s\n "  , __func__, path.c_str ());
387411                return  false ;
388412            }
389413        }
390414
415+         const  std::string path_temporary = path + " .downloadInProgress"  ;
416+         if  (should_download_from_scratch) {
417+             if  (std::filesystem::exists (path_temporary)) {
418+                 if  (remove (path_temporary.c_str ()) != 0 ) {
419+                     LOG_ERR (" %s: unable to delete file: %s\n "  , __func__, path_temporary.c_str ());
420+                     return  false ;
421+                 }
422+             }
423+ 
424+             if  (std::filesystem::exists (path)) {
425+                 if  (remove (path.c_str ()) != 0 ) {
426+                     LOG_ERR (" %s: unable to delete file: %s\n "  , __func__, path.c_str ());
427+                     return  false ;
428+                 }
429+             }
430+         }
431+ 
391432        //  Set the output file
392433
393434        struct  FILE_deleter  {
@@ -396,7 +437,8 @@ static bool common_download_file_single(const std::string & url, const std::stri
396437            }
397438        };
398439
399-         std::unique_ptr<FILE, FILE_deleter> outfile (fopen (path_temporary.c_str (), " wb"  ));
440+         //  Always open file in append mode could be resuming
441+         std::unique_ptr<FILE, FILE_deleter> outfile (fopen (path_temporary.c_str (), " ab"  ));
400442        if  (!outfile) {
401443            LOG_ERR (" %s: error opening local file for writing: %s\n "  , __func__, path.c_str ());
402444            return  false ;
@@ -431,7 +473,19 @@ static bool common_download_file_single(const std::string & url, const std::stri
431473        //  start the download
432474        LOG_INF (" %s: trying to download model from %s to %s (server_etag:%s, server_last_modified:%s)...\n "  , __func__,
433475            llama_download_hide_password_in_url (url).c_str (), path.c_str (), headers.etag .c_str (), headers.last_modified .c_str ());
434-         bool  was_perform_successful = curl_perform_with_retry (url, curl.get (), CURL_MAX_RETRY, CURL_RETRY_DELAY_SECONDS, " GET"  );
476+ 
477+         //  Write the updated JSON metadata file.
478+         metadata.update ({
479+             {" url"  , url},
480+             {" etag"  , headers.etag },
481+             {" lastModified"  , headers.last_modified }
482+         });
483+         write_file (metadata_path, metadata.dump (4 ));
484+         LOG_DBG (" %s: file metadata saved: %s\n "  , __func__, metadata_path.c_str ());
485+ 
486+         const  bool  was_perform_successful =
487+             curl_perform_with_retry (url, curl.get (), CURL_MAX_RETRY, CURL_RETRY_DELAY_SECONDS, " GET"  ,
488+                                     headers.accept_ranges .empty () ? " "   : path_temporary);
435489        if  (!was_perform_successful) {
436490            return  false ;
437491        }
@@ -446,15 +500,6 @@ static bool common_download_file_single(const std::string & url, const std::stri
446500        //  Causes file to be closed explicitly here before we rename it.
447501        outfile.reset ();
448502
449-         //  Write the updated JSON metadata file.
450-         metadata.update ({
451-             {" url"  , url},
452-             {" etag"  , headers.etag },
453-             {" lastModified"  , headers.last_modified }
454-         });
455-         write_file (metadata_path, metadata.dump (4 ));
456-         LOG_DBG (" %s: file metadata saved: %s\n "  , __func__, metadata_path.c_str ());
457- 
458503        if  (rename (path_temporary.c_str (), path.c_str ()) != 0 ) {
459504            LOG_ERR (" %s: unable to rename file: %s to %s\n "  , __func__, path_temporary.c_str (), path.c_str ());
460505            return  false ;
@@ -770,7 +815,7 @@ static std::string common_docker_get_token(const std::string & repo) {
770815}
771816
772817static  std::string common_docker_resolve_model (const  std::string & docker) {
773-     //  Parse ai/smollm2:135M-Q4_K_M 
818+     //  Parse ai/smollm2:135M-Q4_0 
774819    size_t       colon_pos = docker.find (' :'  );
775820    std::string repo, tag;
776821    if  (colon_pos != std::string::npos) {
0 commit comments