heavy_file_ops.cpp

Go to the documentation of this file.
00001 #ifndef HEAVY_FILE_OPS_IMPLEMENTATION_FILE
00002 #define HEAVY_FILE_OPS_IMPLEMENTATION_FILE
00003 
00004 /*****************************************************************************\
00005 *                                                                             *
00006 *  Name   : heavy file operations                                             *
00007 *  Author : Chris Koeritz                                                     *
00008 *                                                                             *
00009 *******************************************************************************
00010 * Copyright (c) 2005-$now By Author.  This program is free software; you can  *
00011 * redistribute it and/or modify it under the terms of the GNU General Public  *
00012 * License as published by the Free Software Foundation; either version 2 of   *
00013 * the License or (at your option) any later version.  This is online at:      *
00014 *     http://www.fsf.org/copyleft/gpl.html                                    *
00015 * Please send any updates to: fred@gruntose.com                               *
00016 \*****************************************************************************/
00017 
00018 #include "directory.h"
00019 #include "filename.h"
00020 #include "filename_list.h"
00021 #include "heavy_file_ops.h"
00022 #include "huge_file.h"
00023 
00024 #include <basis/function.h>
00025 #include <basis/guards.h>
00026 #include <basis/log_base.h>
00027 
00028 using namespace basis;
00029 
00030 #undef LOG
00031 #define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger(), s)
00032 
00034 
00035 file_transfer_header::file_transfer_header()
00036 : _filename(),
00037   _byte_start(0),
00038   _length(0)
00039 {
00040 }
00041 
00042 istring file_transfer_header::text_form() const
00043 {
00044   return istring("file=") + _filename
00045       + isprintf(" start=%d len=%d", _byte_start, _length);
00046 }
00047 
00048 void file_transfer_header::pack(byte_array &packed_form) const
00049 {
00050   _filename.pack(packed_form);
00051   attach(packed_form, _byte_start);
00052   attach(packed_form, _length);
00053 }
00054 
00055 bool file_transfer_header::unpack(byte_array &packed_form)
00056 {
00057   if (!_filename.unpack(packed_form)) return false;
00058   if (!detach(packed_form, _byte_start)) return false;
00059   if (!detach(packed_form, _length)) return false;
00060   return true;
00061 }
00062 
00063 int file_transfer_header::packed_size() const
00064 {
00065   return _filename.length() + 1
00066       + sizeof(int)
00067       + sizeof(int);
00068 }
00069 
00071 
00072 const size_t heavy_file_operations::COPY_CHUNK_FACTOR = 1 * MEGABYTE;
00073 
00074 const size_t heavy_file_operations::copy_chunk_factor()
00075 { return COPY_CHUNK_FACTOR; }
00076 
00077 heavy_file_operations::~heavy_file_operations() {}
00078   // we only need this due to our use of the object_base class_name support.
00079 
00080 const char *heavy_file_operations::outcome_name(const outcome &to_name)
00081 {
00082   switch (to_name.value()) {
00083     case SOURCE_MISSING: return "SOURCE_MISSING";
00084     case TARGET_ACCESS_ERROR: return "TARGET_ACCESS_ERROR";
00085     case TARGET_DIR_ERROR: return "TARGET_DIR_ERROR";
00086     default: return common::outcome_name(to_name);
00087   }
00088 }
00089 
00090 outcome heavy_file_operations::copy_file(const istring &source,
00091     const istring &destination, int copy_chunk_factor)
00092 {
00093   FUNCDEF("copy_file");
00094   // check that the source exists...
00095   filename source_path(source);
00096   if (!source_path.exists()) return SOURCE_MISSING;
00097 
00098   // make sure the target directory exists...
00099   filename target_path(destination);
00100   filename targ_dir = target_path.dirname();
00101   if (!directory::recursive_create(targ_dir.raw())) return TARGET_DIR_ERROR;
00102 
00103   // open the source for reading.
00104   huge_file source_file(source, "rb");
00105   if (!source_file.good()) return SOURCE_MISSING;
00106 //hmmm: could be source is not accessible instead.
00107 
00108   // open target file for writing.
00109   huge_file target_file(destination, "wb");
00110   if (!target_file.good()) return TARGET_ACCESS_ERROR;
00111 
00112   byte_array chunk;
00113   int bytes_read = 0;
00114   outcome ret;
00115   while ( (ret = source_file.read(chunk, copy_chunk_factor, bytes_read))
00116       == huge_file::OKAY) {
00117     int bytes_stored;
00118     ret = target_file.write(chunk, bytes_stored);
00119     if (bytes_stored != bytes_read) return TARGET_ACCESS_ERROR;
00120     if (source_file.eof()) break;  // time to escape.
00121   }
00122 
00123   return OKAY;
00124 }
00125 
00126 outcome heavy_file_operations::write_file_chunk(const istring &target,
00127     double byte_start, const byte_array &chunk, bool truncate,
00128     int copy_chunk_factor)
00129 {
00130   FUNCDEF("write_file_chunk");
00131   if (byte_start < 0) return BAD_INPUT;
00132 
00133   filename targ_name(target);
00134   if (!directory::recursive_create(targ_name.dirname().raw()))
00135     return TARGET_DIR_ERROR;
00136 
00137   if (!targ_name.exists()) {
00138     huge_file target_file(target, "w");
00139   }
00140 
00141   huge_file target_file(target, "r+b");
00142     // open the file for updating (either read or write).
00143   if (!target_file.good()) return TARGET_ACCESS_ERROR;
00144   double curr_len = target_file.length();
00145 
00146   if (curr_len < byte_start) {
00147     byte_array new_chunk;
00148     while (curr_len < byte_start) {
00149       target_file.seek(0, byte_filer::FROM_END);  // go to the end of the file.
00150       new_chunk.reset(minimum(copy_chunk_factor,
00151           int(curr_len - byte_start + 1)));
00152       int written;
00153       outcome ret = target_file.write(new_chunk, written);
00154       if (written < new_chunk.length()) return TARGET_ACCESS_ERROR;
00155       curr_len = target_file.length();
00156     }
00157   }
00158   target_file.seek(byte_start, byte_filer::FROM_START);
00159     // jump to the proper location in the file.
00160   int wrote;
00161   outcome ret = target_file.write(chunk, wrote);
00162   if (wrote != chunk.length()) return TARGET_ACCESS_ERROR;
00163   if (truncate) {
00164     target_file.truncate();
00165   }
00166   return OKAY;
00167 }
00168 
00169 bool advance(const filename_list &to_transfer,
00170     file_transfer_header &last_action)
00171 {
00172   int indy = to_transfer.locate(last_action._filename);
00173   if (negative(indy)) return false;  // error.
00174   if (indy == to_transfer.elements() - 1) return false;  // done.
00175   last_action._filename = to_transfer.get(indy + 1)->raw();
00176   last_action._byte_start = 0;
00177   last_action._length = 0;
00178   return true;
00179 }
00180 
00181 outcome heavy_file_operations::buffer_files(const istring &source_root,
00182     const filename_list &to_transfer, file_transfer_header &last_action,
00183     byte_array &storage, int maximum_bytes)
00184 {
00185   FUNCDEF("buffer_files");
00186   storage.reset();  // clear out the current contents.
00187 
00188   if (!to_transfer.elements()) {
00189     // we seem to be done.
00190     return OKAY;
00191   }
00192 
00193   outcome to_return = OKAY;
00194 
00195   // start filling the array with bytes from the files.
00196   while (storage.length() < maximum_bytes) {
00197     double remaining_in_array = maximum_bytes - storage.length()
00198         - last_action.packed_size();
00199     if (remaining_in_array < 128) {
00200       // ensure that we at least have a reasonable amount of space left
00201       // for storing into the array.
00202       break;
00203     }
00204 
00205     // find the current file we're at, as provided in record.
00206     if (!last_action._filename) {
00207       // no filename yet.  assume this is the first thing we've done.
00208       last_action._filename = to_transfer.get(0)->raw();
00209       last_action._byte_start = 0;
00210       last_action._length = 0;
00211     }
00212 
00213     const file_info *found = to_transfer.find(last_action._filename);
00214     if (!found) {
00215       // they have referenced a file that we don't have.  that's bad news.
00216       return BAD_INPUT;
00217     }
00218 
00219     istring full_file = source_root + "/" + last_action._filename;
00220     huge_file current(full_file, "rb");
00221     if (!current.good()) {
00222       // we need to skip this file.
00223       if (!advance(to_transfer, last_action)) break;
00224       continue;
00225     }
00226 
00227     if ((last_action._byte_start + last_action._length >= current.length())
00228         && current.length()) {
00229       // this file is done now.  go to the next one.
00230       if (!advance(to_transfer, last_action)) break;
00231       continue;
00232     }
00233 
00234     // calculate the largest piece remaining of that file that will fit in the
00235     // allotted space.
00236     double new_start = last_action._byte_start + last_action._length;
00237     double remaining_in_file = current.length() - new_start;
00238     if (remaining_in_file < 0) remaining_in_file = 0;
00239     double new_len = minimum(remaining_in_file, remaining_in_array);
00240     
00241     // pack this new piece of the file.
00242     current.seek(new_start, byte_filer::FROM_START);
00243     byte_array new_chunk;
00244     int bytes_read = 0;
00245     outcome ret = current.read(new_chunk, int(new_len), bytes_read);
00246     if (bytes_read != new_len) {
00247       if (!bytes_read) {
00248         // some kind of problem reading the file.
00249         if (!advance(to_transfer, last_action)) break;
00250         continue;
00251       }
00252 //why would this happen?  just complain, i guess.
00253     }
00254 
00255     // update the record since it seems we're successful here.
00256     last_action._byte_start = new_start;
00257     last_action._length = int(new_len);
00258 
00259     // add in this next new chunk of file.
00260     last_action.pack(storage);  // add the header.
00261     storage += new_chunk;  // add the new stuff.
00262 
00263     if (!current.length()) {
00264       // ensure we don't get stuck redoing zero length files, which we allowed
00265       // to go past their end above (since otherwise we'd never see them).
00266       if (!advance(to_transfer, last_action)) break;
00267       continue;
00268     }
00269     
00270     // just keep going, if there's space...
00271   }
00272 
00273   return to_return;
00274 }
00275 
00276 
00277 #endif //HEAVY_FILE_OPS_IMPLEMENTATION_FILE
00278 

Generated on Sat Oct 11 04:28:49 2008 for HOOPLE Libraries by  doxygen 1.5.1