Coverage Report

Created: 2026-04-29 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openexr/src/lib/OpenEXR/ImfIDManifest.cpp
Line
Count
Source
1
// SPDX-License-Identifier: BSD-3-Clause
2
// Copyright (c) Contributors to the OpenEXR Project.
3
4
//-----------------------------------------------------------------------------
5
//
6
//        ID Manifest class implementation
7
//
8
//-----------------------------------------------------------------------------
9
10
#include "ImfIO.h"
11
#include "ImfXdr.h"
12
#include <Iex.h>
13
#include <ImfIDManifest.h>
14
#include <openexr_compression.h>
15
16
#include <algorithm>
17
#include <stdint.h>
18
#include <stdlib.h>
19
#include <string.h>
20
21
//
22
// debugging only
23
//
24
#ifdef DUMP_TABLE
25
#    include <iostream>
26
#endif
27
28
OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_ENTER
29
30
using namespace OPENEXR_IMF_INTERNAL_NAMESPACE;
31
using std::fill;
32
using std::make_pair;
33
using std::map;
34
using std::pair;
35
using std::set;
36
using std::sort;
37
using std::string;
38
using std::vector;
39
40
const std::string IDManifest::UNKNOWN        = "unknown";
41
const std::string IDManifest::NOTHASHED      = "none";
42
const std::string IDManifest::CUSTOMHASH     = "custom";
43
const std::string IDManifest::MURMURHASH3_32 = "MurmurHash3_32";
44
const std::string IDManifest::MURMURHASH3_64 = "MurmurHash3_64";
45
46
const std::string IDManifest::ID_SCHEME  = "id";
47
const std::string IDManifest::ID2_SCHEME = "id2";
48
49
IDManifest::IDManifest ()
50
0
{}
51
52
namespace
53
{
54
55
// map of strings to index of string in table
56
typedef std::map<std::string, int> indexedStringSet;
57
58
// when handling vectors/sets of strings, the string is got by dereferencing the pointer/iterator
59
template <class T>
60
size_t
61
stringSize (const T& i)
62
0
{
63
0
    return i->size ();
64
0
}
Unexecuted instantiation: ImfIDManifest.cpp:unsigned long Imf_3_3::(anonymous namespace)::stringSize<std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*> >(std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*> const&)
Unexecuted instantiation: ImfIDManifest.cpp:unsigned long Imf_3_3::(anonymous namespace)::stringSize<std::__1::__tree_const_iterator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::__tree_node<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, void*>*, long> >(std::__1::__tree_const_iterator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::__tree_node<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, void*>*, long> const&)
65
66
template <class T>
67
const char*
68
cStr (const T& i)
69
0
{
70
0
    return i->c_str ();
71
0
}
Unexecuted instantiation: ImfIDManifest.cpp:char const* Imf_3_3::(anonymous namespace)::cStr<std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*> >(std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*> const&)
Unexecuted instantiation: ImfIDManifest.cpp:char const* Imf_3_3::(anonymous namespace)::cStr<std::__1::__tree_const_iterator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::__tree_node<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, void*>*, long> >(std::__1::__tree_const_iterator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::__tree_node<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, void*>*, long> const&)
72
73
/*
74
    // but for indexedStringSet the string is the first of the iterator pair
75
    size_t stringSize(indexedStringSet::const_iterator &i )
76
    {
77
        return i->first.size();
78
    }
79
    
80
    const char* cStr(indexedStringSet::const_iterator &i)
81
    {
82
       return i->first.c_str();
83
    }
84
    */
85
86
size_t
87
getVariableLengthIntegerSize (uint64_t value)
88
0
{
89
90
0
    if (value < 1llu << 7) { return 1; }
91
92
0
    if (value < 1llu << 14) { return 2; }
93
0
    if (value < 1llu << 21) { return 3; }
94
0
    if (value < 1llu << 28) { return 4; }
95
0
    if (value < 1llu << 35) { return 5; }
96
0
    if (value < 1llu << 42) { return 6; }
97
0
    if (value < 1llu << 49) { return 7; }
98
0
    if (value < 1llu << 56) { return 8; }
99
0
    if (value < 1llu << 63) { return 9; }
100
0
    return 10;
101
0
}
102
103
uint64_t
104
readVariableLengthInteger (const char*& readPtr, const char* endPtr)
105
0
{
106
    // bytes are stored LSB first, so each byte that is read from the stream must be
107
    // shifted before mixing into the existing length
108
0
    int           shift = 0;
109
0
    unsigned char byte  = 0;
110
0
    uint64_t      value = 0;
111
0
    do
112
0
    {
113
0
        if (readPtr >= endPtr)
114
0
        {
115
0
            throw IEX_NAMESPACE::InputExc (
116
0
                "IDManifest too small for variable length integer");
117
0
        }
118
        // Each chunk contributes at most 7 bits; shifts must stay < 64 or
119
        // (byte & 127) << shift has undefined behavior (C++).
120
0
        if (shift >= 64)
121
0
        {
122
0
            throw IEX_NAMESPACE::InputExc (
123
0
                "Invalid variable-length integer in IDManifest");
124
0
        }
125
0
        byte = *(unsigned char*) readPtr++;
126
        // top bit of byte isn't part of actual number, it just indicates there's more info to come
127
        // so take bottom 7 bits, shift them to the right place, and insert them
128
        //
129
0
        value |= (uint64_t (byte & 127)) << shift;
130
0
        shift += 7;
131
0
    } while (byte &
132
0
             128); //while top bit set on previous byte, there is more to come
133
0
    return value;
134
0
}
135
136
void
137
writeVariableLengthInteger (char*& outPtr, uint64_t value)
138
0
{
139
0
    do
140
0
    {
141
0
        unsigned char byte = (unsigned char) (value & 127);
142
0
        value >>= 7;
143
0
        if (value > 0) { byte |= 128; }
144
0
        *(unsigned char*) outPtr++ = byte;
145
0
    } while (value > 0);
146
0
}
147
148
//
149
// read a list of strings into the given container
150
// format is:
151
// numberOfStrings (unless numberOfStrings already passed in)
152
//  length of string 0
153
//  length of string 1
154
//  ...
155
//  string 0
156
//  string 1
157
//  ...
158
//  (the sizes come first then the strings because that helps compression performance)
159
//  note - updates readPtr to point to first byte after readStrings
160
//
161
162
template <class T>
163
void
164
readStringList (
165
    const char*& readPtr,
166
    const char*  endPtr,
167
    T&           outputVector,
168
    int          numberOfStrings = 0)
169
0
{
170
0
    if (numberOfStrings == 0)
171
0
    {
172
0
        if (readPtr + 4 > endPtr)
173
0
        {
174
0
            throw IEX_NAMESPACE::InputExc (
175
0
                "IDManifest too small for string list size");
176
0
        }
177
0
        Xdr::read<CharPtrIO> (readPtr, numberOfStrings);
178
0
    }
179
180
0
    vector<size_t> lengths (numberOfStrings);
181
182
0
    for (int i = 0; i < numberOfStrings; ++i)
183
0
    {
184
0
        lengths[i] = readVariableLengthInteger (readPtr, endPtr);
185
0
    }
186
0
    for (int i = 0; i < numberOfStrings; ++i)
187
0
    {
188
189
0
        if (readPtr + lengths[i] > endPtr)
190
0
        {
191
0
            throw IEX_NAMESPACE::InputExc ("IDManifest too small for string");
192
0
        }
193
0
        outputVector.insert (outputVector.end (), string (readPtr, lengths[i]));
194
0
        readPtr += lengths[i];
195
0
    }
196
0
}
Unexecuted instantiation: ImfIDManifest.cpp:void Imf_3_3::(anonymous namespace)::readStringList<std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > >(char const*&, char const*, std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >&, int)
Unexecuted instantiation: ImfIDManifest.cpp:void Imf_3_3::(anonymous namespace)::readStringList<std::__1::set<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::less<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > >(char const*&, char const*, std::__1::set<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::less<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >&, int)
197
198
//
199
// computes number of bytes required to serialize vector/set of strings
200
//
201
template <typename T>
202
int
203
getStringListSize (const T& stringList, size_t entries = 0)
204
0
{
205
0
    int totalSize = 0;
206
0
    if (entries == 0)
207
0
    {
208
0
        totalSize += 4; // 4 bytes to store number of entries;
209
0
    }
210
0
    else
211
0
    {
212
0
        if (stringList.size () != entries)
213
0
        {
214
0
            throw IEX_NAMESPACE::InputExc (
215
0
                "Incorrect number of components stored in ID Manifest");
216
0
        }
217
0
    }
218
0
    for (typename T::const_iterator i = stringList.begin ();
219
0
         i != stringList.end ();
220
0
         ++i)
221
0
    {
222
0
        size_t length = stringSize (i);
223
0
        totalSize += length;
224
        // up to five bytes for variable length encoded size
225
226
0
        totalSize += getVariableLengthIntegerSize (length);
227
0
    }
228
0
    return totalSize;
229
0
}
Unexecuted instantiation: ImfIDManifest.cpp:int Imf_3_3::(anonymous namespace)::getStringListSize<std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > >(std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > const&, unsigned long)
Unexecuted instantiation: ImfIDManifest.cpp:int Imf_3_3::(anonymous namespace)::getStringListSize<std::__1::set<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::less<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > >(std::__1::set<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::less<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > const&, unsigned long)
230
231
//
232
// write string list to outPtr. if entries nonzero, omits number of entries,
233
// but confirms 'entries' == T.size()
234
//
235
template <typename T>
236
void
237
writeStringList (char*& outPtr, const T& stringList, int entries = 0)
238
0
{
239
0
    int size = stringList.size ();
240
0
    if (entries == 0) { Xdr::write<CharPtrIO> (outPtr, size); }
241
0
    else
242
0
    {
243
0
        if (size != entries)
244
0
        {
245
0
            throw IEX_NAMESPACE::InputExc (
246
0
                "Incorrect number of components stored in ID Manifest");
247
0
        }
248
0
    }
249
0
    for (typename T::const_iterator i = stringList.begin ();
250
0
         i != stringList.end ();
251
0
         ++i)
252
0
    {
253
0
        int stringLength = stringSize (i);
254
        //
255
        // variable length encoding:
256
        // values between 0 and 127 inclusive are stored in a single byte
257
        // values between 128 and 16384 are encoded with two bytes: 1LLLLLLL 0MMMMMMMM where L and M are the least and most significant bits of the value
258
        // in general, values are stored least significant values first, with the top bit of each byte indicating more values follow
259
        // the top bit is clear in the last byte of the value
260
        // (this scheme requires two bytes to store values above 1<<7, and five bytes to store values above 1<<28)
261
        //
262
263
0
        writeVariableLengthInteger (outPtr, stringLength);
264
0
    }
265
266
0
    for (typename T::const_iterator i = stringList.begin ();
267
0
         i != stringList.end ();
268
0
         ++i)
269
0
    {
270
0
        int stringLength = stringSize (i);
271
0
        Xdr::write<CharPtrIO> (outPtr, (const char*) cStr (i), stringLength);
272
0
    }
273
0
}
Unexecuted instantiation: ImfIDManifest.cpp:void Imf_3_3::(anonymous namespace)::writeStringList<std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > >(char*&, std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > const&, int)
Unexecuted instantiation: ImfIDManifest.cpp:void Imf_3_3::(anonymous namespace)::writeStringList<std::__1::set<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::less<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > >(char*&, std::__1::set<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::less<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > const&, int)
274
275
int
276
getStringSize (const string& str)
277
0
{
278
0
    return 4 + str.size ();
279
0
}
280
281
void
282
readPascalString (
283
    const char*& readPtr, const char* endPtr, string& outputString)
284
0
{
285
286
0
    if (readPtr + 4 > endPtr)
287
0
    {
288
0
        throw IEX_NAMESPACE::InputExc ("IDManifest too small for string size");
289
0
    }
290
0
    unsigned int length = 0;
291
0
    Xdr::read<CharPtrIO> (readPtr, length);
292
293
0
    if (readPtr + length > endPtr)
294
0
    {
295
0
        throw IEX_NAMESPACE::InputExc ("IDManifest too small for string");
296
0
    }
297
0
    outputString = string ((const char*) readPtr, length);
298
0
    readPtr += length;
299
0
}
300
301
void
302
writePascalString (char*& outPtr, const string& str)
303
0
{
304
0
    unsigned int length = str.size ();
305
0
    Xdr::write<CharPtrIO> ((char*&) outPtr, length);
306
0
    Xdr::write<CharPtrIO> ((char*&) outPtr, (const char*) str.c_str (), length);
307
0
}
308
309
} // namespace
310
311
IDManifest::IDManifest (const char* data, const char* endOfData)
312
0
{
313
0
    init (data, endOfData);
314
0
}
315
316
void
317
IDManifest::init (const char* data, const char* endOfData)
318
0
{
319
320
0
    unsigned int version;
321
0
    Xdr::read<CharPtrIO> (data, version);
322
0
    if (version != 0)
323
0
    {
324
0
        throw IEX_NAMESPACE::InputExc ("Unrecognized IDmanifest version");
325
0
    }
326
327
    //
328
    // first comes list of all strings used in manifest
329
    //
330
0
    vector<string> stringList;
331
0
    readStringList (data, endOfData, stringList);
332
333
    //
334
    // expand the strings in the stringlist
335
    // each string begins with number of characters to copy from the previous string
336
    // the remainder is the 'new' bit that appears after that
337
    //
338
339
0
    for (size_t i = 1; i < stringList.size (); ++i)
340
0
    {
341
342
0
        size_t common; // number of characters in common with previous string
343
0
        int    stringStart = 1; // first character of string itself;
344
        //
345
        // previous string had more than 255 characters?
346
        //
347
0
        const size_t minPrefixLen =
348
0
            stringList[i - 1].size () > 255 ? size_t (2) : size_t (1);
349
0
        if (stringList[i].size () < minPrefixLen)
350
0
        {
351
0
            throw IEX_NAMESPACE::InputExc (
352
0
                "IDManifest string too small for common prefix length");
353
0
        }
354
0
        if (stringList[i - 1].size () > 255)
355
0
        {
356
0
            common = size_t (((unsigned char) (stringList[i][0])) << 8) +
357
0
                     size_t ((unsigned char) (stringList[i][1]));
358
0
            stringStart = 2;
359
0
        }
360
0
        else { common = (unsigned char) stringList[i][0]; }
361
0
        if (common > stringList[i - 1].size ())
362
0
        {
363
0
            throw IEX_NAMESPACE::InputExc (
364
0
                "Bad common string length in IDmanifest string table");
365
0
        }
366
0
        stringList[i] = stringList[i - 1].substr (0, common) +
367
0
                        stringList[i].substr (stringStart);
368
0
    }
369
370
    //
371
    // decode mapping table from indices in table to indices in string list
372
    // the mapping uses smaller indices for more commonly occurring strings, since these are encoded with fewer bits
373
    // comments in serialize function describe the format
374
    //
375
376
0
    vector<int> mapping (stringList.size ());
377
378
    //
379
    // overlapping sequences: A list [(4,5),(3,6)] expands to 4,5,3,6 - because 4 and 5 are including already
380
    // they are not included again
381
    // the 'seen' list indicates which values have already been used, so they are not re-referenced
382
    //
383
384
0
    vector<char> seen (stringList.size ());
385
386
0
    int rleLength;
387
0
    if (endOfData < data + 4)
388
0
    {
389
0
        throw IEX_NAMESPACE::InputExc ("IDManifest too small");
390
0
    }
391
392
0
    Xdr::read<CharPtrIO> (data, rleLength);
393
394
0
    int currentIndex = 0;
395
0
    for (int i = 0; i < rleLength; ++i)
396
0
    {
397
0
        int first;
398
0
        int last;
399
0
        if (endOfData < data + 8)
400
0
        {
401
0
            throw IEX_NAMESPACE::InputExc ("IDManifest too small");
402
0
        }
403
0
        Xdr::read<CharPtrIO> (data, first);
404
0
        Xdr::read<CharPtrIO> (data, last);
405
406
0
        if (first < 0 || last < 0 || first > last ||
407
0
            first >= int (stringList.size ()) ||
408
0
            last >= int (stringList.size ()))
409
0
        {
410
0
            throw IEX_NAMESPACE::InputExc (
411
0
                "Bad mapping table entry in IDManifest");
412
0
        }
413
0
        for (int entry = first; entry <= last; entry++)
414
0
        {
415
            // don't remap already mapped values
416
0
            if (seen[entry] == 0)
417
0
            {
418
0
                mapping[currentIndex] = entry;
419
0
                seen[entry]           = 1;
420
0
                currentIndex++;
421
0
            }
422
0
        }
423
0
    }
424
425
#ifdef DUMP_TABLE
426
    //
427
    // dump mapping table for debugging
428
    //
429
    for (size_t i = 0; i < mapping.size (); ++i)
430
    {
431
        std::cout << i << ' ' << mapping[i] << std::endl;
432
    }
433
#endif
434
435
    //
436
    // number of manifest entries comes after string list
437
    //
438
0
    int manifestEntries;
439
440
0
    if (endOfData < data + 4)
441
0
    {
442
0
        throw IEX_NAMESPACE::InputExc ("IDManifest too small");
443
0
    }
444
445
0
    Xdr::read<CharPtrIO> (data, manifestEntries);
446
447
0
    _manifest.clear ();
448
449
0
    _manifest.resize (manifestEntries);
450
451
0
    for (int manifestEntry = 0; manifestEntry < manifestEntries;
452
0
         ++manifestEntry)
453
0
    {
454
455
0
        ChannelGroupManifest& m = _manifest[manifestEntry];
456
457
        //
458
        // read header of this manifest entry
459
        //
460
0
        readStringList (data, endOfData, m._channels);
461
0
        readStringList (data, endOfData, m._components);
462
463
0
        char lifetime;
464
0
        if (endOfData < data + 4)
465
0
        {
466
0
            throw IEX_NAMESPACE::InputExc ("IDManifest too small");
467
0
        }
468
0
        Xdr::read<CharPtrIO> (data, lifetime);
469
470
0
        m.setLifetime (IdLifetime (lifetime));
471
0
        readPascalString (data, endOfData, m._hashScheme);
472
0
        readPascalString (data, endOfData, m._encodingScheme);
473
474
0
        if (endOfData < data + 5)
475
0
        {
476
0
            throw IEX_NAMESPACE::InputExc ("IDManifest too small");
477
0
        }
478
0
        char storageScheme;
479
0
        Xdr::read<CharPtrIO> (data, storageScheme);
480
481
0
        int tableSize;
482
0
        Xdr::read<CharPtrIO> (data, tableSize);
483
484
0
        uint64_t previousId = 0;
485
486
0
        for (int entry = 0; entry < tableSize; ++entry)
487
0
        {
488
0
            uint64_t id;
489
490
0
            switch (storageScheme)
491
0
            {
492
0
                case 0: {
493
0
                    if (endOfData < data + 8)
494
0
                    {
495
0
                        throw IEX_NAMESPACE::InputExc ("IDManifest too small");
496
0
                    }
497
0
                    Xdr::read<CharPtrIO> (data, id);
498
0
                    break;
499
0
                }
500
0
                case 1: {
501
0
                    if (endOfData < data + 4)
502
0
                    {
503
0
                        throw IEX_NAMESPACE::InputExc ("IDManifest too small");
504
0
                    }
505
0
                    unsigned int id32;
506
0
                    Xdr::read<CharPtrIO> (data, id32);
507
0
                    id = id32;
508
0
                    break;
509
0
                }
510
0
                default: {
511
0
                    id = readVariableLengthInteger (data, endOfData);
512
0
                }
513
0
            }
514
515
0
            id += previousId;
516
0
            previousId = id;
517
518
            //
519
            // insert into table - insert tells us if it was already there
520
            //
521
0
            pair<map<uint64_t, vector<string>>::iterator, bool> insertion =
522
0
                m._table.insert (make_pair (id, vector<string> ()));
523
0
            if (insertion.second == false)
524
0
            {
525
0
                throw IEX_NAMESPACE::InputExc (
526
0
                    "ID manifest contains multiple entries for the same ID");
527
0
            }
528
0
            (insertion.first)->second.resize (m.getComponents ().size ());
529
0
            for (size_t i = 0; i < m.getComponents ().size (); ++i)
530
0
            {
531
0
                int stringIndex = readVariableLengthInteger (data, endOfData);
532
0
                if (size_t (stringIndex) > stringList.size () ||
533
0
                    stringIndex < 0)
534
0
                {
535
0
                    throw IEX_NAMESPACE::InputExc (
536
0
                        "Bad string index in IDManifest");
537
0
                }
538
0
                (insertion.first)->second[i] = stringList[mapping[stringIndex]];
539
0
            }
540
0
        }
541
0
    }
542
0
}
543
544
IDManifest::IDManifest (const CompressedIDManifest& compressed)
545
0
{
546
    //
547
    // decompress the compressed manifest
548
    //
549
550
0
    vector<char> uncomp (compressed._uncompressedDataSize);
551
0
    size_t       outSize;
552
0
    size_t       inSize = static_cast<size_t> (compressed._compressedDataSize);
553
0
    if (EXR_ERR_SUCCESS != exr_uncompress_buffer (
554
0
                               nullptr,
555
0
                               compressed._data,
556
0
                               inSize,
557
0
                               uncomp.data (),
558
0
                               compressed._uncompressedDataSize,
559
0
                               &outSize))
560
0
    {
561
0
        throw IEX_NAMESPACE::InputExc (
562
0
            "IDManifest decompression (zlib) failed.");
563
0
    }
564
0
    if (outSize != compressed._uncompressedDataSize)
565
0
    {
566
0
        throw IEX_NAMESPACE::InputExc (
567
0
            "IDManifest decompression (zlib) failed: mismatch in decompressed data size");
568
0
    }
569
570
0
    init ((const char*) &uncomp[0], (const char*) &uncomp[0] + outSize);
571
0
}
572
573
void
574
IDManifest::serialize (std::vector<char>& data) const
575
0
{
576
577
0
    indexedStringSet stringSet;
578
579
    //
580
    // build string map - this turns unique strings into indices
581
    // the manifest stores the string indices - this allows duplicated
582
    // strings to point to the same place
583
    // grabs all the strings regardless of which manifest/mapping they are in
584
    //
585
    // at this point we just count the manifest entries
586
    //
587
0
    {
588
        //
589
        // over each channel group
590
        //
591
0
        for (size_t m = 0; m < _manifest.size (); ++m)
592
0
        {
593
            // over each mapping
594
0
            for (IDManifest::ChannelGroupManifest::IDTable::const_iterator i =
595
0
                     _manifest[m]._table.begin ();
596
0
                 i != _manifest[m]._table.end ();
597
0
                 ++i)
598
0
            {
599
                // over each string in the mapping
600
601
0
                for (size_t s = 0; s < i->second.size (); ++s)
602
0
                {
603
0
                    stringSet[i->second[s]]++;
604
0
                }
605
0
            }
606
0
        }
607
0
    }
608
609
    //
610
    // build compressed string representation - all but first string starts with number of characters to copy from previous string.
611
    // max 65535 bytes - use two bytes to store if previous string was more than 255 characters, big endian
612
    //
613
0
    vector<string> prefixedStringList (stringSet.size ());
614
615
    //
616
    // also make a sorted list so the most common entry appears first. Keep equally likely entries in numerical order
617
    //
618
0
    vector<pair<int, int>> sortedIndices (stringSet.size ());
619
620
0
    string prevString;
621
0
    int    index = 0;
622
0
    for (indexedStringSet::iterator i = stringSet.begin ();
623
0
         i != stringSet.end ();
624
0
         ++i)
625
0
    {
626
627
        // no prefix on first string - map stores index of each string, so use that rather than a counter;
628
0
        if (index == 0) { prefixedStringList[index] = i->first; }
629
0
        else
630
0
        {
631
0
            size_t common = 0;
632
0
            while (common < 65535 && common < prevString.size () &&
633
0
                   common < i->first.size () &&
634
0
                   prevString[common] == i->first[common])
635
0
            {
636
0
                ++common;
637
0
            }
638
639
0
            if (prevString.size () > 255)
640
0
            {
641
                //
642
                // long previous string - use two bytes to encode number of common chars
643
                //
644
0
                prefixedStringList[index] = string (1, char (common >> 8)) +
645
0
                                            string (1, char (common & 255)) +
646
0
                                            i->first.substr (common);
647
0
            }
648
0
            else
649
0
            {
650
0
                prefixedStringList[index] =
651
0
                    string (1, char (common)) + i->first.substr (common);
652
0
            }
653
0
        }
654
0
        prevString = i->first;
655
0
        sortedIndices[index].first =
656
0
            -i->second; // use negative of count so largest count appears first
657
0
        sortedIndices[index].second = index;
658
659
        //
660
        // also, repurpose stringSet so that it maps from string names to indices in the string table
661
        //
662
0
        i->second = index;
663
664
0
        index++;
665
0
    }
666
667
0
    sort (sortedIndices.begin (), sortedIndices.end ());
668
669
    //
670
    // the first 1<<7 characters will all be encoded with 1 byte, regardless of how common they are
671
    // the next 1<<14 characters will be encoded with 2 bytes
672
    // (a full huffman encode would do this at the bit level, not the byte level)
673
    //
674
    // the mapping table can be reduced in size by rewriting the IDs to exploit that
675
    // can rearrange the IDs to have more long runs by sorting numbers
676
    // that will need the same number of bytes to encode together
677
    //
678
0
    {
679
0
        size_t i = 0;
680
681
0
        for (; i < sortedIndices.size () && i < 1 << 7; ++i)
682
0
        {
683
0
            sortedIndices[i].first = 1;
684
0
        }
685
0
        for (; i < sortedIndices.size () && i < 1 << 14; ++i)
686
0
        {
687
0
            sortedIndices[i].first = 2;
688
0
        }
689
0
        for (; i < sortedIndices.size () && i < 1 << 21; ++i)
690
0
        {
691
0
            sortedIndices[i].first = 3;
692
0
        }
693
0
        for (; i < sortedIndices.size () && i < 1 << 28; ++i)
694
0
        {
695
0
            sortedIndices[i].first = 4;
696
0
        }
697
0
        for (; i < sortedIndices.size (); ++i)
698
0
        {
699
0
            sortedIndices[i].first = 5;
700
0
        }
701
0
    }
702
0
    sort (sortedIndices.begin (), sortedIndices.end ());
703
704
0
    vector<int> stringIndices (sortedIndices.size ());
705
706
    //
707
    // table will be stored with RLE encoding - store pairs of 'start index,end index'
708
    // so, the sequence 10,11,12,1,2,3,4  is stored as [ (10,12) , (1,4)]
709
    //
710
    // sequential IDs ignore already referenced IDs, so the sequence  11,9,10,12,13 can be stored as [ (11,11) , (9,13)]
711
    // on reading, don't reference an entry that has already been seen
712
    // on writing, need to track which entries have already been stored to allow this overlapping to occur
713
    //
714
715
0
    vector<pair<int, int>> RLEmapping;
716
717
0
    if (sortedIndices.size () > 0)
718
0
    {
719
0
        RLEmapping.resize (1);
720
0
        RLEmapping[0].first  = sortedIndices[0].second;
721
0
        RLEmapping[0].second = sortedIndices[0].second;
722
723
0
        fill (stringIndices.begin (), stringIndices.end (), -1);
724
725
0
        stringIndices[sortedIndices[0].second] = 0;
726
727
        //
728
        // as the loop below runs, nextToInclude tracks the value that can be merged with the current run length
729
        // (RLWmapping.back()) - generally this is on more than the current length, but it jumps forward
730
        // over values already seen
731
        //
732
0
        int nextToInclude = stringIndices[sortedIndices[0].second] + 1;
733
734
0
        for (size_t i = 1; i < sortedIndices.size (); ++i)
735
0
        {
736
0
            if (sortedIndices[i].second == nextToInclude)
737
0
            {
738
                //
739
                // this index can be treated as part of the current run, so extend the run to include it
740
                //
741
0
                RLEmapping.back ().second = sortedIndices[i].second;
742
0
            }
743
0
            else
744
0
            {
745
0
                pair<int, int> newEntry (
746
0
                    sortedIndices[i].second, sortedIndices[i].second);
747
0
                RLEmapping.push_back (newEntry);
748
0
            }
749
            // build mapping for this entry
750
0
            stringIndices[sortedIndices[i].second] = i;
751
752
            // what would the next entry have to be to be included in this run
753
            // skip over already mapped strings
754
0
            nextToInclude = sortedIndices[i].second + 1;
755
756
0
            while (nextToInclude < int (stringIndices.size ()) &&
757
0
                   stringIndices[nextToInclude] >= 0)
758
0
            {
759
0
                nextToInclude++;
760
0
            }
761
0
        }
762
0
    }
763
#ifdef DUMP_TABLE
764
    // dump RLE table for debugging
765
    for (size_t i = 1; i < sortedIndices.size (); ++i)
766
    {
767
        std::cout << i << ' ' << sortedIndices[i].second << std::endl;
768
    }
769
#endif
770
771
    // now compute size of uncompressed memory block for serialization
772
773
0
    int outputSize =
774
0
        8; // at least need four bytes for integer to store number of channel manifests, plus four bytes to indicate version pattern
775
776
0
    outputSize += getStringListSize (prefixedStringList);
777
778
    //
779
    // RLE mapping table size - number of entries followed by eight bytes for each run length
780
    //
781
0
    outputSize += RLEmapping.size () * 8 + 4;
782
783
    //
784
    // track which storage scheme is optimal for storing the IDs of each type
785
    // ID storage scheme: 0 = 8 bytes per ID, 1 = 4 bytes per ID, 2 = variable
786
    //
787
788
0
    std::vector<char> storageSchemes;
789
790
0
    for (size_t groupNumber = 0; groupNumber < _manifest.size (); ++groupNumber)
791
0
    {
792
0
        const ChannelGroupManifest& m = _manifest[groupNumber];
793
0
        outputSize += getStringListSize (m._channels); //size of channel group
794
0
        outputSize +=
795
0
            getStringListSize (m._components); //size of component list
796
0
        outputSize += 1;                       //size of lifetime enum
797
0
        outputSize += getStringSize (m._hashScheme);
798
0
        outputSize += getStringSize (m._encodingScheme);
799
800
0
        outputSize += 1; // ID scheme
801
0
        outputSize +=
802
0
            4; // size of storage for number of 32 bit entries in ID table
803
804
0
        uint64_t previousId                 = 0;
805
0
        uint64_t IdStorageForVariableScheme = 0;
806
0
        bool     canUse32Bits               = true;
807
0
        for (IDManifest::ChannelGroupManifest::IDTable::const_iterator i =
808
0
                 m._table.begin ();
809
0
             i != m._table.end ();
810
0
             ++i)
811
0
        {
812
813
0
            uint64_t idToStore = i->first - previousId;
814
0
            IdStorageForVariableScheme +=
815
0
                getVariableLengthIntegerSize (idToStore);
816
0
            if (idToStore >= 1llu << 32) { canUse32Bits = false; }
817
0
            previousId = i->first;
818
819
0
            for (size_t s = 0; s < m._components.size (); ++s)
820
0
            {
821
0
                int stringID  = stringSet[i->second[s]];
822
0
                int idToWrite = stringIndices[stringID];
823
0
                outputSize += getVariableLengthIntegerSize (idToWrite);
824
0
            }
825
0
        }
826
        // pick best scheme to use to store IDs
827
0
        if (canUse32Bits)
828
0
        {
829
0
            if (IdStorageForVariableScheme < m._table.size () * 4)
830
0
            {
831
                //
832
                // variable storage smaller than fixed 32 bit, so use that
833
                //
834
0
                storageSchemes.push_back (2);
835
0
                outputSize += IdStorageForVariableScheme;
836
0
            }
837
0
            else
838
0
            {
839
                //
840
                // variable scheme bigger than fixed 32 bit, but all ID differences fit into 32 bits
841
                //
842
0
                storageSchemes.push_back (1);
843
0
                outputSize += m._table.size () * 4;
844
0
            }
845
0
        }
846
0
        else
847
0
        {
848
0
            if (IdStorageForVariableScheme < m._table.size () * 8)
849
0
            {
850
                //
851
                // variable storage smaller than fixed 64 bit, so use that
852
                //
853
0
                storageSchemes.push_back (2);
854
0
                outputSize += IdStorageForVariableScheme;
855
0
            }
856
0
            else
857
0
            {
858
                //
859
                // variable scheme bigger than fixed 64 bit, and some ID differences bigger than 32 bit
860
                //
861
0
                storageSchemes.push_back (0);
862
0
                outputSize += m._table.size () * 8;
863
0
            }
864
0
        }
865
0
    }
866
867
    //
868
    // resize output array
869
    //
870
0
    data.resize (outputSize);
871
872
    //
873
    // populate output array
874
    //
875
0
    char* outPtr = &data[0];
876
877
    //
878
    // zeroes to indicate this is version 0 of the header
879
    //
880
0
    Xdr::write<CharPtrIO> (outPtr, int (0));
881
882
    //
883
    // table of strings
884
    //
885
0
    writeStringList (outPtr, prefixedStringList);
886
887
    //
888
    // RLE block
889
    //
890
0
    Xdr::write<CharPtrIO> (outPtr, int (RLEmapping.size ()));
891
0
    for (size_t i = 0; i < RLEmapping.size (); ++i)
892
0
    {
893
0
        Xdr::write<CharPtrIO> (outPtr, RLEmapping[i].first);
894
0
        Xdr::write<CharPtrIO> (outPtr, RLEmapping[i].second);
895
0
    }
896
897
    //
898
    // number of manifests
899
    //
900
0
    Xdr::write<CharPtrIO> (outPtr, int (_manifest.size ()));
901
0
    int manifestIndex = 0;
902
903
0
    for (size_t groupNumber = 0; groupNumber < _manifest.size (); ++groupNumber)
904
0
    {
905
0
        const ChannelGroupManifest& m = _manifest[groupNumber];
906
        //
907
        // manifest header
908
        //
909
0
        writeStringList (outPtr, m._channels);
910
0
        writeStringList (outPtr, m._components);
911
0
        Xdr::write<CharPtrIO> (outPtr, char (m._lifeTime));
912
0
        writePascalString (outPtr, m._hashScheme);
913
0
        writePascalString (outPtr, m._encodingScheme);
914
915
0
        char scheme = storageSchemes[manifestIndex];
916
0
        Xdr::write<CharPtrIO> (outPtr, scheme);
917
918
0
        Xdr::write<CharPtrIO> (outPtr, int (m._table.size ()));
919
920
0
        uint64_t previousId = 0;
921
        //
922
        // table
923
        //
924
0
        for (IDManifest::ChannelGroupManifest::IDTable::const_iterator i =
925
0
                 m._table.begin ();
926
0
             i != m._table.end ();
927
0
             ++i)
928
0
        {
929
930
0
            uint64_t idToWrite = i->first - previousId;
931
0
            switch (scheme)
932
0
            {
933
0
                case 0: Xdr::write<CharPtrIO> (outPtr, idToWrite); break;
934
0
                case 1:
935
0
                    Xdr::write<CharPtrIO> (outPtr, (unsigned int) idToWrite);
936
0
                    break;
937
0
                case 2: writeVariableLengthInteger (outPtr, idToWrite);
938
0
            }
939
940
0
            previousId = i->first;
941
942
0
            for (size_t s = 0; s < m._components.size (); ++s)
943
0
            {
944
0
                int stringID  = stringSet[i->second[s]];
945
0
                int idToWrite = stringIndices[stringID];
946
0
                writeVariableLengthInteger (outPtr, idToWrite);
947
0
            }
948
0
        }
949
0
        manifestIndex++;
950
0
    }
951
    //
952
    // check we've written the ID manifest correctly
953
    //
954
0
    if (outPtr != &data[0] + data.size ())
955
0
    {
956
0
        throw IEX_NAMESPACE::ArgExc ("Error - IDManifest size error");
957
0
    }
958
0
}
959
960
bool
961
IDManifest::operator== (const IDManifest& other) const
962
0
{
963
0
    return other._manifest == _manifest;
964
0
}
965
966
bool
967
IDManifest::operator!= (const IDManifest& other) const
968
0
{
969
0
    return !(*this == other);
970
0
}
971
972
bool
973
IDManifest::merge (const IDManifest& other)
974
0
{
975
0
    bool conflict = false;
976
0
    for (size_t otherManifest = 0; otherManifest < other._manifest.size ();
977
0
         ++otherManifest)
978
0
    {
979
0
        bool merged = false;
980
0
        for (size_t thisManifest = 0; thisManifest < _manifest.size ();
981
0
             ++thisManifest)
982
0
        {
983
0
            if (_manifest[thisManifest]._channels ==
984
0
                other._manifest[otherManifest]._channels)
985
0
            {
986
                // found same channels
987
988
0
                merged = true;
989
990
0
                if (other._manifest[otherManifest]._components !=
991
0
                    _manifest[thisManifest]._components)
992
0
                {
993
                    // cannot merge if components are different
994
0
                    conflict = true;
995
0
                }
996
0
                else
997
0
                {
998
999
                    //                    if(other._manifest[otherManifest]._encodingScheme !=  _manifest[thisManifest]._encodingScheme ||
1000
                    //                        other._manifest[otherManifest]._hashScheme !=  _manifest[thisManifest]._hashScheme ||
1001
                    //                        other._manifest[otherManifest]._hashScheme !=  _manifest[thisManifest]._hashScheme ||
1002
                    //                        other._manifest[otherManifest]._lifeTime !=  _manifest[thisManifest]._lifeTime)
1003
                    //                    {
1004
                    //                        conflict = true;
1005
                    //                    }
1006
1007
0
                    for (IDManifest::ChannelGroupManifest::ConstIterator it =
1008
0
                             other._manifest[otherManifest].begin ();
1009
0
                         it != other._manifest[otherManifest].end ();
1010
0
                         ++it)
1011
0
                    {
1012
0
                        IDManifest::ChannelGroupManifest::ConstIterator ours =
1013
0
                            _manifest[thisManifest].find (it.id ());
1014
0
                        if (ours == _manifest[thisManifest].end ())
1015
0
                        {
1016
0
                            _manifest[thisManifest].insert (
1017
0
                                it.id (), it.text ());
1018
0
                        }
1019
0
                        else
1020
0
                        {
1021
0
                            if (ours.text () != it.text ()) { conflict = true; }
1022
0
                        }
1023
0
                    }
1024
0
                }
1025
0
            }
1026
0
        }
1027
1028
0
        if (!merged) { _manifest.push_back (other._manifest[otherManifest]); }
1029
0
    }
1030
1031
0
    return conflict;
1032
0
}
1033
1034
CompressedIDManifest::CompressedIDManifest ()
1035
1.85k
    : _compressedDataSize (0), _uncompressedDataSize (0), _data (NULL)
