aboutsummaryrefslogtreecommitdiffstats
path: root/vespalib/src/vespa/vespalib/data/fileheader.h
blob: 80fe6820b43aa02bef4792a5fe205928abd37ad3 (plain) (blame)
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
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#pragma once

#include <vespa/vespalib/util/exception.h>
#include <map>

class FastOS_FileInterface;

namespace vespalib {

class DataBuffer;
class asciistream;

/**
 * This exception can be thrown when serializing or deserializing header content.
 */
VESPA_DEFINE_EXCEPTION(IllegalHeaderException, vespalib::Exception);

/**
 * This class implements a collection of GenericHeader::Tag objects that can be set and retrieved by name. The
 * interfaces GenericHeader::IDataReader or GenericHeader::IDataWriter define the api to allow deserialization
 * and serialization of an instance of this class to any underlying data buffer.
 */
class GenericHeader {
public:
    static const uint32_t MAGIC;
    static const uint32_t VERSION;

    /**
     * This class implements a immutable named value of a specified type. This type can be any of
     * Tag::Type. There is no enforcement of type, so using asInteger() on a TYPE_STRING instance will simple
     * return the default integer value.
     */
    class Tag {
    public:
        enum Type {
            TYPE_EMPTY   = 'e',
            TYPE_FLOAT   = 'f',
            TYPE_INTEGER = 'i',
            TYPE_STRING  = 's'
        };

    private:
        Type             _type;
        vespalib::string _name;
        double           _fVal;
        int64_t          _iVal;
        vespalib::string _sVal;

    public:
        Tag();
        Tag(const Tag &);
        Tag & operator=(const Tag &);
        Tag(const vespalib::string &name, float val);
        Tag(const vespalib::string &name, double val);
        Tag(const vespalib::string &name, int8_t val);
        Tag(const vespalib::string &name, uint8_t val);
        Tag(const vespalib::string &name, int16_t val);
        Tag(const vespalib::string &name, uint16_t val);
        Tag(const vespalib::string &name, int32_t val);
        Tag(const vespalib::string &name, uint32_t val);
        Tag(const vespalib::string &name, int64_t val);
        Tag(const vespalib::string &name, uint64_t val);
        Tag(const vespalib::string &name, bool val);
        Tag(const vespalib::string &name, const char *val);
        Tag(const vespalib::string &name, const vespalib::string &val);
        ~Tag();

        size_t           read(DataBuffer &buf);
        size_t           write(DataBuffer &buf) const;
        size_t           getSize()   const;

        bool               isEmpty()   const { return _type == TYPE_EMPTY; }
        Type               getType()   const { return _type; }
        const vespalib::string &getName()   const { return _name; }

        double             asFloat()   const { return _fVal; }
        int64_t            asInteger() const { return _iVal; }
        const vespalib::string &asString()  const { return _sVal; }
        bool               asBool()    const { return _iVal != 0; }
    };

    /**
     * This class defines the interface used by GenericHeader to deserialize content. It has implementation
     * GenericHeader::BufferReader for reading from a buffer, and FileHeader::FileReader for reading from a
     * file.
     */
    class IDataReader {
    public:
        virtual ~IDataReader() { /* empty */ }
        virtual size_t getData(char *buf, size_t len) = 0;
    };

    /**
     * This class defines the interface used by GenericHeader to serialize content. It has implementation
     * GenericHeader::BufferWriter for reading from a buffer, and FileHeader::FileWriter for reading from a
     * file.
     */
    class IDataWriter {
    public:
        virtual ~IDataWriter() { /* empty */ }
        virtual size_t putData(const char *buf, size_t len) = 0;
    };

    /**
     * Implements the GenericHeader::IDataReader interface for deserializing header content from a
     * DataBuffer instance.
     */
    class BufferReader : public IDataReader {
    private:
        DataBuffer &_buf;

    public:
        BufferReader(DataBuffer &buf);
        size_t getData(char *buf, size_t len) override;
    };

    /**
     * Implements the GenericHeader::IDataWriter interface for serializing header content to a
     * DataBuffer instance.
     */
    class BufferWriter : public IDataWriter {
    private:
        DataBuffer &_buf;

    public:
        BufferWriter(DataBuffer &buf);
        size_t putData(const char *buf, size_t len) override;
    };

    class MMapReader : public IDataReader
    {
        const char *_buf;
        size_t _sz;

    public:
        MMapReader(const char *buf, size_t sz);

        size_t getData(char *buf, size_t len) override;
    };

private:
    static const Tag EMPTY;

    using TagMap = std::map<vespalib::string, Tag>;
    TagMap _tags;

public:
    /**
     * Constructs a new instance of this class.
     */
    GenericHeader();

    /**
     * Virtual destructor required for inheritance.
     */
    virtual ~GenericHeader();

    /**
     * Returns the number of tags contained in this header.
     *
     * @return The size of the tag map.
     */
    size_t getNumTags() const { return _tags.size(); }

