Coverage Report

Created: 2026-08-31 06:15

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