1036
1.85k
{}
1037
1038
CompressedIDManifest::CompressedIDManifest (const CompressedIDManifest& other)
1039
0
    : _compressedDataSize (other._compressedDataSize)
1040
0
    , _uncompressedDataSize (other._uncompressedDataSize)
1041
0
    , _data ((unsigned char*) malloc (other._compressedDataSize))
1042
0
{
1043
0
    memcpy (_data, other._data, _compressedDataSize);
1044
0
}
1045
1046
CompressedIDManifest&
1047
CompressedIDManifest::operator= (const CompressedIDManifest& other)
1048
901
{
1049
901
    if (this != &other)
1050
901
    {
1051
901
        if (_data) { free (_data); }
1052
901
        _data = (unsigned char*) malloc (other._compressedDataSize);
1053
901
        _compressedDataSize   = other._compressedDataSize;
1054
901
        _uncompressedDataSize = other._uncompressedDataSize;
1055
901
        memcpy (_data, other._data, _compressedDataSize);
1056
901
    }
1057
901
    return *this;
1058
901
}
1059
1060
CompressedIDManifest::~CompressedIDManifest ()
1061
1.85k
{
1062
1.85k
    if (_data) { free (_data); }
1063
1.85k
    _data               = NULL;
1064
1.85k
    _compressedDataSize = 0;
1065
1.85k
}
1066
1067
CompressedIDManifest::CompressedIDManifest (const IDManifest& manifest)
1068
0
{
1069
    //
1070
    // make a compressed copy of the manifest by serializing the data into contiguous memory,
1071
    // then calling zlib to compress
1072
    //
1073
1074
0
    std::vector<char> serial;
1075
1076
0
    manifest.serialize (serial);
1077
1078
0
    size_t outputSize = serial.size ();
1079
1080
    //
1081
    // allocate a buffer which is guaranteed to be big enough for compression
1082
    //
1083
0
    size_t compressedBufferSize = exr_compress_max_buffer_size (outputSize);
1084
0
    size_t compressedDataSize;
1085
0
    _data = (unsigned char*) malloc (compressedBufferSize);
1086
0
    if (EXR_ERR_SUCCESS != exr_compress_buffer (
1087
0
                               nullptr,
1088
0
                               -1,
1089
0
                               serial.data (),
1090
0
                               outputSize,
1091
0
                               _data,
1092
0
                               compressedBufferSize,
1093
0
                               &compressedDataSize))
1094
0
    {
1095
0
        throw IEX_NAMESPACE::InputExc ("ID manifest compression failed");
1096
0
    }
1097
1098
    // now call realloc to reallocate the buffer to a smaller size - this might free up memory
1099
0
    _data = (unsigned char*) realloc (_data, compressedDataSize);
1100
1101
0
    _uncompressedDataSize = outputSize;
1102
0
    _compressedDataSize   = compressedDataSize;
1103
0
}
1104
1105
IDManifest::ChannelGroupManifest::ChannelGroupManifest ()
1106
0
    : _lifeTime (IDManifest::LIFETIME_STABLE)
