1 /*********************************************************************/ 2 // dar - disk archive - a backup/restoration program 3 // Copyright (C) 2002-2052 Denis Corbin 4 // 5 // This program is free software; you can redistribute it and/or 6 // modify it under the terms of the GNU General Public License 7 // as published by the Free Software Foundation; either version 2 8 // of the License, or (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU General Public License for more details. 14 // 15 // You should have received a copy of the GNU General Public License 16 // along with this program; if not, write to the Free Software 17 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 // 19 // to contact the author : http://dar.linux.free.fr/email.html 20 /*********************************************************************/ 21 22 #include "../my_config.h" 23 24 25 #include "datetime.hpp" 26 27 using namespace std; 28 29 namespace libdar 30 { 31 32 static const infinint one_unit = 1; 33 static const infinint one_thousand = 1000; 34 static const infinint one_million = one_thousand*one_thousand; 35 static const infinint one_billion = one_million*one_thousand; 36 datetime(time_t second,time_t subsec,time_unit unit)37 datetime::datetime(time_t second, time_t subsec, time_unit unit) 38 { 39 build(infinint(second), infinint(subsec), unit); 40 } 41 operator <(const datetime & ref) const42 bool datetime::operator < (const datetime & ref) const 43 { 44 if(uni <= ref.uni && val < ref.val) 45 return true; 46 47 if(uni < ref.uni) 48 { 49 infinint newval, reste; 50 euclide(val, get_scaling_factor(ref.uni, uni), newval, reste); 51 return newval < ref.val; 52 } 53 54 if(uni == ref.uni) 55 return val < ref.val; 56 else 57 { 58 // uni > ref.uni 59 infinint newval, reste; 60 euclide(ref.val, get_scaling_factor(uni, ref.uni), newval, reste); 61 return (val == newval && !reste.is_zero()) 62 || val < newval; 63 } 64 } 65 operator ==(const datetime & ref) const66 bool datetime::operator == (const datetime & ref) const 67 { 68 return uni == ref.uni && val == ref.val; 69 // fields are always reduced to the larger possible unit 70 } 71 operator -=(const datetime & ref)72 void datetime::operator -= (const datetime & ref) 73 { 74 if(ref.uni < uni) 75 { 76 val *= get_scaling_factor(uni, ref.uni); 77 uni = ref.uni; 78 } 79 80 if(ref.uni == uni) 81 { 82 if(val < ref.val) 83 throw SRC_BUG; 84 val -= ref.val; 85 } 86 else // ref.uni > uni 87 { 88 infinint tmp = ref.val * get_scaling_factor(ref.uni, uni); 89 if(tmp > val) 90 throw SRC_BUG; 91 val -= tmp; 92 } 93 94 reduce_to_largest_unit(); 95 } 96 operator +=(const datetime & ref)97 void datetime::operator += (const datetime & ref) 98 { 99 if(ref.uni < uni) 100 { 101 val *= get_scaling_factor(uni, ref.uni); 102 uni = ref.uni; 103 } 104 105 if(ref.uni == uni) 106 val += ref.val; 107 else // ref.uni > uni 108 { 109 infinint tmp = ref.val * get_scaling_factor(ref.uni, uni); 110 val += tmp; 111 } 112 113 reduce_to_largest_unit(); 114 } 115 loose_equal(const datetime & ref) const116 bool datetime::loose_equal(const datetime & ref) const 117 { 118 if(uni == ref.uni) 119 return val == ref.val; 120 else 121 { 122 time_unit tu = max(uni, ref.uni); 123 infinint val1, val2; 124 125 if(uni < tu) 126 val1 = val / get_scaling_factor(tu, uni); 127 else 128 val1 = val; 129 130 if(ref.uni < tu) 131 val2 = ref.val / get_scaling_factor(tu, ref.uni); 132 else 133 val2 = ref.val; 134 135 return val1 == val2; 136 } 137 } 138 loose_diff(const datetime & ref) const139 datetime datetime::loose_diff(const datetime & ref) const 140 { 141 #if LIBDAR_MICROSECOND_READ_ACCURACY && LIBDAR_MICROSECOND_WRITE_ACCURACY 142 static const time_unit max_capa = tu_microsecond; 143 #else 144 static const time_unit max_capa = tu_second; 145 #endif 146 datetime ret; 147 infinint aux; 148 149 // using the less precised unit to avoid loosing accuracy 150 ret.uni = max(uni, ref.uni); 151 152 if(ret.uni < max_capa) 153 ret.uni = max_capa; 154 155 if(uni < ret.uni) 156 ret.val = val / get_scaling_factor(ret.uni, uni); 157 else 158 ret.val = val; 159 160 if(ref.uni < ret.uni) 161 aux = ref.val / get_scaling_factor(ret.uni, ref.uni); 162 else 163 aux = ref.val; 164 165 if(ret.val < aux) 166 throw SRC_BUG; // negative date would result of the operation 167 ret.val -= aux; 168 ret.reduce_to_largest_unit(); 169 170 return ret; 171 } 172 get_storage_size() const173 infinint datetime::get_storage_size() const 174 { 175 infinint sec, sub, size; 176 get_value(sec, sub, uni); 177 178 size = sec.get_storage_size(); 179 if(uni < tu_second) 180 size += sub.get_storage_size() + 1; 181 182 return size; 183 } 184 reduce_to_largest_unit() const185 void datetime::reduce_to_largest_unit() const 186 { 187 infinint newval, reste; 188 datetime *me = const_cast<datetime *>(this); 189 190 if(me == nullptr) 191 throw SRC_BUG; 192 193 if(val.is_zero()) 194 { 195 if(uni != tu_second) 196 me->uni = tu_second; 197 } 198 else 199 { 200 switch(uni) 201 { 202 case tu_nanosecond: 203 euclide(val, get_scaling_factor(tu_microsecond, uni), newval, reste); 204 if(!reste.is_zero()) 205 break; // cannot reduce the unit further 206 else 207 { 208 me->val = newval; 209 me->uni = tu_microsecond; 210 } 211 /* no break ! */ 212 case tu_microsecond: 213 euclide(val, get_scaling_factor(tu_second, uni), newval, reste); 214 if(!reste.is_zero()) 215 break; // cannot reduce the unit further 216 else 217 { 218 me->val = newval; 219 me->uni = tu_second; 220 } 221 /* no break ! */ 222 case tu_second: 223 // cannot reduce further as 224 // this is the largest known time unit 225 // so we break here 226 break; 227 default: 228 throw SRC_BUG; 229 } 230 } 231 } 232 get_value(infinint & sec,infinint & sub,time_unit unit) const233 void datetime::get_value(infinint & sec, infinint & sub, time_unit unit) const 234 { 235 euclide(val, get_scaling_factor(tu_second, uni), sec, sub); 236 if(unit < uni) 237 sub *= get_scaling_factor(uni, unit); 238 if(unit > uni) 239 sub /= get_scaling_factor(unit, uni); 240 } 241 build(const infinint & sec,const infinint & sub,time_unit unit)242 void datetime::build(const infinint & sec, const infinint & sub, time_unit unit) 243 { 244 bool loop = false; 245 infinint subsec = sub; 246 247 do 248 { 249 try 250 { 251 if(tu_second == unit) 252 val = sec; 253 // this saves an infinint multiplication and fixes 254 // a bug when reading an archive generated by dar 2.4.x or 255 // below that included a file for which the system returned 256 // a negative date, which was read by libdar as a huge positive 257 // number, that much that multiplying it by 1 triggers the 258 // limiting overflow mecanism 259 else 260 val = sec*get_scaling_factor(tu_second, unit) + subsec; 261 uni = unit; 262 loop = false; 263 } 264 catch(Elimitint& e) 265 { 266 switch(unit) 267 { 268 case tu_nanosecond: 269 unit = tu_microsecond; 270 subsec = subsec/1000; 271 break; 272 case tu_microsecond: 273 unit = tu_second; 274 subsec = subsec/1000; 275 break; 276 case tu_second: 277 throw; 278 default: 279 throw SRC_BUG; 280 } 281 loop = true; 282 } 283 } 284 while(loop); 285 reduce_to_largest_unit(); 286 } 287 288 get_subsecond_value(time_unit unit) const289 infinint datetime::get_subsecond_value(time_unit unit) const 290 { 291 infinint ret, tmp; 292 293 get_value(tmp, ret, unit); 294 295 return ret; 296 } 297 get_value(time_t & second,time_t & subsecond,time_unit unit) const298 bool datetime::get_value(time_t & second, time_t & subsecond, time_unit unit) const 299 { 300 infinint sub, sec; 301 302 get_value(sec, sub, unit); 303 304 second = 0; 305 sec.unstack(second); 306 if(!sec.is_zero()) 307 return false; 308 309 subsecond = 0; 310 sub.unstack(subsecond); 311 if(!sub.is_zero()) 312 return false; 313 314 return true; 315 } 316 dump(generic_file & x) const317 void datetime::dump(generic_file &x) const 318 { 319 char tmp; 320 infinint sec, sub; 321 322 get_value(sec, sub, uni); 323 tmp = time_unit_to_char(uni); 324 325 // we keep storing: 326 // - a first flag telling the unit 327 // - an infinint telling the amount of seconds 328 // - an other infinint telling the amount of subsecond additional time expressed in the unit of the flag 329 x.write(&tmp, 1); 330 sec.dump(x); 331 if(uni < tu_second) 332 sub.dump(x); 333 } 334 read(generic_file & f,archive_version ver)335 void datetime::read(generic_file &f, archive_version ver) 336 { 337 infinint sec, sub; 338 339 if(ver < 9) 340 uni = tu_second; 341 else 342 { 343 char tmp; 344 f.read(&tmp, 1); 345 uni = char_to_time_unit(tmp); 346 } 347 348 sec.read(f); 349 if(uni < tu_second) 350 sub.read(f); 351 else 352 sub = 0; 353 build(sec, sub, uni); 354 } 355 min(time_unit a,time_unit b)356 datetime::time_unit datetime::min(time_unit a, time_unit b) 357 { 358 if(a < b) 359 return a; 360 else 361 return b; 362 } 363 max(time_unit a,time_unit b)364 datetime::time_unit datetime::max(time_unit a, time_unit b) 365 { 366 if(a < b) 367 return b; 368 else 369 return a; 370 } 371 time_unit_to_char(time_unit a)372 const char datetime::time_unit_to_char(time_unit a) 373 { 374 switch(a) 375 { 376 case tu_nanosecond: 377 return 'n'; 378 case tu_microsecond: 379 return 'u'; 380 case tu_second: 381 return 's'; 382 default: 383 throw SRC_BUG; 384 } 385 } 386 char_to_time_unit(const char a)387 datetime::time_unit datetime::char_to_time_unit(const char a) 388 { 389 switch(a) 390 { 391 case 'n': 392 return tu_nanosecond; 393 case 's': 394 return tu_second; 395 case 'u': 396 return tu_microsecond; 397 default: 398 throw Erange("datetime::time_unit", gettext("Unknown time unit")); 399 } 400 } 401 get_scaling_factor(time_unit source,time_unit dest)402 const infinint & datetime::get_scaling_factor(time_unit source, time_unit dest) 403 { 404 if(dest > source) 405 throw SRC_BUG; 406 407 switch(source) 408 { 409 case tu_second: 410 if(dest == tu_second) 411 return one_unit; 412 else if(dest == tu_microsecond) 413 return one_million; 414 else if(dest == tu_nanosecond) 415 return one_billion; 416 else 417 throw SRC_BUG; // unknown dest unit! 418 case tu_microsecond: 419 if(dest == tu_microsecond) 420 return one_unit; 421 else if(dest == tu_nanosecond) 422 return one_thousand; 423 else 424 throw SRC_BUG; // unknown dest unit! 425 case tu_nanosecond: 426 if(dest == tu_nanosecond) 427 return one_unit; 428 else 429 throw SRC_BUG; // unknown dest unit! 430 default: 431 throw SRC_BUG; 432 } 433 } 434 db2archive_version(unsigned char db_version)435 archive_version db2archive_version(unsigned char db_version) 436 { 437 // the class datetime read() method is based on dar archive version. 438 // here we know the database version (dar_manager). Starting with version 4 (release 2.5.0) 439 // time is no more stored as an integer. This correspond to dar archive version 9 440 // and above (release 2.5.0 too), wherefrom this hack below: 441 return db_version > 3 ? archive_version(9,0) : archive_version(8,0); 442 } 443 444 445 } // end of namespace 446