-
Notifications
You must be signed in to change notification settings - Fork 0
/
_gr_sril.h
494 lines (433 loc) · 17.1 KB
/
_gr_sril.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
#ifndef __GR_SRIL
#define __GR_SRIL
// Copyright David Lawrence Bien 1997 - 2021.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt).
// _gr_sril.h
// This module declares objects used during output serialization of the graph.
__DGRAPH_BEGIN_NAMESPACE
// When !t_fUseSeek we don't attempt to tell/seek the stream - this is not throw-safe -
// i.e. state may not be restored appropriately. It is throw-safe - put it is not state-safe
// upon throw.
template < class t_TyOutputStreamBase,
class t_TyAllocator,
bool t_fUseSeek = true > // By default we use the seek.
struct _graph_output_iter_base
: public _graph_fwd_iter_base< typename t_TyOutputStreamBase::_TyGraphNodeBase,
typename t_TyOutputStreamBase::_TyGraphLinkBase,
t_TyAllocator, false >
{
private:
typedef _graph_output_iter_base< t_TyOutputStreamBase,
t_TyAllocator, t_fUseSeek > _TyThis;
typedef _graph_fwd_iter_base< typename t_TyOutputStreamBase::_TyGraphNodeBase,
typename t_TyOutputStreamBase::_TyGraphLinkBase,
t_TyAllocator, false > _TyBase;
public:
typedef typename t_TyOutputStreamBase::_TyGraphLinkBase _TyGraphLinkBase;
typedef typename t_TyOutputStreamBase::_TyGraphNodeBase _TyGraphNodeBase;
typedef typename _TyBase::_TyPMFnNotifyUnfinished _TyPMFnNotifyUnfinished;
// Output stream object - supports some types and functionality - allows dumping
// ( for instance ) in both human readable as well as binary forms. Also allows
// output ( for instance ) to a FILE*, an istream, or perhaps OLE structured storage.
// The output stream supports:
// _TyStreamPos - type for position within the stream ( allows reading/setting ).
// An set of methods that permit:
// _TyStreamPos _Tell(void).
// void _Seek( _TyStreamPos ).
t_TyOutputStreamBase & m_ros;
typedef typename t_TyOutputStreamBase::_TyStreamPos _TyStreamPos;
bool m_fNewUnfinished;
_TyGraphLinkBase * m_pglbUnfinished; // The link the entered into the last unfinished node.
int m_iContextsOutput; // Our idea of the current number of contexts.
bool m_fDirectionDownOutput; // Our idea of the current direction of iteration.
bool m_fWroteLinkFooter; // This used when we are not seeking.
_TyStreamPos m_spBeforeLinkFooter;
bool m_fWroteGraphFooter;
// Derived settable callbacks - this allows the caller to know the types of links and nodes
// ( keeps code size smaller in the presence of multiply typed graphs ).
typedef void ( _TyThis :: * _TyPMFnWriteNode )();
_TyPMFnWriteNode m_pmfnWriteNode;
typedef void ( _TyThis :: * _TyPMFnWriteLink )();
_TyPMFnWriteLink m_pmfnWriteLink;
protected:
void _Init()
{
// Set up callback:
_TyBase::m_pmfnNotifyUnfinished = static_cast< _TyPMFnNotifyUnfinished >( &_TyThis::_NotifyUnfinished );
m_fNewUnfinished = false;
if ( !_TyBase::m_fInitialized )
{
if ( _TyBase::m_pgnbCur || _TyBase::m_pglbCur )
{
_TyBase::_Init();
}
else
{
_TyBase::_ClearState();
}
}
}
void _Seek( _TyStreamPos _sp )
{
if ( t_fUseSeek )
{
m_ros._Seek( _sp );
}
}
void _Tell( _TyStreamPos & _sp )
{
if ( t_fUseSeek )
{
_sp = m_ros._Tell( );
}
}
public:
_graph_output_iter_base( t_TyOutputStreamBase & _ros,
_TyGraphNodeBase * _pgnbCur,
_TyGraphLinkBase * _pglbCur,
bool _fClosedDirected,
bool _fDirectionDown,
t_TyAllocator const & _rAlloc )
: _TyBase( _pgnbCur, _pglbCur, _fClosedDirected, _fDirectionDown, _rAlloc, false ),
m_ros( _ros ),
m_iContextsOutput( 0 ),
m_fDirectionDownOutput( !_fDirectionDown ), // Force a direction record to be written immediately.
m_fWroteGraphFooter( false )
{
_Init();
}
#if 0 // This is too poorly defined now - may b able to define better later.
explicit _graph_output_iter_base( _TyThis const & _r,
t_TyOutputStreamBase & _ros )
: _TyBase( _r ),
m_ros( _ros ),
m_iContextsOutput( 0 )
{
Assert( &m_ros != &_r.m_ros );
}
#endif //0
// Allow initialization with the base - this allows us to skip sections of the graph
// using a graph iterator and then initialize the output iterator with the state
// of this iterator and start the serialization from there ( this requires special
// consideration on read ).
// This constructor also expensive.
explicit _graph_output_iter_base( _TyBase const & _r,
t_TyOutputStreamBase & _ros )
: _TyBase( _r, false ),
m_ros( _ros ),
m_iContextsOutput( _r.m_iContexts ),
m_fDirectionDownOutput( !_r.m_fDirectionDown ),// Force a direction record to be written immediately.
m_fWroteGraphFooter( false )
{
_Init();
}
// Initialization with the base's base class - this allows us to initialize the
// output iterator with a iterator_pos ( i.e. dgraph::begin() ).
explicit _graph_output_iter_base(typename _TyBase::_TyFwdIterBaseBase const & _r,
t_TyOutputStreamBase & _ros )
: _TyBase( _r, false ),
m_ros( _ros ),
m_iContextsOutput( 0 ),
m_fDirectionDownOutput( !_r.m_fDirectionDown ),// Force a direction record to be written immediately.
m_fWroteGraphFooter( false )
{
_Init();
}
~_graph_output_iter_base() _BIEN_NOTHROW
{
}
// REVIEW: Comparison with end() will return true even if the footer has not been written.
// May want to override and fix this somehow.
bool FAtEnd()
{
return _TyBase::FAtEnd() && m_fWroteGraphFooter;
}
// This should only before the iteration is begun:
void SetDirection( bool _fDirectionDown )
{
if ( _fDirectionDown != _TyBase::m_fDirectionDown )
{
Assert( !m_iContextsOutput );
m_fNewUnfinished = false; // Clear any state.
m_ros._SetDirection( _fDirectionDown );
_TyBase::SetDirection( _fDirectionDown );
m_fDirectionDownOutput = !_fDirectionDown; // Force a direction record to be written immed.
}
}
_TyGNIndex _NotifyUnfinished( bool _fNew, _TyGraphNodeBase * _pgnb, _TyGraphLinkBase * _pglb )
{
if ( _fNew )
{
m_fNewUnfinished = true;
m_pglbUnfinished = _pglb;
return 1; // Return bogus count - we will count during output.
}
else
{
Assert( _pglb );
Assert( _pgnb );
// We merely re-write the link footer here - it has already been written in
// a default manner:
if ( t_fUseSeek )
{
m_ros._Seek( m_spBeforeLinkFooter );
}
else
{
// Then indicate that we wrote the footer:
m_fWroteLinkFooter = true;
}
m_ros._WriteLinkFooter( _TyBase::_FAtUnfinishedNode() ? 0 : _pglb, _pgnb ); // Indicate connection to _pgnb.
return 1; // Return value is ignored.
}
}
// This method does all the work.
void _Next()
{
// We write the current state and then we change it - first we record where
// we are right now in case we throw.
_TyStreamPos sp;
_Tell( sp );
// We can manage the context writing separately from the node and link writing
// irrespective of exceptions that may occur afterwards.
// If we pushed or popped a context since the last check then update stream:
if ( m_iContextsOutput != _TyBase::m_iContexts )
{
_BIEN_TRY
{
m_ros._WriteContext( m_iContextsOutput < _TyBase::m_iContexts ); // throws.
m_iContextsOutput = _TyBase::m_iContexts;
}
_BIEN_UNWIND( _Seek( sp ) );
_Tell( sp );
}
// Take care of any direction change that may have occured since we last checked:
if ( m_fDirectionDownOutput != _TyBase::m_fDirectionDown )
{
_BIEN_TRY
{
m_ros._WriteDirectionChange( _TyBase::m_fDirectionDown ); // throws.
m_fDirectionDownOutput = _TyBase::m_fDirectionDown;
}
catch( ... )
{
_Seek( sp );
throw;
}
// _BIEN_UNWIND( _Seek( sp ) );
_Tell( sp );
}
bool _fNewUnfinished = m_fNewUnfinished; // save state for throw.
_TyGraphLinkBase * _pglbCheckWriteFooter = 0;
bool _fAtUnfinishedNode;
_BIEN_TRY
{
// See where we are right now:
if ( _TyBase::PGLBCur() )
{
m_ros._WriteLinkHeader( _TyBase::PGLBCur(),
_TyBase::_FAtUnfinishedNode() ? _TyBase::PGNBCur() : 0 ); // throws.
if ( _TyBase::PGLBCur()->FIsConstructed() )
{
(this->*m_pmfnWriteLink)(); // throws.
}
// Save our place - we will re-write the footer if we encounter an unfinished node.
if ( t_fUseSeek )
{
m_spBeforeLinkFooter = m_ros._Tell();
m_ros._WriteLinkFooter( _TyBase::_FAtUnfinishedNode() ? 0 : _TyBase::PGLBCur(), 0 ); // Indicate no re-connected node. throws.
}
else
{
_pglbCheckWriteFooter = _TyBase::PGLBCur();
_fAtUnfinishedNode = _TyBase::_FAtUnfinishedNode();
m_fWroteLinkFooter = false; // Indicate that we have not written the link footer
// we will check for this after calling the base.
}
}
else
if ( _TyBase::PGNBCur() )
{
// If this is a new unfinished node then write the link information.
if ( m_fNewUnfinished )
{
m_ros._WriteNewUnfinishedNodeHeader( _TyBase::PGNBCur(), m_pglbUnfinished );
}
else
{
// Just write a simple node header.
m_ros._WriteNodeHeader( _TyBase::PGNBCur() );
}
// write the node itself:
(this->*m_pmfnWriteNode)();
if ( m_fNewUnfinished )
{
// We write the link information for the unfinished node now - this
// allows easier throw-safe construction of the graph on read:
// Update the remaining links after writing the footer:
_TyBase::m_itNodeLastInserted->second.m_iRemainingLinks =
m_ros._WriteUnfinishedNodeFooter( _TyBase::PGNBCur(), m_pglbUnfinished ) -
!!m_pglbUnfinished;
m_fNewUnfinished = false;
}
else
{
m_ros._WriteNodeFooter( _TyBase::PGNBCur() );
}
}
else
{
if ( !m_fWroteGraphFooter )
{
// Then write the graph footer and return.
_WriteGraphFooter();
}
else
{
Assert( 0 ); // Attempt to iterate beyond the end.
}
return;
}
// Move to the next iteration state:
_TyBase::_Next(); // throws.
if ( !t_fUseSeek )
{
if ( _pglbCheckWriteFooter && !m_fWroteLinkFooter )
{
// Then we didn't write the footer:
m_ros._WriteLinkFooter( _fAtUnfinishedNode ? 0 : _pglbCheckWriteFooter, 0 ); // Indicate no re-connected node. throws.
}
}
}
catch( ... )
{
_Seek( sp );
m_fNewUnfinished = _fNewUnfinished;
throw;
}
// _BIEN_UNWIND( ( _Seek( sp ),
// ( m_fNewUnfinished = _fNewUnfinished ) ) );
if ( _TyBase::FAtEnd() && !m_fWroteGraphFooter )
{
_WriteGraphFooter();
}
}
void _WriteGraphFooter()
{
m_ros._WriteGraphFooter();
m_fWroteGraphFooter = true;
}
};
// Declare the output iterator wrapper - this wraps objects that are eventually
// based on _graph_output_iter_base:
template < class t_TyGraphNode, class t_TyGraphLink, class t_TyOutputStream,
class t_TyBaseOutIter, class t_TyFIsConstIterator >
struct _graph_output_iterator
: public t_TyBaseOutIter,
public _graph_iter_const_base< t_TyGraphNode, t_TyGraphLink, t_TyFIsConstIterator >
{
private:
typedef t_TyBaseOutIter _TyBase;
typedef _graph_iter_const_base< t_TyGraphNode, t_TyGraphLink, t_TyFIsConstIterator > _tyConstIterBase;
typedef _graph_output_iterator _TyThis;
public:
typedef _TyBase _TyIterBase; // This type supported by all graph iterators.
typedef _graph_output_iterator< t_TyGraphNode, t_TyGraphLink, t_TyOutputStream,
t_TyBaseOutIter, std::false_type > iterator;
typedef _graph_output_iterator< t_TyGraphNode, t_TyGraphLink, t_TyOutputStream,
t_TyBaseOutIter, std::true_type > const_iterator;
typedef t_TyOutputStream _TyOutputStream;
typedef typename _TyOutputStream::_TyInitArg _TyInitArg;
typedef typename _TyOutputStream::_TyIONodeEl _TyIONodeEl;
typedef typename _TyOutputStream::_TyIOLinkEl _TyIOLinkEl;
typedef typename _tyConstIterBase::_TyGraphNodeCQ _TyGraphNodeCQ;
typedef typename _tyConstIterBase::_TyGraphLinkCQ _TyGraphLinkCQ;
typedef typename _tyConstIterBase::node_reference node_reference;
typedef typename _tyConstIterBase::link_reference link_reference;
typedef typename _tyConstIterBase::node_pointer node_pointer;
typedef typename _tyConstIterBase::link_pointer link_pointer;
typedef typename _tyConstIterBase::_TyGraphNode _TyGraphNode;
typedef typename _tyConstIterBase::_TyGraphLink _TyGraphLink;
typedef typename _tyConstIterBase::_TyGraphNodeBaseBase _TyGraphNodeBaseBase;
typedef typename _tyConstIterBase::_TyGraphLinkBaseBase _TyGraphLinkBaseBase;
_TyOutputStream m_os; // The output stream object.
_graph_output_iterator( _TyInitArg _oia,
_TyGraphNodeCQ * _pgnbCur,
_TyGraphLinkCQ * _pglbCur,
bool _fClosedDirected,
bool _fDirectionDown,
typename _TyBase::_TyAllocatorAsPassed const & _rAlloc,
_TyIONodeEl const & _rione = _TyIONodeEl(),
_TyIOLinkEl const & _riole = _TyIOLinkEl() )
: _TyBase( m_os,
const_cast< t_TyGraphNode* >( _pgnbCur ),
const_cast< t_TyGraphLink* >( _pglbCur ),
_fClosedDirected, _fDirectionDown, _rAlloc ),
m_os( _oia, _fDirectionDown, _rione, _riole, _rAlloc )
{
// Set up function pointers in the base:
_TyBase::m_pmfnWriteNode = static_cast< typename _TyBase::_TyPMFnWriteNode >( &_TyThis::_WriteNode );
_TyBase::m_pmfnWriteLink = static_cast< typename _TyBase::_TyPMFnWriteLink >( &_TyThis::_WriteLink );
}
// Initialization with the base's base class - this allows us to initialize the
// output iterator with a iterator_pos ( i.e. dgraph::begin() ).
explicit _graph_output_iterator( _TyInitArg _oia,
typename _TyBase::_TyFwdIterBaseBase const & _r,
_TyIONodeEl const & _rione = _TyIONodeEl(),
_TyIOLinkEl const & _riole = _TyIOLinkEl() )
: _TyBase( _r, m_os ),
m_os( _oia, _r.m_fDirectionDown, _rione, _riole, _r.get_allocator() )
{
// Set up function pointers in the base:
_TyBase::m_pmfnWriteNode = static_cast< typename _TyBase::_TyPMFnWriteNode >( &_TyThis::_WriteNode );
_TyBase::m_pmfnWriteLink = static_cast< typename _TyBase::_TyPMFnWriteLink >( &_TyThis::_WriteLink );
}
// Template that accesses similar named method in the output object:
template < class t_TyOptions >
void SetOutputOptions( t_TyOptions _o )
{
m_os.template SetOutputOptions< t_TyOptions >( _o );
}
_TyGraphNodeCQ * PGNCur()
{
return const_cast< _TyGraphNodeCQ * >( static_cast< t_TyGraphNode * >( _TyBase::PGNBCur() ) );
}
_TyGraphLinkCQ * PGLCur()
{
return const_cast< _TyGraphLinkCQ * >( static_cast< t_TyGraphLink * >( _TyBase::PGLBCur() ) );
}
// note: may not has a node ( may be at end of iteration ).
node_reference RNodeEl() const _BIEN_NOTHROW
{
return const_cast< node_reference >( *static_cast< _TyGraphNode * >( _TyBase::PGNBCur() ) );
}
// note: may not have a link!
link_reference RLinkEl() const _BIEN_NOTHROW
{
return const_cast< link_reference >( *static_cast< _TyGraphLink * >( _TyBase::PGLBCur() ) );
}
// The way this works: if the link_pointer is non-null then the iteration is currently
// at a link. Otherwise the iteration either at node_pointer or at the end() if node_pointer null.
pair< link_pointer, node_pointer > PairCur() const _BIEN_NOTHROW
{
return pair< link_pointer, node_pointer >( _TyBase::PGLBCur() ? &RLinkEl() : 0, _TyBase::PGNBCur() ? &RNodeEl() : 0 );
}
_TyThis & operator ++()
{
_TyBase::_Next();
return *this;
}
protected:
void _WriteNode()
{
m_os._WriteNode( PGNCur() );
}
void _WriteLink()
{
m_os._WriteLink( PGLCur() );
}
};
__DGRAPH_END_NAMESPACE
#endif //__GR_SRIL