1107
0
    , _hashScheme (IDManifest::UNKNOWN)
1108
0
    , _encodingScheme (IDManifest::UNKNOWN)
1109
0
    , _insertingEntry (false)
1110
0
{}
1111
1112
const vector<string>&
1113
IDManifest::ChannelGroupManifest::getComponents () const
1114
0
{
1115
0
    return _components;
1116
0
}
1117
1118
set<string>&
1119
IDManifest::ChannelGroupManifest::getChannels ()
1120
0
{
1121
0
    return _channels;
1122
0
}
1123
1124
const set<string>&
1125
IDManifest::ChannelGroupManifest::getChannels () const
1126
0
{
1127
0
    return _channels;
1128
0
}
1129
1130
void
1131
IDManifest::ChannelGroupManifest::setChannel (const string& channel)
1132
0
{
1133
0
    _channels.clear ();
1134
0
    _channels.insert (channel);
1135
0
}
1136
1137
void
1138
IDManifest::ChannelGroupManifest::setChannels (const set<string>& channels)
1139
0
{
1140
0
    _channels = channels;
1141
0
}
1142
1143
//
1144
// set number of components of table
1145
//
1146
void
1147
IDManifest::ChannelGroupManifest::setComponents (
1148
    const std::vector<std::string>& components)