    /**
     * Returns the tag at the given index. This can be used along with getNumTags() to iterate over all the
     * tags in a header. This is not an efficient way of accessing tags, since the underlying map does not
     * support random access. If you are interested in a specific tag, use getTag() by name instead.
     *
     * @param idx The index of the tag to return.
     * @return The tag at the given index.
     */
    const Tag &getTag(size_t idx) const;

    /**
     * Returns a reference to the named tag. If hasTag() returned false for the same key, this method returns
     * a tag that is of type Tag::TYPE_EMPTY and returns true for Tag::isEmpty().
     *
     * @param key The name of the tag to return.
     * @return A reference to the named tag.
     */
    const Tag &getTag(const vespalib::string &key) const;

    /**
     * Returns whether or not there exists a tag with the given name.
     *
     * @param key The name of the tag to look for.
     * @return True if the named tag exists.
     */
    bool hasTag(const vespalib::string &key) const;

    /**
     * Adds the given tag to this header. If a tag already exists with the given name, this method replaces
     * that tag and returns false.
     *
     * @param tag The tag to add.
     * @return True if no tag was overwritten.
     */
    bool putTag(const Tag &tag);

    /**
     * Removes a named tag. If no tag exists with the given name, this method returns false.
     *
     * @param key The name of the tag to remove.
     * @return True if a tag was removed.
     */
    bool removeTag(const vespalib::string &key);

    /**
     * Returns whether or not this header contains any data. The current implementation only checks for tags,
     * but as this class evolves it might include other data as well.
     *
     * @return True if this header has no data.
     */
    bool isEmpty() const { return _tags.empty(); }

    static size_t
    getMinSize(void);

    /**
     * Returns the number of bytes required to hold the content of this when calling write().
     *
     * @return The number of bytes.
     */
    virtual size_t getSize() const;

    static size_t
    readSize(IDataReader &reader);

    /**
     * Deserializes header content from the given provider into this.
     *
     * @param reader The provider to read from.
     * @return The number of bytes read.
     */
    size_t read(IDataReader &reader);

    /**
     * Serializes the content of this into the given consumer.
     *
     * @param writer The consumer to write to.
     * @return The number of bytes written.
     */
    size_t write(IDataWriter &writer) const;
};

/**
 * This class adds file-specific functionality to the GenericHeader class. This includes alignment of size to
 * some set number of bytes, as well as the ability to update a header in-place (see FileHeader::rewrite()).
 */
class FileHeader : public GenericHeader {
public:
    /**
     * Implements the GenericHeader::IDataReader interface for deserializing header content from a
     * FastOS_FileInterface instance.
     */
    class FileReader : public IDataReader {
    private:
        FastOS_FileInterface &_file;

    public:
        FileReader(FastOS_FileInterface &file);
        size_t getData(char *buf, size_t len) override;
    };

    /**
     * Implements the GenericHeader::IDataWriter interface for serializing header content to a
     * FastOS_FileInterface instance.
     */
    class FileWriter : public IDataWriter {
    private:
        FastOS_FileInterface &_file;

    public:
        FileWriter(FastOS_FileInterface &file);
        size_t putData(const char *buf, size_t len) override;
    };

private:
    size_t _alignTo;
    size_t _minSize;
    size_t _fileSize;

public:
    /**
     * Constructs a new instance of this class.
     *
     * @param alignTo The number of bytes to which the serialized size must be aligned to.
     * @param minSize The minimum number of bytes of the serialized size.
     */
    FileHeader(size_t alignTo = 8u, size_t minSize = 0u);

    /**
     * This function overrides GenericHeader::getSize() to align header size to the number of bytes supplied
     * to the constructor of this class. Furthermore, it will attempt to keep the header size constant after
     * an initial read() or write() so that one can later rewrite() it.
     *
     * @return The number of bytes required to hold the content of this.
     */
    size_t getSize() const override;

    /**
     * Deserializes header content from the given file into this. This requires that the file is open in read
     * mode, and that it is positioned at the start of the file.
     *
     * @param file The file to read from.
     * @return The number of bytes read.
     */
    size_t readFile(FastOS_FileInterface &file);

    /**
     * Serializes the content of this into the given file. This requires that the file is open in write mode,
     * and that it is positioned at the start of the file.
     *
     * @param file The file to write to.
     * @return The number of bytes written.
     */
    size_t writeFile(FastOS_FileInterface &file) const;

    /**
     * Serializes the content of this into the given file. This requires that the file is open in read-write
     * mode. This method reads the first 64 bits of the file to ensure that it is compatible with this, then
     * write its content to it. Finally, it moves the file position back to where it was before.
     *
     * @param file The file to write to.
     * @return The number of bytes written.
     */
    size_t rewriteFile(FastOS_FileInterface &file);
};

/**
 * Implements ostream operator for GenericHeader::Tag class. This method will only output the actual value of
 * the tag, not its name or type. Without this operator you would have to switch on tag type to decide which
 * value accessor to use.
 */
vespalib::asciistream &operator<<(vespalib::asciistream &out, const GenericHeader::Tag &tag);

} // namespace