-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathMultiFileStream.cs
More file actions
347 lines (297 loc) · 10.8 KB
/
MultiFileStream.cs
File metadata and controls
347 lines (297 loc) · 10.8 KB
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
using System;
using System.IO;
using System.Collections.Generic;
namespace JMS.DVB.DirectShow.AccessModules
{
/// <summary>
/// Simuliert das Arbeiten mit einer Liste von Dateien.
/// </summary>
public class MultiFileStream : IVirtualStream
{
/// <summary>
/// Alle Dateien, die hier verwaltet werden.
/// </summary>
private List<FileInfo> m_Files = new List<FileInfo>();
/// <summary>
/// Die letzte Datei der Liste, die immer geöffnet ist.
/// </summary>
private FileStream m_LastFile;
/// <summary>
/// Die aktuell geöffnete Datei.
/// </summary>
private FileStream m_CurrentFile;
/// <summary>
/// Der Name des Datenstroms abgeleitet aus dem Namen der ersten Datei.
/// </summary>
public string Name { get; set; }
/// <summary>
/// Die aktuelle Position in der Gesamtdatei.
/// </summary>
private long m_Position;
/// <summary>
/// Erzeugt eine neue Verwaltungseinheit.
/// </summary>
/// <param name="streams">Die bereitgestellten Datenströme.</param>
/// <exception cref="ArgumentNullException">Eine Datei wurde nicht angegeben.</exception>
/// <exception cref="ArgumentException">Eine der angebenen Dateien existiert nicht.</exception>
public MultiFileStream( IEnumerable<FileInfo> streams )
{
// Load all
if (streams != null)
m_Files.AddRange( streams );
// Validate
foreach (var file in m_Files)
if (file == null)
throw new ArgumentNullException( "streams" );
// Open the last
if (m_Files.Count > 0)
m_LastFile = Open( m_Files[m_Files.Count - 1] );
// Set the name
if (m_Files.Count > 0)
Name = Path.GetFileNameWithoutExtension( m_Files[0].Name );
else
Name = string.Empty;
}
/// <summary>
/// Öffnet eine Datei im korrekten Zugriffsmodus.
/// </summary>
/// <param name="path">Der volle Pfad zur Datei.</param>
/// <returns>Die gewünschte Datei.</returns>
private FileStream Open( FileInfo path )
{
// Forward
return new FileStream( path.FullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 10000000 );
}
/// <summary>
/// Ermittelt die gesamte Länge.
/// </summary>
/// <param name="count">Die Anzahl der Dateien, die berücksichtigt werden sollen.</param>
/// <returns>Die gewünschte Länge.</returns>
private long GetLength( int count )
{
// Overall counter
long all = 0;
// All but the last - may be growing
for (int i = 0, imax = Math.Min( count, m_Files.Count - 1 ); i < imax; i++)
all += m_Files[i].Length;
// The last
if (count >= m_Files.Count)
if (m_LastFile != null)
all += m_LastFile.Length;
// Report
return all;
}
/// <summary>
/// Ergänzt einen Datenstrom.
/// </summary>
/// <param name="stream">Ein beliebiger Datenstrom.</param>
/// <exception cref="ArgumentNullException">Es wurde kein Datenstrom angegeben.</exception>
public void Add( FileInfo stream )
{
// Validate
if (stream == null)
throw new ArgumentNullException( "stream" );
// Open it
var lastStream = Open( stream );
try
{
// Just append one
m_Files.Add( stream );
// Reopen the last file
using (m_LastFile)
m_LastFile = lastStream;
}
catch
{
// Release ressources
lastStream.Dispose();
// Forward
throw;
}
// Update the name
if (m_Files.Count == 1)
Name = Path.GetFileNameWithoutExtension( stream.Name );
}
#region IVirtualStream Members
/// <summary>
/// Liest Daten ab der aktuellen Position.
/// </summary>
/// <param name="buffer">Ein Speicherbereich, der zu befüllen ist.</param>
/// <param name="offset">Die Position des ersten Bytes, das befüllt werden darf.</param>
/// <param name="length">Die maximale Anzahl von zu befüllenden Bytes.</param>
/// <returns>Die tatsächlich Anzahl der übertragenen Bytes.</returns>
/// <exception cref="ArgumentNullException">Es wurde kein Speicherbereich angegeben.</exception>
/// <exception cref="ArgumentOutOfRangeException">Es wurde ein ungültiger Bereich angegeben.</exception>
public int Read( byte[] buffer, int offset, int length )
{
// Validate
if (length < 0)
throw new ArgumentOutOfRangeException( "length" );
if (offset < 0)
throw new ArgumentOutOfRangeException( "offset" );
// Validate
if (buffer == null)
{
// Only allowed when all parameters are zero - assume zero sized buffer
if (length > 0)
throw new ArgumentNullException( "buffer" );
if (offset > 0)
throw new ArgumentOutOfRangeException( "offset" );
}
else
{
// Check range
if (offset > buffer.Length)
throw new ArgumentOutOfRangeException( "offset" );
if (checked( offset + length ) > buffer.Length)
throw new ArgumentOutOfRangeException( "offset" );
}
// None
if (m_LastFile == null)
return 0;
// Data read
int read = 0;
// Process
while (length > 0)
{
// Get the current file
while (m_CurrentFile == null)
{
// Position left to scan
long position = m_Position;
// Try all but the last
for (int i = 0; i < m_Files.Count - 1; i++)
{
// Attach to the file
var file = m_Files[i];
// Check mode
if (position >= file.Length)
position -= file.Length;
else
{
// Be safe
try
{
// Open this one
m_CurrentFile = Open( file );
}
catch (IOException)
{
// Remove this chunk from the position counter
m_Position = GetLength( i );
// Remove from list
m_Files.RemoveAt( i );
// Try again without it
position = -1;
}
// Done
break;
}
}
// Retry
if (position < 0)
continue;
// Open last
if (m_CurrentFile == null)
if (position > m_LastFile.Length)
break;
else
m_CurrentFile = m_LastFile;
// Position modulo
m_CurrentFile.Position = position;
}
// End reached
if (m_CurrentFile == null)
break;
// Load some stuff
int part = m_CurrentFile.Read( buffer, offset, length );
// See if we got something - if not skip to the next file if not already at the very end
if (part > 0)
{
// Advance local pointers
offset += part;
length -= part;
read += part;
// Advance overall pointer
m_Position += part;
}
else if (ReferenceEquals( m_CurrentFile, m_LastFile ))
break;
else
CloseCurrent();
}
// For now
return read;
}
/// <summary>
/// Meldet oder setzt die aktuelle Position im Datenstrom.
/// </summary>
/// <exception cref="ArgumentOutOfRangeException">Die angegebene Position ist unzulässig.</exception>
public long Position
{
get
{
// For now
return m_Position;
}
set
{
// Validate
if (value < 0)
throw new ArgumentOutOfRangeException( "value" );
else if (value > Length)
throw new ArgumentOutOfRangeException( "value" );
// Change
m_Position = value;
// Reload the current file
CloseCurrent();
}
}
/// <summary>
/// Schließt die aktuelle Datei.
/// </summary>
private void CloseCurrent()
{
// Process
if (m_CurrentFile != null)
try
{
// May just be a reference
if (!ReferenceEquals( m_CurrentFile, m_LastFile ))
m_CurrentFile.Dispose();
}
finally
{
// Forget
m_CurrentFile = null;
}
}
/// <summary>
/// Meldet die aktuelle Größe des Datenstroms.
/// </summary>
public long Length
{
get
{
// All we have
return GetLength( m_Files.Count );
}
}
#endregion
#region IDisposable Members
/// <summary>
/// Beendet die Nutzung dieser Instanz endgültig.
/// </summary>
public void Dispose()
{
// Forget files
m_Files = null;
// Close current stream
CloseCurrent();
// Close last stream
using (m_LastFile)
m_LastFile = null;
}
#endregion
}
}