1149
0
{
1150
1151
    // if there are already entries in the table, cannot change the number of components
1152
0
    if (_table.size () != 0 && components.size () != _components.size ())
1153
0
    {
1154
0
        THROW (
1155
0
            IEX_NAMESPACE::ArgExc,
1156
0
            "attempt to change number of components in manifest once entries have been added");
1157
0
    }
1158
0
    _components = components;
1159
0
}
1160
1161
void
1162
IDManifest::ChannelGroupManifest::setComponent (const std::string& component)
1163
0
{
1164
0
    vector<string> components (1);
1165
0
    components[0] = component;
1166
0
    setComponents (components);
1167
0
}
1168
1169
IDManifest::ChannelGroupManifest::ConstIterator
1170
IDManifest::ChannelGroupManifest::begin () const
1171
0
{
1172
0
    return IDManifest::ChannelGroupManifest::ConstIterator (_table.begin ());
1173
0
}
1174
1175
IDManifest::ChannelGroupManifest::Iterator
1176
IDManifest::ChannelGroupManifest::begin ()
1177
0
{
1178
0
    return IDManifest::ChannelGroupManifest::Iterator (_table.begin ());
1179
0
}
1180
1181
IDManifest::ChannelGroupManifest::ConstIterator
1182
IDManifest::ChannelGroupManifest::end () const
1183
0
{
1184
0
    return IDManifest::ChannelGroupManifest::ConstIterator (_table.end ());
1185
0
}
1186
1187
IDManifest::ChannelGroupManifest::Iterator
1188
IDManifest::ChannelGroupManifest::end ()
1189
0
{
1190
0
    return IDManifest::ChannelGroupManifest::Iterator (_table.end ());
1191
0
}
1192
1193
IDManifest::ChannelGroupManifest::ConstIterator
1194
IDManifest::ChannelGroupManifest::find (uint64_t idValue) const
1195
0
{
1196
0
    return IDManifest::ChannelGroupManifest::ConstIterator (
1197
0
        _table.find (idValue));
1198
0
}
1199
1200
void
1201
IDManifest::ChannelGroupManifest::erase (uint64_t idValue)
1202
0
{
1203
0
    _table.erase (idValue);
1204
0
}
1205
size_t
1206
IDManifest::ChannelGroupManifest::size () const
1207
0
{
1208
0
    return _table.size ();
1209
0
}
1210
1211
IDManifest::ChannelGroupManifest::Iterator
1212
IDManifest::ChannelGroupManifest::find (uint64_t idValue)
1213
0
{
1214
0
    return IDManifest::ChannelGroupManifest::Iterator (_table.find (idValue));
1215
0
}
1216
1217
std::vector<std::string>&
1218
IDManifest::ChannelGroupManifest::operator[] (uint64_t idValue)
1219
0
{
1220
0
    return _table[idValue];
1221
0
}
1222
1223
IDManifest::ChannelGroupManifest::Iterator
1224
IDManifest::ChannelGroupManifest::insert (
1225
    uint64_t idValue, const std::string& text)
