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