00001 #ifndef HEAVY_FILE_OPS_IMPLEMENTATION_FILE
00002 #define HEAVY_FILE_OPS_IMPLEMENTATION_FILE
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
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
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
00095 filename source_path(source);
00096 if (!source_path.exists()) return SOURCE_MISSING;
00097
00098
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
00104 huge_file source_file(source, "rb");
00105 if (!source_file.good()) return SOURCE_MISSING;
00106
00107
00108
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;
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
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);
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
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;
00174 if (indy == to_transfer.elements() - 1) return false;
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();
00187
00188 if (!to_transfer.elements()) {
00189
00190 return OKAY;
00191 }
00192
00193 outcome to_return = OKAY;
00194
00195
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
00201
00202 break;
00203 }
00204
00205
00206 if (!last_action._filename) {
00207
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
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
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
00230 if (!advance(to_transfer, last_action)) break;
00231 continue;
00232 }
00233
00234
00235
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
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
00249 if (!advance(to_transfer, last_action)) break;
00250 continue;
00251 }
00252
00253 }
00254
00255
00256 last_action._byte_start = new_start;
00257 last_action._length = int(new_len);
00258
00259
00260 last_action.pack(storage);
00261 storage += new_chunk;
00262
00263 if (!current.length()) {
00264
00265
00266 if (!advance(to_transfer, last_action)) break;
00267 continue;
00268 }
00269
00270
00271 }
00272
00273 return to_return;
00274 }
00275
00276
00277 #endif //HEAVY_FILE_OPS_IMPLEMENTATION_FILE
00278