1226
0
{
1227
0
    if (_components.size () != 1)
1228
0
    {
1229
0
        THROW (
1230
0
            IEX_NAMESPACE::ArgExc,
1231
0
            "Cannot insert single component attribute into manifest with multiple components");
1232
0
    }
1233
0
    vector<string> tempVector (1);
1234
0
    tempVector[0] = text;
1235
0
    return IDManifest::ChannelGroupManifest::Iterator (
1236
0
        _table.insert (make_pair (idValue, tempVector)).first);
1237
0
}
1238
1239
IDManifest::ChannelGroupManifest::Iterator
1240
IDManifest::ChannelGroupManifest::insert (
1241
    uint64_t idValue, const std::vector<std::string>& text)
1242
0
{
1243
0
    if (_components.size () != text.size ())
1244
0
    {
1245
0
        THROW (
1246
0
            IEX_NAMESPACE::ArgExc,
1247
0
            "mismatch between number of components in manifest and number of components in inserted entry");
1248
0
    }
1249
0
    return IDManifest::ChannelGroupManifest::Iterator (
1250
0
        _table.insert (make_pair (idValue, text)).first);
1251
0
}
1252
1253
uint64_t
1254
IDManifest::ChannelGroupManifest::insert (const std::vector<std::string>& text)
1255
0
{
1256
0
    uint64_t hash;
1257
0
    if (_hashScheme == MURMURHASH3_32) { hash = MurmurHash32 (text); }
1258
0
    else if (_hashScheme == MURMURHASH3_64) { hash = MurmurHash64 (text); }
1259
0
    else
1260
0
    {
1261
0
        THROW (
1262
0
            IEX_NAMESPACE::ArgExc,
1263
0
            "Cannot compute hash: unknown hashing scheme");
1264
0
    }
1265
0
    insert (hash, text);
1266
0
    return hash;
1267
0
}
1268
1269
uint64_t
1270
IDManifest::ChannelGroupManifest::insert (const std::string& text)
1271
0
{
1272
0
    uint64_t hash;
1273
0
    if (_hashScheme == MURMURHASH3_32) { hash = MurmurHash32 (text); }
1274
0
    else if (_hashScheme == MURMURHASH3_64) { hash = MurmurHash64 (text); }
1275
0
    else
1276
0
    {
1277
0
        THROW (
1278
0
            IEX_NAMESPACE::ArgExc,
1279
0
            "Cannot compute hash: unknown hashing scheme");
1280
0
    }
1281
0
    insert (hash, text);
1282
0
    return hash;
1283
0
}
1284
1285
IDManifest::ChannelGroupManifest&
1286
IDManifest::ChannelGroupManifest::operator<< (uint64_t idValue)
1287
0
{
1288
0
    if (_insertingEntry)
1289
0
    {
1290
0
        THROW (
1291
0
            IEX_NAMESPACE::ArgExc,
1292
0
            "not enough components inserted into previous entry in ID table before inserting new entry");
1293
0
    }
1294
1295
0
    _insertionIterator =
1296
0
        _table.insert (make_pair (idValue, std::vector<std::string> ())).first;
1297
1298
    //
1299
    // flush out previous entry: reinserting an attribute overwrites previous entry
1300
    //
1301
0
    _insertionIterator->second.resize (0);
1302
1303
    //
1304
    // curious edge-case: it's possible to have an ID table with no strings, just a list of IDs
1305
    // There's little purpose to this, but it means that this entry is now 'complete'
1306
    //
1307
0
    if (_components.size () == 0) { _insertingEntry = false; }
1308
0
    else { _insertingEntry = true; }
1309
0
    return *this;
1310
0
}
1311
1312
IDManifest::ChannelGroupManifest&
1313
IDManifest::ChannelGroupManifest::operator<< (const std::string& text)
1314
0
{
1315
0
    if (!_insertingEntry)
1316
0
    {
1317
0
        THROW (
1318
0
            IEX_NAMESPACE::ArgExc,
1319
0
            "attempt to insert too many strings into entry, or attempt to insert text before ID integer");
1320
0
    }
1321
0
    if (_insertionIterator->second.size () >= _components.size ())
1322
0
    {
1323
0
        THROW (
1324
0
            IEX_NAMESPACE::ArgExc,
1325
0
            "Internal error: too many strings in component");
1326
0
    }
1327
0
    _insertionIterator->second.push_back (text);
1328
1329
    //
1330
    // if the last component has been inserted, switch off insertingEntry, to mark all entries as complete
1331
    //
1332
0
    if (_insertionIterator->second.size () == _components.size ())
1333
0
    {
1334
0
        _insertingEntry = false;
1335
0
    }
1336
0
    return *this;
1337
0
}
1338
1339
bool
1340
IDManifest::ChannelGroupManifest::operator== (
1341
    const IDManifest::ChannelGroupManifest& other) const
