Coverage Report

Created: 2026-01-25 07:18

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