33 int Gv::Ribbon::m_height = 12 ;
40 m_scan_base(scan_base) ,
44 const std::string prefix = name.empty() ? std::string() : ( name +
"." ) ;
45 m_match1 =
"/####/##/##/##/##/##/" + prefix +
"##" ;
46 m_match2 =
"/####/##/##/##/##/##/###/" + prefix +
"##" ;
51 const std::string prefix = name.empty() ? std::string() : ( name +
"." ) ;
52 std::string match1 =
"/####/##/##/##/##/##/" + prefix +
"##" ;
53 std::string match2 =
"/####/##/##/##/##/##/###/" + prefix +
"##" ;
54 return timeposImp( path , match1 , match2 ) ;
59 return timeposImp( path , m_match1 , m_match2 ) ;
62 size_t Gv::Ribbon::timeposImp(
const G::Path & path ,
const std::string & match1 ,
const std::string & match2 )
69 if( path_str.length() < match1.length() )
72 std::string::iterator
const end = path_str.end() ;
73 for( std::string::iterator p = path_str.begin() ; p != end ; ++p )
75 if( *p ==
'#' ) *p =
'_' ;
76 if( *p >=
'0' && *p <=
'9' ) *p =
'#' ;
79 size_t pos = path_str.find( match1 ) ;
80 if( pos != std::string::npos )
83 pos = path_str.find( match2 ) ;
84 return pos == std::string::npos ? 0U : (pos+1U) ;
87 void Gv::Ribbon::clear()
89 m_list.assign( m_list.size() , Item() ) ;
94 G_ASSERT( tpos != 0U ) ;
100 if( !m_list.empty() )
103 if( scanStart( file_tree , range ) )
110 G_ASSERT( tpos != 0U ) ;
111 return scanStart( file_tree ,
RibbonRange(trigger_path,tpos,m_tz) ) ;
124 G::Path start_path = m_range.startpath() ;
125 G_LOG(
"Gv::Ribbon::scan: ribbon: starting scan: base=[" << m_scan_base <<
"] start=[" << start_path <<
"]" ) ;
126 file_tree.
reroot( m_scan_base ) ;
136 unsigned int count = 0U ;
139 unsigned int ts = m_range.timestamp( path ) ;
140 if( ts == 0U ) continue ;
141 if( ts >= m_range.end() )
break ;
142 m_list.at(bucket(ts)).update( path , ts ) ;
145 G_LOG(
"Gv::Ribbon::scan: ribbon: partial scan: count=" << (count+1U) ) ;
150 G_LOG(
"Gv::Ribbon::scan: ribbon: scan complete: count=" << count ) ;
156 if( !m_list.empty() )
158 unsigned int ts = m_range.timestamp( path ) ;
162 bool outside_range = ts < m_range.start() || ts >= m_range.end() ;
166 m_range = m_range.around( path ) ;
169 size_t index = bucket( ts ) ;
170 Item & item = m_list.at( index ) ;
171 item.update( path , ts ) ;
172 if( !item.mark ) unmark() ;
175 return outside_range ;
183 void Gv::Ribbon::unmark()
185 const List::iterator end = m_list.end() ;
186 for( List::iterator p = m_list.begin() ; p != end ; ++p )
190 size_t Gv::Ribbon::bucket(
unsigned int ts )
const
192 unsigned int range = m_range.end() - m_range.start() ;
193 size_t index = (ts-m_range.start()) * m_list.size() ;
195 return std::min( index , m_list.size()-1U ) ;
200 List::const_iterator p = m_list.begin() ;
201 while( p != m_list.end() && (*p).first ==
G::Path() )
203 return p == m_list.end() ?
G::Path() : (*p).first ;
208 if( m_list.empty() )
return G::Path() ;
209 List::const_iterator p = m_list.begin() + (m_list.size()-1U) ;
210 while( p != m_list.begin() && (*p).first ==
G::Path() )
217 G_ASSERT( bucket < m_list.size() ) ;
218 List::const_iterator p = m_list.begin() + bucket ;
221 while( p != m_list.begin() && (*p).first ==
G::Path() )
223 return (*p).first ==
G::Path() ? first() : (*p).last ;
227 while( p != m_list.end() && (*p).first ==
G::Path() )
229 return p == m_list.end() ? last() : (*p).first ;
235 return m_list.size() ;
245 return timepos( path ) != 0U ;
250 return m_list.begin() ;
255 return m_list.end() ;
265 return m_range(offset) ;
273 m_startpath(
"/2000/01/01/00/00") ,
274 m_endpath(
"/2000/01/02/00/00")
284 const unsigned int day_seconds = 24U * 3600U ;
285 m_start = daystamp( bdt(trigger_path.
str(),tpos) , tz ) ;
286 m_end = m_start ? (m_start+day_seconds) : 0U ;
290 m_startpath = makePath( trigger_path , tpos ,
G::EpochTime(m_start) ) ;
291 m_endpath = makePath( trigger_path , tpos ,
G::EpochTime(m_end) ) ;
294 G_LOG(
"Gv::RibbonRange::ctor: ribbon: trigger-path=[" << trigger_path <<
"] " <<
"startpath=[" << m_startpath <<
"]" ) ;
304 const int day_seconds = 3600*24 ;
307 result.m_start = m_start + ( offset * day_seconds ) ;
308 result.m_end = m_end + ( offset * day_seconds ) ;
309 result.m_startpath = makePath( m_startpath , m_tpos ,
G::EpochTime(result.m_start) ) ;
310 result.m_endpath = makePath( m_startpath , m_tpos ,
G::EpochTime(result.m_end) ) ;
319 return trigger_path.
str().substr(0U,tpos) +
"/" + date.string() +
"/" + time.hhmm(
"/") ;
324 return m_start < epoch_base ? 0U : static_cast<unsigned int>(m_start-epoch_base) ;
329 return m_end < epoch_base ? 0U : static_cast<unsigned int>(m_end-epoch_base) ;
344 return timestamp( path , m_tpos ) ;
352 G::DateTime::BrokenDownTime tm = bdt( path.
str() , tpos ) ;
353 if( tm.tm_year == 0 )
357 if( t == std::time_t(-1) || t < epoch_base )
360 return static_cast<unsigned int>(t-epoch_base) ;
363 std::time_t Gv::RibbonRange::daystamp( G::DateTime::BrokenDownTime tm ,
const Gv::Timezone & tz )
365 const std::time_t day_seconds = 3600*24 ;
366 if( tm.tm_year == 0 )
return 0 ;
367 G_ASSERT( tm.tm_hour >= 0 && tm.tm_min >= 0 ) ;
370 std::time_t day_offset = std::time_t(tm.tm_hour)*3600 + std::time_t(tm.tm_min)*60 + tm.tm_sec ;
371 tm.tm_hour = tm.tm_min = tm.tm_sec = 0 ;
373 if( start_of_day == std::time_t(-1) )
return 0 ;
380 start_of_day -= day_seconds ;
381 else if( tz.
seconds() < 0 && day_offset > (day_seconds+tz.
seconds()) )
382 start_of_day += day_seconds ;
384 return start_of_day ;
387 G::DateTime::BrokenDownTime Gv::RibbonRange::bdt(
const std::string & path ,
size_t tpos )
393 G_ASSERT( tpos < path.length() ) ;
394 G_ASSERT( path.at(tpos+4U) ==
'/' && path.at(tpos+7U) ==
'/' ) ;
395 G_ASSERT( path.at(tpos+10U) ==
'/' && path.at(tpos+13U) ==
'/' ) ;
396 G_ASSERT( path.at(tpos+16U) ==
'/' ) ;
398 const char * p = path.data() + tpos ;
400 unsigned int yy = to_int( p[2] , p[3] ) ;
401 unsigned int mm = to_int( p[5] , p[6] ) ;
402 unsigned int dd = to_int( p[8] , p[9] ) ;
403 unsigned int HH = to_int( p[11], p[12] ) ;
404 unsigned int MM = to_int( p[14], p[15] ) ;
408 mm >= 1U && mm <= 12U &&
409 dd >= 1U && mm <= 31U &&
420 tm.tm_mon = mm - 1U ;
421 tm.tm_year = yy + 100U ;
429 unsigned int Gv::RibbonRange::to_int(
char a ,
char b ,
unsigned int error )
431 if( a < '0' || a >
'9' || b < '0' || b >
'9' )
return error ;
432 unsigned int hi =
static_cast<unsigned int>(a-
'0') ;
433 unsigned int lo =
static_cast<unsigned int>(b-
'0') ;
434 return hi * 10U + lo ;
439 Gv::Ribbon::Item::Item() :
446 void Gv::Ribbon::Item::update(
const G::Path & path ,
unsigned int ts )
448 G_ASSERT( path !=
G::Path() && ts != 0U && (first_ts==0U) == (first==
G::Path()) ) ;
451 first = last = path ;
452 first_ts = last_ts = ts ;
454 else if( ts < first_ts || ( ts == first_ts && path.
str() <= first.str() ) )
459 else if( ts > last_ts || ( ts == last_ts && path.
str() > last.str() ) )
466 bool Gv::Ribbon::Item::set()
const
468 return first_ts != 0U ;
471 bool Gv::Ribbon::Item::includes(
const G::Path & path )
const
473 return set() && path.
str() >= first.str() && path.
str() <= last.str() ;
476 bool Gv::Ribbon::Item::marked()
const
static BrokenDownTime utc(EpochTime epoch_time)
Converts from epoch time to UTC broken-down-time.
std::string str() const
Returns the path string.
A subsecond-resolution timestamp based on a time_t.
void scan(const G::Path &path, size_t tpos)
Does a complete scan for the day that encompases the given timestamped() file.
RibbonRange around(const G::Path &) const
Returns a range includes the given timestamped trigger path.
A date (dd/mm/yyyy) class.
bool apply(const G::Path &)
Called when a possibly-new timestamped file is encountered, allowing the ribbon to update itself for ...
A class for walking files in a directory tree, with repositioning.
Path withoutExtension() const
Returns a path without the basename extension, if any.
std::time_t seconds() const
Returns the offset as a signed number of seconds.
A representation of a timezone.
RibbonRange()
Default constructor for an unusable object.
void reroot(const G::Path &root)
Resets the root, as if newly constructed.
A simple time-of-day (hh/mm/ss) class.
G::Path last() const
Returns the last scanned path.
bool reposition(const G::Path &path)
Repositions the iterator within the current tree, at or after the given position. ...
List::const_iterator begin() const
Returns the item list's begin iterator.
RibbonRange operator()(int offset) const
Returns a range that is offset from the current range.
static EpochTime now()
Returns the current epoch time.
unsigned int end() const
Returns the timestamp() value for the start of the next day.
G::Path first() const
Returns the first scanned path.
Represents a day span within the file store, used by Gv::Ribbon.
const RibbonRange & range() const
Returns the current day range.
static int height()
Returns a suggested height of the ribbon, in pixels.
static size_t timepos(const G::Path &path, const std::string &name)
Returns the timestamp position within the given path, or zero if none.
G::Path current() const
Returns the current path.
size_t size() const
Returns the size of the list, as passed in to the constructor.
G::Path next()
Moves to the next file in the tree, depth first, and returns the path.
unsigned int start() const
Returns the timestamp() value for the start of the day.
bool scanSome(G::FileTree &, G::EpochTime interval)
Does a partial scan, limited by the given time interval.
G::Path startpath() const
Returns the directory path that is the start of the day at the minutes level (eg. ...
List::const_iterator end() const
Returns the item list's end iterator.
static EpochTime epochTime(const BrokenDownTime &broken_down_time)
Converts from UTC broken-down-time to epoch time.
unsigned int timestamp(const G::Path &) const
Returns the time value for the given path, as seconds within some arbitrary epoch.
bool scanStart(G::FileTree &, const RibbonRange &range)
Prepares for an iterative scan, with subsequent calls to scanSome().
G::Path find(size_t offset, bool before=false) const
Finds the first scanned path at-or-after the given bucket position, or returns the last() path if the...
bool timestamped(const G::Path &path) const
Returns true if the given absolute path has a non-zero timepos().
G::Path endpath() const
Returns the directory path that is the start of the next day at the minutes level.
A Path object represents a file system path.
A time bucket within a Gv::Ribbon.
Ribbon()
Default constructor for a zero-size() ribbon.