1342
0
{
1343
0
    return (
1344
0
        _lifeTime == other._lifeTime && _components == other._components &&
1345
0
        _hashScheme == other._hashScheme && _components == other._components &&
1346
0
        _table == other._table);
1347
0
}
1348
1349
size_t
1350
IDManifest::size () const
1351
0
{
1352
0
    return _manifest.size ();
1353
0
}
1354
1355
size_t
1356
IDManifest::find (const string& channel) const
1357
0
{
1358
    // search the set of channels for each ChannelGroupManifest searching for
1359
    // one that contains 'channel'
1360
0
    for (size_t i = 0; i < _manifest.size (); ++i)
1361
0
    {
1362
1363
0
        if (_manifest[i].getChannels ().find (channel) !=
1364
0
            _manifest[i].getChannels ().end ())
1365
0
        {
1366
0
            return i;
1367
0
        }
1368
0
    }
1369
    //  not find, return size()
1370
0
    return _manifest.size ();
1371
0
}
1372
1373
IDManifest::ChannelGroupManifest&
1374
IDManifest::add (const set<string>& group)
1375
0
{
1376
0
    _manifest.push_back (ChannelGroupManifest ());
1377
0
    ChannelGroupManifest& mfst = _manifest.back ();
1378
0
    mfst._channels             = group;
1379
0
    return mfst;
1380
0
}
1381
1382
IDManifest::ChannelGroupManifest&
1383
IDManifest::add (const string& channel)
1384
0
{
1385
0
    _manifest.push_back (ChannelGroupManifest ());
1386
0
    ChannelGroupManifest& mfst = _manifest.back ();
1387
0
    mfst._channels.insert (channel);
1388
0
    return mfst;
1389
0
}
1390
1391
IDManifest::ChannelGroupManifest&
1392
IDManifest::add (const IDManifest::ChannelGroupManifest& table)
1393
0
{
1394
0
    _manifest.push_back (table);
1395
0
    return _manifest.back ();
1396
0
}
1397
1398
IDManifest::ChannelGroupManifest&
1399
IDManifest::operator[] (size_t index)
1400
0
{
1401
0
    return _manifest[index];
1402
0
}
1403
1404
const IDManifest::ChannelGroupManifest&
1405
IDManifest::operator[] (size_t index) const
1406
0
{
1407
0
    return _manifest[index];
1408
0
}
1409
1410
namespace
1411
{
1412
1413
//-----------------------------------------------------------------------------
1414
// MurmurHash3 was written by Austin Appleby, and is placed in the public
1415
// domain. The author hereby disclaims copyright to this source code.
1416
//
1417
// smhasher provides two different 128 bit hash schemes, optimised for either
1418
// 32 or 64 bit architectures. IDManifest uses only the 64 bit optimised version
1419
// of the 128 bit hash function to generate '64 bit hashes'
1420
//-----------------------------------------------------------------------------
1421
// Platform-specific functions and macros
1422
// Microsoft Visual Studio
1423
#if defined(_MSC_VER)
1424
#    define FORCE_INLINE __forceinline
1425
#    define ROTL32(x, y) _rotl (x, y)
1426
#    define ROTL64(x, y) _rotl64 (x, y)
1427
#    define BIG_CONSTANT(x) (x)
1428
// Other compilers
1429
#else // defined(_MSC_VER)
1430
#    define FORCE_INLINE inline __attribute__ ((always_inline))
1431
inline uint32_t
1432
rotl32 (uint32_t x, int8_t r)
1433
0
{
1434
0
    return (x << r) | (x >> (32 - r));
1435
0
}
1436
inline uint64_t
1437
rotl64 (uint64_t x, int8_t r)
1438
0
{
1439
0
    return (x << r) | (x >> (64 - r));
1440
0
}
1441
0
#    define ROTL32(x, y) rotl32 (x, y)
1442
0
#    define ROTL64(x, y) rotl64 (x, y)
1443
0
#    define BIG_CONSTANT(x) (x##LLU)
1444
#endif // !defined(_MSC_VER)
1445
//-----------------------------------------------------------------------------
1446
// Block read - if your platform needs to do endian-swapping or can only
1447
// handle aligned reads, do the conversion here
1448
FORCE_INLINE uint32_t
1449
getblock32 (const uint32_t* p, int i)
1450
0
{
1451
0
    return p[i];
1452
0
}
1453
FORCE_INLINE uint64_t
1454
getblock64 (const uint64_t* p, int i)
1455
0
{
1456
0
    return p[i];
1457
0
}
1458
//-----------------------------------------------------------------------------
1459
// Finalization mix - force all bits of a hash block to avalanche
1460
FORCE_INLINE uint32_t
1461
fmix32 (uint32_t h)
1462
0
{
1463
0
    h ^= h >> 16;
1464
0
    h *= 0x85ebca6b;
1465
0
    h ^= h >> 13;
1466
0
    h *= 0xc2b2ae35;
1467
0
    h ^= h >> 16;
1468
0
    return h;
1469
0
}
1470
//----------
1471
FORCE_INLINE uint64_t
1472
fmix64 (uint64_t k)
1473
0
{
1474
0
    k ^= k >> 33;
1475
0
    k *= BIG_CONSTANT (0xff51afd7ed558ccd);
1476
0
    k ^= k >> 33;
1477
0
    k *= BIG_CONSTANT (0xc4ceb9fe1a85ec53);
1478
0
    k ^= k >> 33;
1479
0
    return k;
1480
0
}
1481
//-----------------------------------------------------------------------------
1482
void
1483
MurmurHash3_x86_32 (const void* key, int len, uint32_t seed, void* out)
1484
0
{
1485
0
    const uint8_t* data    = (const uint8_t*) key;
1486
0
    const int      nblocks = len / 4;
1487
0
    uint32_t       h1      = seed;
1488
0
    const uint32_t c1      = 0xcc9e2d51;
1489
0
    const uint32_t c2      = 0x1b873593;
1490
    //----------
1491
    // body
1492
0
    const uint32_t* blocks = (const uint32_t*) (data + nblocks * 4);
1493
0
    for (int i = -nblocks; i; i++)
1494
0
    {
1495
0
        uint32_t k1 = getblock32 (blocks, i);
1496
0
        k1 *= c1;
1497
0
        k1 = ROTL32 (k1, 15);
1498
0
        k1 *= c2;
1499
1500
0
        h1 ^= k1;
1501
0
        h1 = ROTL32 (h1, 13);
1502
0
        h1 = h1 * 5 + 0xe6546b64;
1503
0
    }
1504
    //----------
1505
    // tail
1506
0
    const uint8_t* tail = (const uint8_t*) (data + nblocks * 4);
1507
0
    uint32_t       k1   = 0;
1508
0
    switch (len & 3)
1509
0
    {
1510
0
        case 3: k1 ^= tail[2] << 16;
1511
0
        case 2: k1 ^= tail[1] << 8;
1512
0
        case 1:
1513
0
            k1 ^= tail[0];
1514
0
            k1 *= c1;
1515
0
            k1 = ROTL32 (k1, 15);
1516
0
            k1 *= c2;
1517
0
            h1 ^= k1;
1518
0
    };
1519
    //----------
1520
    // finalization
1521
0
    h1 ^= len;
1522
0
    h1               = fmix32 (h1);
1523
0
    *(uint32_t*) out = h1;
1524
0
}
1525
1526
//-----------------------------------------------------------------------------
1527
void
1528
MurmurHash3_x64_128 (
1529
    const void* key, const int len, const uint32_t seed, void* out)
1530
0
{
1531
0
    const uint8_t* data    = (const uint8_t*) key;
1532
0
    const int      nblocks = len / 16;
1533
0
    uint64_t       h1      = seed;
1534
0
    uint64_t       h2      = seed;
1535
0
    const uint64_t c1      = BIG_CONSTANT (0x87c37b91114253d5);
1536
0
    const uint64_t c2      = BIG_CONSTANT (0x4cf5ad432745937f);
1537
    //----------
1538
    // body
1539
0
    const uint64_t* blocks = (const uint64_t*) (data);
1540
0
    for (int i = 0; i < nblocks; i++)
1541
0
    {
1542
0
        uint64_t k1 = getblock64 (blocks, i * 2 + 0);
1543
0
        uint64_t k2 = getblock64 (blocks, i * 2 + 1);
1544
0
        k1 *= c1;
1545
0
        k1 = ROTL64 (k1, 31);
1546
0
        k1 *= c2;
1547
0
        h1 ^= k1;
1548
0
        h1 = ROTL64 (h1, 27);
1549
0
        h1 += h2;
1550
0
        h1 = h1 * 5 + 0x52dce729;
1551
0
        k2 *= c2;
1552
0
        k2 = ROTL64 (k2, 33);
1553
0
        k2 *= c1;
1554
0
        h2 ^= k2;
1555
0
        h2 = ROTL64 (h2, 31);
1556
0
        h2 += h1;
1557
0
        h2 = h2 * 5 + 0x38495ab5;
1558
0
    }
1559
    //----------
1560
    // tail
1561
0
    const uint8_t* tail = (const uint8_t*) (data + nblocks * 16);
1562
0
    uint64_t       k1   = 0;
1563
0
    uint64_t       k2   = 0;
1564
0
    switch (len & 15)
1565
0
    {
1566
0
        case 15: k2 ^= ((uint64_t) tail[14]) << 48;
1567
0
        case 14: k2 ^= ((uint64_t) tail[13]) << 40;
1568
0
        case 13: k2 ^= ((uint64_t) tail[12]) << 32;
1569
0
        case 12: k2 ^= ((uint64_t) tail[11]) << 24;
1570
0
        case 11: k2 ^= ((uint64_t) tail[10]) << 16;
1571
0
        case 10: k2 ^= ((uint64_t) tail[9]) << 8;
1572
0
        case 9:
1573
0
            k2 ^= ((uint64_t) tail[8]) << 0;
1574
0
            k2 *= c2;
1575
0
            k2 = ROTL64 (k2, 33);
1576
0
            k2 *= c1;
1577
0
            h2 ^= k2;
1578
0
        case 8: k1 ^= ((uint64_t) tail[7]) << 56;
1579
0
        case 7: k1 ^= ((uint64_t) tail[6]) << 48;
1580
0
        case 6: k1 ^= ((uint64_t) tail[5]) << 40;
1581
0
        case 5: k1 ^= ((uint64_t) tail[4]) << 32;
1582
0
        case 4: k1 ^= ((uint64_t) tail[3]) << 24;
1583
0
        case 3: k1 ^= ((uint64_t) tail[2]) << 16;
1584
0
        case 2: k1 ^= ((uint64_t) tail[1]) << 8;
1585
0
        case 1:
1586
0
            k1 ^= ((uint64_t) tail[0]) << 0;
1587
0
            k1 *= c1;
1588
0
            k1 = ROTL64 (k1, 31);
1589
0
            k1 *= c2;
1590
0
            h1 ^= k1;
1591
0
    };
1592
    //----------
1593
    // finalization
1594
0
    h1 ^= len;
1595
0
    h2 ^= len;
1596
0
    h1 += h2;
1597
0
    h2 += h1;
1598
0
    h1 = fmix64 (h1);
1599
0
    h2 = fmix64 (h2);
1600
0
    h1 += h2;
1601
0
    h2 += h1;
1602
0
    ((uint64_t*) out)[0] = h1;
1603
0
    ((uint64_t*) out)[1] = h2;
1604
0
}
1605
//-----------------------------------------------------------------------------
1606
1607
//
1608
// combine the idStrings into a single string, separating each with a ; character
1609
// (use of the ; character is discouraged, though not prohibited)
1610
//
1611
void
1612
catString (const vector<string>& idString, std::string& str)
1613
0
{
1614
0
    str = idString[0];
1615
0
    for (size_t i = 1; i < idString.size (); ++i)
1616
0
    {
1617
0
        str += ";";
1618
0
        str += idString[i];
1619
0
    }
1620
0
}
1621
} // namespace
1622
1623
unsigned int
1624
IDManifest::MurmurHash32 (const std::string& idString)
1625
0
{
1626
0
    unsigned int out;
1627
0
    MurmurHash3_x86_32 (idString.c_str (), idString.size (), 0, (void*) &out);
1628
0
    return out;
1629
0
}
1630
1631
uint64_t
1632
IDManifest::MurmurHash64 (const std::string& idString)
1633
0
{
1634
1635
0
    uint64_t out[2];
1636
0
    MurmurHash3_x64_128 (idString.c_str (), idString.size (), 0, out);
1637
0
    return out[0];
1638
0
}
1639
1640
unsigned int
1641
IDManifest::MurmurHash32 (const vector<string>& idString)
1642
0
{
1643
0
    if (idString.size () == 0) { return 0; }
1644
0
    std::string str;
1645
0
    catString (idString, str);
1646
0
    return MurmurHash32 (str);
1647
0
}
1648
1649
uint64_t
1650
IDManifest::MurmurHash64 (const vector<string>& idString)
1651
0
{
1652
0
    if (idString.size () == 0) { return 0; }
1653
0
    std::string str;
1654
0
    catString (idString, str);
1655
0
    return MurmurHash64 (str);
1656
0
}
1657
1658
OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT