/
archive.h
326 lines (286 loc) · 10.5 KB
/
archive.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
/*
* The Doomsday Engine Project -- libdeng2
*
* Copyright (c) 2004-2013 Jaakko Keränen <jaakko.keranen@iki.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LIBDENG2_ARCHIVE_H
#define LIBDENG2_ARCHIVE_H
#include "../libdeng2.h"
#include "../IByteArray"
#include "../IWritable"
#include "../String"
#include "../Time"
#include "../File"
#include "../PathTree"
#include <set>
namespace de {
class IBlock;
class Block;
/**
* Collection of named memory blocks stored inside a byte array.
*
* An archive consists of a collection of Block instances that are
* identified using a path tree structure. Blocks can be added and removed
* at any time.
*
* Archive is merely IWritable instead of ISerializable because of its
* memory management model. The basic assumption is that even though the
* archive is kept in serialized form, individual entries can still be
* accessed without processing the entire serialization. Therefore, Archive
* only operates on existing, serialized archives that live in externally
* owned byte arrays; Archive does not take ownership of the source data.
* This also means that read-only access to very large byte arrays can be
* done without loading all the source data into memory (e.g., Archive
* that reads from a large NativeFile).
*
* It is also assumed that accessing the source data and extracting a
* particular entry is potentially a slow operation: when individual
* entries are read from the source, the entries are cached in memory so
* that subsequent access is fast.
*
* An Archive instance expects that the source byte array is never changed
* by third parties while it is the source of the Archive.
*
* It is possible to detach an Archive instance from its source byte array
* by calling @c cache(DetachFromSource). This forces all entries to be
* copied to Archive-owned memory (in original serialized form).
*
* @see ArchiveFeed, ArchiveEntryFile
*
* @ingroup data
*/
class DENG2_PUBLIC Archive : public IWritable
{
public:
/// Base class for format-related errors. @ingroup errors
DENG2_ERROR(FormatError);
/// Provided path was not valid. @ingroup errors
DENG2_ERROR(InvalidPathError);
/// The requested entry does not exist in the archive. @ingroup errors
DENG2_ERROR(NotFoundError);
/// There is an error related to content processing. @ingroup errors
DENG2_ERROR(ContentError);
typedef std::set<String> Names; // alphabetical order
public:
/**
* Constructs an empty Archive.
*/
Archive();
/**
* Constructs a new Archive instance. The content index contained in
* @a data is read during construction.
*
* @param data Data of the source archive. No copy of the
* data is made, so the caller must make sure the
* byte array remains in existence for the lifetime
* of the Archive instance.
*/
Archive(IByteArray const &data);
virtual ~Archive();
/**
* Returns the source byte array. @c NULL, if the archive was
* constructed without a source (as empty) or has been detached from
* its original source.
*/
IByteArray const *source() const;
enum CacheAttachment {
RemainAttachedToSource = 0,
DetachFromSource = 1
};
/**
* Loads a copy of the serialized data into memory for all the entries that
* don't already have deserialized data stored.
*
* @param attach If DetachFromSource, the archive becomes a standalone
* archive that no longer needs the source byte array to
* remain in existence.
*/
void cache(CacheAttachment attach = DetachFromSource);
/**
* Determines whether the archive contains an entry (not a folder).
*
* @param path Path of the entry.
*
* @return @c true or @c false.
*/
bool hasEntry(Path const &path) const;
/**
* List the files in a specific folder of the archive.
*
* @param folder Folder path to look in. By default looks in the root.
* @param names Entry names collected in a set. The names are relative to a
* @a folder and are in alphabetical order.
*
* @return Number of names returned in @a names.
*/
dint listFiles(Names &names, Path const &folder = Path()) const;
/**
* List the folders in a specific folder of the archive.
*
* @param folder Folder path to look in. By default looks in the root.
* @param names Folder entry names collected in a set. The names are
* relative to @a folder and are in alphabetical order.
*
* @return Number of names returned in @a names.
*/
dint listFolders(Names &names, Path const &folder = Path()) const;
/**
* Returns information about the specified path.
*
* @param path Path of the entry within the archive.
*
* @return Type, size, and other metadata about the entry.
*/
File::Status entryStatus(Path const &path) const;
/**
* Returns the deserialized data of an entry for read-only access. The
* data is deserialized and cached if a cached copy doesn't already
* exist.
*
* This method operates on the Archive in immutable mode: the user is
* not expected to modify the contents of the returned Block, and the
* existing serialized data of the entry can be used as-is when the
* archive is written.
*
* @param path Entry path. The entry must already exist in the archive.
*
* @return Immutable contents of the entry.
*/
Block const &entryBlock(Path const &path) const;
inline Block const &constEntryBlock(Path const &path) const {
return entryBlock(path);
}
/**
* Returns the deserialized data of an entry for read and write access.
* The data is deserialized and cached if a cached copy doesn't already
* exist.
*
* The user is allowed to make changes to the returned block. The
* entry's data is automatically marked for re-serialization in case
* the archive is written.
*
* @param path Entry path. If doesn't exist, a new entry will be added.
*
* @return Modifiable contents of the entry.
*/
Block &entryBlock(Path const &path);
/**
* Adds an entry to the archive. The entry will not be committed to the
* source, but instead remains as-is in memory.
*
* @param path Path of the entry within the archive.
* @param data Data of the entry.
*/
void add(Path const &path, IByteArray const &data);
/**
* Removes an entry from the archive. If there is deserialized data for
* the entry in memory, it will be deleted.
*
* @param path Path of the entry.
*/
void remove(Path const &path);
/**
* Clears the index of the archive. All entries are deleted.
*/
void clear();
/**
* Determines if the archive has been modified.
*/
bool modified() const;
/**
* Writes the archive to a Writer. Deserialized entries are
* re-serialized just-in-time before writing if they have been
* modified.
*
* @note If overwriting the source array, be sure to either first write
* to a temporary array and then replace the source, or alternatively
* detach the source from the archive beforehand. Otherwise the
* unchanged entries may become corrupted as they are reused from their
* old location in the source, which may have been already overwritten.
*
* @param to Where to write.
*/
virtual void operator >> (Writer &to) const = 0;
protected:
/*
* Interface for derived classes:
*/
/// Base class for archive entries.
struct Entry : public PathTree::Node
{
dsize offset; ///< Offset from the start of the source array.
dsize size; ///< Deserialized size.
dsize sizeInArchive; ///< Size within the archive (serialized).
Time modifiedAt; ///< Latest modification timestamp.
bool maybeChanged; ///< @c true, if the data must be re-serialized when writing.
/// Deserialized data. Can be @c NULL. Entry has ownership.
Block *data;
/// Cached copy of the serialized data. Can be @c NULL. Entry has ownership.
Block mutable *dataInArchive;
Entry(PathTree::NodeArgs const &args) : Node(args),
offset(0),
size(0),
sizeInArchive(0),
maybeChanged(false),
data(0),
dataInArchive(0)
{}
virtual ~Entry()
{
// Entry has ownership of the cached data.
delete data;
delete dataInArchive;
}
};
/**
* Sets the index used by the Archive. A concrete subclass must call
* this in their constructor; Archive does not create an index on its
* own.
*
* @param tree PathTree with entries of suitable type. Ownership given to Archive.
*/
void setIndex(PathTree *tree);
/**
* Reads an entry from the source archive. The implementation of this
* method is expected to cache the read data of the entry in its
* original, serialized format in Entry::dataInArchive.
*
* @param entry Entry that is being read.
* @param path Path of the entry within the archive.
* @param data Data is written here.
*/
virtual void readFromSource(Entry const &entry, Path const &path, IBlock &data) const = 0;
/**
* Inserts an entry into the archive's index. If the path already
* exists in the index, the old entry is deleted first.
*
* @param path Path of the entry.
*
* @return Inserted entry.
*/
Entry &insertEntry(Path const &path);
/**
* Returns the full entry index so that derived classes can iterate the
* entries.
*
* @return Entry index.
*/
PathTree const &index() const;
private:
DENG2_PRIVATE(d)
};
} // namespace de
#endif /* LIBDENG2_ARCHIVE_H */