-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathxml_transport.h
431 lines (415 loc) · 16.2 KB
/
xml_transport.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
#pragma once
// 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).
// xml_transport.h
// Declare XML output transport classes.
// dbien
// 17FEB2021
#include "xml_types.h"
#include "_fdobjs.h"
__XMLP_BEGIN_NAMESPACE
// xml_write_transport_file:
// Write XML to a file.
template < class t_TyChar, class t_TyFSwitchEndian >
class xml_write_transport_file
{
typedef xml_write_transport_file _TyThis;
public:
typedef t_TyChar _TyChar;
typedef t_TyFSwitchEndian _TyFSwitchEndian;
// We don't need to open a xml_write_transport_file read/write, as we do for a xml_write_transport_mapped.
typedef false_type _TyFOpenFileReadWrite;
typedef unique_ptr< _TyChar[] > _TyBuffer;
xml_write_transport_file() = delete;
xml_write_transport_file( FileObj & _rfoFile, bool _fWriteBOM, size_t _nchWriteBuffer = 4096 )
: m_foFile( std::move( _rfoFile ) ),
m_nchWriteBuffer( _nchWriteBuffer )
{
#ifdef _MSC_VER
m_upBuffer = make_unique_for_overwrite< _TyChar[] >( m_nchWriteBuffer );
#else
m_upBuffer = _TyBuffer( new _TyChar[ m_nchWriteBuffer ] );
#endif
m_pcBufCur = &m_upBuffer[0];
VerifyThrow( m_foFile.FIsOpen() );
if ( _fWriteBOM )
WriteBOM< _TyChar, _TyFSwitchEndian >( m_foFile.HFileGet() );
// Leave the file right where it is.
}
xml_write_transport_file( xml_write_transport_file const & ) = delete;
xml_write_transport_file & operator =( xml_write_transport_file const & ) = delete;
xml_write_transport_file( xml_write_transport_file && _rr )
{
swap( _rr );
}
xml_write_transport_file & operator =( xml_write_transport_file && _rr )
{
_TyThis acquire( std::move( _rr ) );
swap( acquire );
return *this;
}
void swap( _TyThis & _r )
{
m_foFile.swap( _r.m_foFile );
m_upBuffer.swap( _r.m_upBuffer );
std::swap( m_nchWriteBuffer, _r.m_nchWriteBuffer );
std::swap( m_pcBufCur, _r.m_pcBufCur );
}
~xml_write_transport_file()
{
bool fInUnwinding = !!std::uncaught_exceptions();
if ( !!m_pcBufCur && ( m_pcBufCur != &m_upBuffer[0] ) )
{
bool fFlush = _FFlushBuffer();
if ( !fFlush )
{
if ( !fInUnwinding )
THROWNAMEDEXCEPTIONERRNO( GetLastErrNo(), "Failed to write to file handle [0x%zx].", (size_t)m_foFile.HFileGet() );
else
{
try
{
LOGSYSLOGERRNO( eslmtError, GetLastErrNo(), "Failed to write to file handle [0x%zx].", (size_t)m_foFile.HFileGet() );
}
catch( ... )
{
// can't throw - we are in an unwinding.
}
}
}
}
}
// Write any character type to the transport - it will do the appropriate translation and it needn't even buffer anything...
template < class t_TyCharWrite >
void Write( const t_TyCharWrite * _pcBegin, const t_TyCharWrite * _pcEnd )
{
// We send to the conversion template which will call back into this:
VerifyThrow( FWriteUTFStream< _TyChar >( _pcBegin, _pcEnd - _pcBegin, *this ) );
}
bool FWrite( const _TyChar * _pcBuf, size_t _nch ) noexcept
{
// Write to the buffer until it is full and then empty it into the file:
const _TyChar * const pcEndBuf = _PcEndBuf();
const _TyChar * pcBufWriteCur = _pcBuf;
for ( size_t nchRemaining = _nch; nchRemaining; )
{
size_t nchLeftInBuf = pcEndBuf - m_pcBufCur;
size_t nchWrite = (min)( nchLeftInBuf, nchRemaining );
nchRemaining -= nchWrite;
memcpy( m_pcBufCur, pcBufWriteCur, nchWrite * sizeof( _TyChar ) );
pcBufWriteCur += nchWrite;
m_pcBufCur += nchWrite;
if ( m_pcBufCur == pcEndBuf )
{
if ( !_FFlushBuffer() )
return false;
m_pcBufCur = &m_upBuffer[0];
}
}
return true;
}
static constexpr EFileCharacterEncoding GetEncoding()
{
return GetCharacterEncoding< _TyChar, _TyFSwitchEndian >();
}
protected:
// We want these calls to be noexcept. We write to the buffer in non-switch-endian
// for debugging purposes. Then when the buffer is full we write to the file, switching
// the endian of the buffer first.
bool _FFlushBuffer() noexcept
requires( is_same_v< _TyFSwitchEndian, false_type > )
{
return _FFlushBufferNoSwitchEndian();
}
bool _FFlushBufferNoSwitchEndian()
{
Assert( !!m_pcBufCur && ( m_pcBufCur != &m_upBuffer[0] ) );
size_t nbyWrite = ( m_pcBufCur - &m_upBuffer[0] ) * sizeof( _TyChar );
int iResult = FileWrite( m_foFile.HFileGet(), &m_upBuffer[0], nbyWrite );
return !iResult;
}
bool _FFlushBuffer() noexcept
requires( is_same_v< _TyFSwitchEndian, true_type > )
{
Assert( !!m_pcBufCur && ( m_pcBufCur != &m_upBuffer[0] ) );
SwitchEndian( &m_upBuffer[0], m_pcBufCur );
return _FFlushBufferNoSwitchEndian();
}
_TyChar * _PcEndBuf()
{
Assert( !!m_upBuffer );
return &m_upBuffer[m_nchWriteBuffer-1] + 1;
}
FileObj m_foFile;
_TyBuffer m_upBuffer;
size_t m_nchWriteBuffer{0};
_TyChar * m_pcBufCur{nullptr};
};
// xml_write_transport_mapped:
// Write XML to a mapped file.
template < class t_TyChar, class t_TyFSwitchEndian >
class xml_write_transport_mapped
{
typedef xml_write_transport_mapped _TyThis;
public:
typedef t_TyChar _TyChar;
typedef t_TyFSwitchEndian _TyFSwitchEndian;
// We must map a xml_write_transport_mapped read/write because we must map it read/write.
typedef true_type _TyFOpenFileReadWrite;
static const size_t s_knGrowFileByBytes = 16384 * sizeof( _TyChar ); // As we write the file grow it in this increment.
xml_write_transport_mapped( FileObj & _rfoFile, bool _fWriteBOM )
: m_foFile( std::move( _rfoFile ) )
{
VerifyThrow( m_foFile.FIsOpen() );
// Write the BOM and then initialize the file.
if ( _fWriteBOM )
WriteBOM< _TyChar, _TyFSwitchEndian >( m_foFile.HFileGet() );
// Leave the file right where it is and map it here - the mapping will take care of correctly offsetting everything.
m_nbyMapAtPosition = (uint64_t)NFileSeekAndThrow(m_foFile.HFileGet(), 0, vkSeekCur);
size_t stMapBytes = s_knGrowFileByBytes;
int iResult = FileSetSize( m_foFile.HFileGet(), m_nbyMapAtPosition + stMapBytes ); // Set initial size.
if ( !!iResult )
THROWNAMEDEXCEPTIONERRNO(GetLastErrNo(), "FileSetSize() failed.");
uint64_t u64SizeMapping;
uint64_t u64ResultMapAtPosition = m_nbyMapAtPosition; // The result will be m_nbyMapAtPosition % PageSize().
m_fmoMap.SetHMMFile( MapReadWriteHandle( m_foFile.HFileGet(), &u64SizeMapping, &u64ResultMapAtPosition ) );
if ( !m_fmoMap.FIsOpen() )
THROWNAMEDEXCEPTIONERRNO(GetLastErrNo(), "MapReadWriteHandle() failed.");
Assert( size_t( u64SizeMapping - u64ResultMapAtPosition ) == stMapBytes );
m_pcCur = m_pcBase = (_TyChar*)m_fmoMap.Pby( (size_t)u64ResultMapAtPosition );
m_pcEnd = m_pcCur + ( stMapBytes / sizeof( _TyChar) );
}
~xml_write_transport_mapped()
{
bool fInUnwinding = !!std::uncaught_exceptions();
(void)_Close( !fInUnwinding ); // Only throw on error from close if we are not currently unwinding.
}
// Write any character type to the transport - it will do the appropriate translation and it needn't even buffer anything...
template < class t_TyCharWrite >
void Write( const t_TyCharWrite * _pcBegin, const t_TyCharWrite * _pcEnd )
{
// We send to the conversion template which will call back into this:
VerifyThrow( FWriteUTFStream< _TyChar >( _pcBegin, _pcEnd - _pcBegin, *this ) );
}
// We want these calls to be noexcept.
bool FWrite( const _TyChar * _pcBuf, size_t _nch ) noexcept
requires( is_same_v< _TyFSwitchEndian, false_type > )
{
int iResult = _CheckGrowMapNoThrow( _nch );
if ( !iResult )
{
memcpy( m_pcCur, _pcBuf, _nch * sizeof( _TyChar ) );
m_pcCur += _nch;
}
return !iResult;
}
// We want these calls to be noexcept.
bool FWrite( const _TyChar * _pcBuf, size_t _nch ) noexcept
requires( is_same_v< _TyFSwitchEndian, true_type > )
{
int iResult = _CheckGrowMapNoThrow( _nch );
if ( !iResult )
{
memcpy( m_pcCur, _pcBuf, _nch * sizeof( _TyChar ) );
SwitchEndian( m_pcCur, _nch );
m_pcCur += _nch;
}
return !iResult;
}
static constexpr EFileCharacterEncoding GetEncoding()
{
return GetCharacterEncoding< _TyChar, _TyFSwitchEndian >();
}
protected:
// Don't throw - return 0 on success, non-zero on failure.
int _CheckGrowMapNoThrow( size_t _charsByAtLeast ) noexcept
{
try
{
if ( size_t( m_pcEnd - m_pcCur ) < _charsByAtLeast )
_GrowMap( _charsByAtLeast );
return 0;
}
catch( std::exception const & _rexc )
{
n_SysLog::Log( eslmtError, "Caught exception [%s]", _rexc.what() );
return -1;
}
}
void _GrowMap( size_t _charsByAtLeast )
{
VerifyThrow( m_foFile.FIsOpen() && m_fmoMap.FIsOpen() );
// unmap, grow file, remap. That was easy!
size_t nbyGrowAtLeast = _charsByAtLeast * sizeof( _TyChar ); // scale from chars to bytes.
size_t nbyGrowBy = (((nbyGrowAtLeast - 1) / s_knGrowFileByBytes) + 1) * s_knGrowFileByBytes;
size_t nbyOldMapping = ( m_pcEnd - m_pcBase ) * sizeof( _TyChar );
(void)m_fmoMap.Close();
int iFileSetSize = FileSetSize( m_foFile.HFileGet(), m_nbyMapAtPosition + nbyOldMapping + nbyGrowBy );
if (-1 == iFileSetSize)
THROWNAMEDEXCEPTIONERRNO( GetLastErrNo(), "FileSetSize() failed." );
uint64_t u64SizeMapping;
uint64_t u64ResultMapAtPosition = m_nbyMapAtPosition; // The result will be m_nbyMapAtPosition % PageSize().
m_fmoMap.SetHMMFile( MapReadWriteHandle( m_foFile.HFileGet(), &u64SizeMapping, &u64ResultMapAtPosition ) );
if ( !m_fmoMap.FIsOpen() )
THROWNAMEDEXCEPTIONERRNO( GetLastErrNo(), "Remapping failed." );
Assert( ( u64SizeMapping - u64ResultMapAtPosition ) == nbyOldMapping + nbyGrowBy );
size_t nchOffsetCur = m_pcCur - m_pcBase;
m_pcBase = (_TyChar*)m_fmoMap.Pby( (size_t)u64ResultMapAtPosition );
m_pcEnd = m_pcBase + ( u64SizeMapping / sizeof( _TyChar) );
m_pcCur = m_pcBase + nchOffsetCur;
}
// Allow throwing on error because closing actually does something and we need to know if it succeeds.
int _Close( bool _fThrowOnError ) noexcept(false)
{
if ( !m_fmoMap.FIsOpen() )
{
// Not much to do cuz we don't know about the size of the mapping, etc. Just close the file.
m_foFile.Close();
return 0;
}
int iCloseFileMapping = m_fmoMap.Close();
vtyErrNo errCloseFileMapping = !iCloseFileMapping ? vkerrNullErrNo : GetLastErrNo();
vtyErrNo errTruncate = vkerrNullErrNo;
vtyErrNo errCloseFile = vkerrNullErrNo;
if ( m_foFile.FIsOpen() )
{
// We need to truncate the file to m_pcCur - m_pcBase bytes.
size_t nbySizeTruncate = ( m_pcCur - m_pcBase ) * sizeof( _TyChar );
int iTruncate = FileSetSize(m_foFile.HFileGet(), m_nbyMapAtPosition + nbySizeTruncate );
errTruncate = !iTruncate ? vkerrNullErrNo : GetLastErrNo();
int iClose = m_foFile.Close();
errCloseFile = !iClose ? vkerrNullErrNo : GetLastErrNo();
}
vtyErrNo errFirst;
unsigned nError;
if ( ( (nError = 1), ( vkerrNullErrNo != ( errFirst = errTruncate ) ) ) ||
( (nError = 2), ( vkerrNullErrNo != ( errFirst = errCloseFileMapping ) ) ) ||
( (nError = 3), ( vkerrNullErrNo != ( errFirst = errCloseFile ) ) ) )
{
// Ensure that the errno is represented in the last error:
SetLastErrNo( errFirst );
if ( _fThrowOnError )
{
const char * pcThrow;
switch( nError )
{
case 1: pcThrow = "Error encountered truncating."; break;
case 2: pcThrow = "Error encountered closing file mapping."; break;
case 3: pcThrow = "Error encountered closing file."; break;
default: pcThrow = "Wh-what?!"; break;
}
THROWNAMEDEXCEPTIONERRNO( errFirst, pcThrow );
}
}
return errFirst == vkerrNullErrNo ? 0 : -1;
}
FileObj m_foFile;
FileMappingObj m_fmoMap;
uint64_t m_nbyMapAtPosition{0}; // We save this because we may be growing the file.
_TyChar * m_pcBase{nullptr};
_TyChar * m_pcCur{nullptr};
_TyChar * m_pcEnd{nullptr};
};
// xml_write_transport_memstream:
// Write XML to a memstream - an in-memory stream impl.
template < class t_TyChar, class t_TyFSwitchEndian >
class xml_write_transport_memstream
{
typedef xml_write_transport_memstream _TyThis;
public:
typedef t_TyChar _TyChar;
typedef t_TyFSwitchEndian _TyFSwitchEndian;
typedef MemStream< size_t, false > _TyMemStream;
typedef MemFileContainer< size_t, false > _TyMemFileContainer;
static const size_t s_knbyChunkSizeMemStream = 4096; // Needs to be divisible by sizeof( char32_t ).
xml_write_transport_memstream( xml_write_transport_memstream const & ) = delete;
xml_write_transport_memstream & operator =( xml_write_transport_memstream const & ) = delete;
xml_write_transport_memstream( bool _fWriteBOM )
{
// Create a new memstream object. The MemFileContainer just goes away - just really there for creating the shared memfile object initially.
{//B
_TyMemFileContainer mfc( s_knbyChunkSizeMemStream );
mfc.OpenStream( m_msMemStream );
}//EB
if ( _fWriteBOM )
{
EFileCharacterEncoding efce = GetCharacterEncoding< _TyChar, _TyFSwitchEndian >();
Assert( efceFileCharacterEncodingCount != efce );
VerifyThrowSz( efceFileCharacterEncodingCount != efce, "Unknown char/switch endian encoding." );
string strBOM = StrGetBOMForEncoding( efce );
m_msMemStream.Write( strBOM.c_str(), strBOM.length() );
}
}
xml_write_transport_memstream( _TyMemStream && _rrmsMemStream )
requires( is_same_v< _TyFSwitchEndian, false_type > )
: m_msMemStream( std::move( _rrmsMemStream ) )
{
}
xml_write_transport_memstream( _TyMemStream && _rrmsMemStream )
requires( is_same_v< _TyFSwitchEndian, true_type > )
: m_msMemStream( std::move( _rrmsMemStream ) )
{
// For switch endian we need to switch the bytes and we didn't write the code to switch bytes within
// a single character that straddles a segment boundary in the SegArray.
VerifyThrowSz( !( m_msMemStream.GetCurPos() % sizeof( _TyChar ) ), "For switch endian xml_write_transport_memstream<> must start at a multiple of a character size." );
}
_TyMemStream & GetMemStream()
{
return m_msMemStream;
}
const _TyMemStream & GetMemStream() const
{
return m_msMemStream;
}
// Write any character type to the transport - it will do the appropriate translation and it needn't even buffer anything...
template < class t_TyCharWrite >
void Write( const t_TyCharWrite * _pcBegin, const t_TyCharWrite * _pcEnd )
{
// We send to the conversion template which will call back into this:
VerifyThrow( FWriteUTFStream< _TyChar >( _pcBegin, _pcEnd - _pcBegin, *this ) );
}
// We want these calls to be noexcept.
bool FWrite( const _TyChar * _pcBuf, size_t _nch ) noexcept
requires( is_same_v< _TyFSwitchEndian, false_type > )
{
ssize_t sstResult = m_msMemStream.WriteNoExcept( _pcBuf, _nch * sizeof ( _TyChar ) );
return !( sstResult < 0 ) && size_t( sstResult ) == ( _nch * sizeof ( _TyChar ) );
}
// We want these calls to be noexcept.
bool FWrite( const _TyChar * _pcBuf, size_t _nch ) noexcept
requires( is_same_v< _TyFSwitchEndian, true_type > )
{
size_t nPosInit = m_msMemStream.GetCurPos();
ssize_t sstResult = m_msMemStream.WriteNoExcept( _pcBuf, _nch * sizeof ( _TyChar ) );
if ( !( sstResult < 0 ) && size_t( sstResult ) == ( _nch * sizeof ( _TyChar ) ) )
{
m_msMemStream.Apply( nPosInit, m_msMemStream.GetCurPos(),
[]( vtyMemStreamByteType * _pbyBegin, vtyMemStreamByteType * _pbyEnd )
{
Assert( !( ( _pbyEnd - _pbyBegin ) % sizeof ( _TyChar ) ) );
SwitchEndian( (_TyChar*)_pbyBegin, (_TyChar*)_pbyEnd );
}
);
return true;
}
return false;
}
static constexpr EFileCharacterEncoding GetEncoding()
{
return GetCharacterEncoding< _TyChar, _TyFSwitchEndian >();
}
protected:
_TyMemStream m_msMemStream;
};
// xml_write_transport_var:
// Variant transport. Not sure if this is necessary.
template < class ... T_TysTransports >
class xml_write_transport_var
{
typedef xml_write_transport_var _TyThis;
public:
protected:
};
__XMLP_END_NAMESPACE