<commit_before>/*
Copyright 2011-2017 Frederic Langlet
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
you may obtain a copy of the License at

                http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#include <algorithm>
#include <vector>
#include <sstream>
#include "../IllegalArgumentException.hpp"
#include "EntropyUtils.hpp"

using namespace kanzi;

class FreqSortData {
public:
    uint _symbol;
    int* _errors;
    uint* _frequencies;

    FreqSortData(int errors[], uint frequencies[], int symbol)
    {
        _errors = errors;
        _frequencies = frequencies;
        _symbol = symbol;
    }
};

struct FreqDataComparator {
    bool operator()(FreqSortData* fd1, FreqSortData* fd2) const
    {
        int res = fd1->_errors[fd1->_symbol] - fd2->_errors[fd2->_symbol];

        // Increasing frequency
        if (res == 0) {
            res = fd1->_frequencies[fd1->_symbol] - fd2->_frequencies[fd2->_symbol];

            // Increasing symbol
            if (res == 0)
                return fd1->_symbol < fd2->_symbol;
        }

        return res < 0;
    }
};

// alphabet must be sorted in increasing order
// length = alphabet array length
int EntropyUtils::encodeAlphabet(OutputBitStream& obs, uint alphabet[], int length, int count)
{
    // Alphabet length must be a power of 2
    if ((length & (length - 1)) != 0)
        return -1;

    if (count > length)
        return -1;

    // First, push alphabet encoding mode
    if ((length > 0) && (count == length)) {
        // Fuint64 alphabet
        obs.writeBit(FULL_ALPHABET);

        if (count == 256)
            obs.writeBit(ALPHABET_256); // shortcut
        else {
            int log = 1;

            while (1 << log <= count)
                log++;

            // Write alphabet size
            obs.writeBit(ALPHABET_NOT_256);
            obs.writeBits(log - 1, 5);
            obs.writeBits(count, log);
        }

        return count;
    }

    obs.writeBit(PARTIAL_ALPHABET);

    if ((length == 256) && (count >= 32) && (count <= 224)) {
        // Regular alphabet of symbols less than 256
        obs.writeBit(BIT_ENCODED_ALPHABET_256);
		uint64 masks[4] = { 0, 0, 0, 0 };

		for (int i = 0; i < count; i++)
			masks[alphabet[i] >> 6] |= ((uint64(1)) << (alphabet[i] & 63));

        for (int i = 0; i < 4; i++)
            obs.writeBits(masks[i], 64);

        return count;
    }

    obs.writeBit(DELTA_ENCODED_ALPHABET);
    int* diffs = new int[count];

    if (length - count < count) {
        // Encode all missing symbols
        count = length - count;
        int log = 1;

        while (1 << log <= count)
            log++;

        // Write length
        obs.writeBits(log - 1, 4);
        obs.writeBits(count, log);

        if (count == 0)
            return 0;

        obs.writeBit(ABSENT_SYMBOLS_MASK);
        log = 1;

        while (1 << log <= length)
            log++;

        // Write log(alphabet size)
        obs.writeBits(log - 1, 5);
        uint symbol = 0;
        int previous = 0;

        // Create deltas of missing symbols
        for (int n = 0, i = 0; n < count;) {
            if (symbol == alphabet[i]) {
                if (i < length - 1 - count)
                    i++;

                symbol++;
                continue;
            }

            diffs[n] = symbol - previous;
            symbol++;
            previous = symbol;
            n++;
        }
    }
    else {
        // Encode all present symbols
        int log = 1;

        while (1 << log <= count)
            log++;

        // Write length
        obs.writeBits(log - 1, 4);
        obs.writeBits(count, log);

        if (count == 0)
            return 0;

        obs.writeBit(PRESENT_SYMBOLS_MASK);
        int previous = 0;

        // Create deltas of present symbols
        for (int i = 0; i < count; i++) {
            diffs[i] = alphabet[i] - previous;
            previous = alphabet[i] + 1;
        }
    }

    const int ckSize = (count <= 64) ? 8 : 16;

    // Encode all deltas by chunks
    for (int i = 0; i < count; i += ckSize) {
        int max = 0;

        // Find log(max(deltas)) for this chunk
        for (int j = i; (j < count) && (j < i + ckSize); j++) {
            if (max < diffs[j])
                max = diffs[j];
        }

        int log = 1;

        while (1 << log <= max)
            log++;

        obs.writeBits(log - 1, 4);

        // Write deltas for this chunk
        for (int j = i; (j < count) && (j < i + ckSize); j++)
            encodeSize(obs, log, diffs[j]);
    }

    delete[] diffs;
    return count;
}

void EntropyUtils::encodeSize(OutputBitStream& obs, int log, int val)
{
    obs.writeBits(val, log);
}

uint64 EntropyUtils::decodeSize(InputBitStream& ibs, int log)
{
    return ibs.readBits(log);
}

int EntropyUtils::decodeAlphabet(InputBitStream& ibs, uint alphabet[]) THROW
{
    // Read encoding mode from bitstream
    const int alphabetType = ibs.readBit();

    if (alphabetType == FULL_ALPHABET) {
        int alphabetSize;

        if (ibs.readBit() == ALPHABET_256)
            alphabetSize = 256;
        else {
            int log = 1 + int(ibs.readBits(5));
            alphabetSize = int(ibs.readBits(log));
        }

        if (alphabetSize > 256) {
            stringstream ss;
            ss << "Invalid bitstream: incorrect alphabet size: " << alphabetSize;
            throw BitStreamException(ss.str(), BitStreamException::INVALID_STREAM);
        }

        // Full alphabet
        for (int i = 0; i < alphabetSize; i++)
            alphabet[i] = i;

        return alphabetSize;
    }

    int count = 0;
    const int mode = ibs.readBit();

    if (mode == BIT_ENCODED_ALPHABET_256) {
        // Decode presence flags
        for (int i = 0; i < 256; i += 64) {
            const uint64 val = ibs.readBits(64);

            for (int j = 0; j < 64; j++) {
                if ((val & ((uint64(1)) << j)) != 0) {
                    alphabet[count] = i + j;
                    count++;
                }
            }
        }

        return count;
    }

    // DELTA_ENCODED_ALPHABET
    int log = 1 + (int)ibs.readBits(4);
    count = (int)ibs.readBits(log);

    if (count == 0)
        return 0;

    const int ckSize = (count <= 64) ? 8 : 16;
    int n = 0;
    int symbol = 0;

    if (ibs.readBit() == ABSENT_SYMBOLS_MASK) {
        int alphabetSize = 1 << (int)ibs.readBits(5);

        // Read missing symbols
        for (int i = 0; i < count; i += ckSize) {
            log = 1 + (int)ibs.readBits(4);

            // Read deltas for this chunk
            for (int j = i; (j < count) && (j < i + ckSize); j++) {
                const int next = symbol + (int)decodeSize(ibs, log);

                while ((symbol < next) && (n < alphabetSize)) {
                    alphabet[n] = symbol++;
                    n++;
                }

                symbol++;
            }
        }

        count = alphabetSize - count;

        while (n < count)
            alphabet[n++] = symbol++;
    }
    else {
        // Read present symbols
        for (int i = 0; i < count; i += ckSize) {
            log = 1 + (int)ibs.readBits(4);

            // Read deltas for this chunk
            for (int j = i; (j < count) && (j < i + ckSize); j++) {
                symbol += (int)decodeSize(ibs, log);
                alphabet[j] = symbol;
                symbol++;
            }
        }
    }

    return count;
}

// Returns the size of the alphabet
// length is the length of the alphabet array
// 'totalFreq 'is the sum of frequencies.
// 'scale' is the target new total of frequencies
// The alphabet and freqs parameters are updated
int EntropyUtils::normalizeFrequencies(uint freqs[], uint alphabet[], int length, uint totalFreq, uint scale) THROW
{
    if (length > 256) {
        stringstream ss;
        ss << "Invalid alphabet size parameter: " << scale << " (must be less than or eqaul to 256)";
        throw IllegalArgumentException(ss.str());
    }

    if ((scale < 1 << 8) || (scale > 1 << 16)) {
        stringstream ss;
        ss << "Invalid scale parameter: " << scale << " (must be in [256..65536])";
        throw IllegalArgumentException(ss.str());
    }

    if ((length == 0) || (totalFreq == 0))
        return 0;

    // Number of present symbols
    int alphabetSize = 0;

    // shortcut
    if (totalFreq == scale) {
        for (int i = 0; i < 256; i++) {
            if (freqs[i] != 0)
                alphabet[alphabetSize++] = i;
        }

        return alphabetSize;
    }

    uint sumScaledFreq = 0;
    uint sumFreq = 0;
    uint freqMax = 0;
    int idxMax = -1;

    // Scale frequencies by stretching distribution over complete range
    for (int i = 0; (i < length) && (sumFreq < totalFreq); i++) {
        alphabet[i] = 0;
        _errors[i] = 0;
        const uint f = freqs[i];

        if (f == 0)
            continue;

        if (f > freqMax) {
            freqMax = f;
            idxMax = i;
        }

        sumFreq += f;
        int64 sf = (int64)f * scale;
        uint scaledFreq;

        if (sf <= (int64)totalFreq) {
            // Quantum of frequency
            scaledFreq = 1;
        }
        else {
            // Find best frequency rounding value
            scaledFreq = (uint)(sf / totalFreq);
            int64 errCeiling = ((scaledFreq + 1) * (int64)totalFreq) - sf;
            int64 errFloor = sf - (scaledFreq * (int64)totalFreq);

            if (errCeiling < errFloor) {
                scaledFreq++;
                _errors[i] = (int)errCeiling;
            }
            else {
                _errors[i] = (int)errFloor;
            }
        }

        alphabet[alphabetSize++] = i;
        sumScaledFreq += scaledFreq;
        freqs[i] = scaledFreq;
    }

    if (alphabetSize == 0)
        return 0;

    if (alphabetSize == 1) {
        freqs[alphabet[0]] = scale;
        return 1;
    }

    if (sumScaledFreq != scale) {
        if (freqs[idxMax] > sumScaledFreq - scale) {
            // Fast path: just adjust the max frequency
            freqs[idxMax] += (scale - sumScaledFreq);
        }
        else {
            // Slow path: spread error across frequencies
            const int inc = (sumScaledFreq > scale) ? -1 : 1;
            vector<FreqSortData*> queue;

            // Create sorted queue of present symbols (except those with 'quantum frequency')
            for (int i = 0; i < alphabetSize; i++) {
                if ((_errors[alphabet[i]] > 0) && ((int)freqs[alphabet[i]] != -inc)) {
                    queue.push_back(new FreqSortData(_errors, freqs, alphabet[i]));
                }
            }

            make_heap(queue.begin(), queue.end(), FreqDataComparator());

            while ((sumScaledFreq != scale) && (queue.size() > 0)) {
                // Remove symbol with highest error
                pop_heap(queue.begin(), queue.end(), FreqDataComparator());
                FreqSortData* fsd = queue.back();
                queue.pop_back();

                // Do not zero out any frequency
                if ((int)freqs[fsd->_symbol] == -inc) {
                    delete fsd;
                    continue;
                }

                // Distort frequency and error
                freqs[fsd->_symbol] += inc;
                _errors[fsd->_symbol] -= scale;
                sumScaledFreq += inc;
                queue.push_back(fsd);
                push_heap(queue.begin(), queue.end(), FreqDataComparator());
            }

            while (queue.size() > 0) {
                FreqSortData* fsd = queue.back();
                queue.pop_back();
                delete fsd;
            }
        }
    }

    return alphabetSize;
}
<commit_msg>Fix incorrect stretching of frequencies in C++ implementation.<commit_after>/*
Copyright 2011-2017 Frederic Langlet
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
you may obtain a copy of the License at

                http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#include <algorithm>
#include <vector>
#include <sstream>
#include "../IllegalArgumentException.hpp"
#include "EntropyUtils.hpp"

using namespace kanzi;

class FreqSortData {
public:
    uint _symbol;
    int* _errors;
    uint* _frequencies;

    FreqSortData(int errors[], uint frequencies[], int symbol)
    {
        _errors = errors;
        _frequencies = frequencies;
        _symbol = symbol;
    }
};

struct FreqDataComparator {
    bool operator()(FreqSortData* fd1, FreqSortData* fd2) const
    {
        int res = fd1->_errors[fd1->_symbol] - fd2->_errors[fd2->_symbol];

        // Increasing frequency
        if (res == 0) {
            res = fd1->_frequencies[fd1->_symbol] - fd2->_frequencies[fd2->_symbol];

            // Increasing symbol
            if (res == 0)
                return fd1->_symbol < fd2->_symbol;
        }

        return res < 0;
    }
};

// alphabet must be sorted in increasing order
// length = alphabet array length
int EntropyUtils::encodeAlphabet(OutputBitStream& obs, uint alphabet[], int length, int count)
{
    // Alphabet length must be a power of 2
    if ((length & (length - 1)) != 0)
        return -1;

    if (count > length)
        return -1;

    // First, push alphabet encoding mode
    if ((length > 0) && (count == length)) {
        // Fuint64 alphabet
        obs.writeBit(FULL_ALPHABET);

        if (count == 256)
            obs.writeBit(ALPHABET_256); // shortcut
        else {
            int log = 1;

            while (1 << log <= count)
                log++;

            // Write alphabet size
            obs.writeBit(ALPHABET_NOT_256);
            obs.writeBits(log - 1, 5);
            obs.writeBits(count, log);
        }

        return count;
    }

    obs.writeBit(PARTIAL_ALPHABET);

    if ((length == 256) && (count >= 32) && (count <= 224)) {
        // Regular alphabet of symbols less than 256
        obs.writeBit(BIT_ENCODED_ALPHABET_256);
		uint64 masks[4] = { 0, 0, 0, 0 };

		for (int i = 0; i < count; i++)
			masks[alphabet[i] >> 6] |= ((uint64(1)) << (alphabet[i] & 63));

        for (int i = 0; i < 4; i++)
            obs.writeBits(masks[i], 64);

        return count;
    }

    obs.writeBit(DELTA_ENCODED_ALPHABET);
    int* diffs = new int[count];

    if (length - count < count) {
        // Encode all missing symbols
        count = length - count;
        int log = 1;

        while (1 << log <= count)
            log++;

        // Write length
        obs.writeBits(log - 1, 4);
        obs.writeBits(count, log);

        if (count == 0)
            return 0;

        obs.writeBit(ABSENT_SYMBOLS_MASK);
        log = 1;

        while (1 << log <= length)
            log++;

        // Write log(alphabet size)
        obs.writeBits(log - 1, 5);
        uint symbol = 0;
        int previous = 0;

        // Create deltas of missing symbols
        for (int n = 0, i = 0; n < count;) {
            if (symbol == alphabet[i]) {
                if (i < length - 1 - count)
                    i++;

                symbol++;
                continue;
            }

            diffs[n] = symbol - previous;
            symbol++;
            previous = symbol;
            n++;
        }
    }
    else {
        // Encode all present symbols
        int log = 1;

        while (1 << log <= count)
            log++;

        // Write length
        obs.writeBits(log - 1, 4);
        obs.writeBits(count, log);

        if (count == 0)
            return 0;

        obs.writeBit(PRESENT_SYMBOLS_MASK);
        int previous = 0;

        // Create deltas of present symbols
        for (int i = 0; i < count; i++) {
            diffs[i] = alphabet[i] - previous;
            previous = alphabet[i] + 1;
        }
    }

    const int ckSize = (count <= 64) ? 8 : 16;

    // Encode all deltas by chunks
    for (int i = 0; i < count; i += ckSize) {
        int max = 0;

        // Find log(max(deltas)) for this chunk
        for (int j = i; (j < count) && (j < i + ckSize); j++) {
            if (max < diffs[j])
                max = diffs[j];
        }

        int log = 1;

        while (1 << log <= max)
            log++;

        obs.writeBits(log - 1, 4);

        // Write deltas for this chunk
        for (int j = i; (j < count) && (j < i + ckSize); j++)
            encodeSize(obs, log, diffs[j]);
    }

    delete[] diffs;
    return count;
}

void EntropyUtils::encodeSize(OutputBitStream& obs, int log, int val)
{
    obs.writeBits(val, log);
}

uint64 EntropyUtils::decodeSize(InputBitStream& ibs, int log)
{
    return ibs.readBits(log);
}

int EntropyUtils::decodeAlphabet(InputBitStream& ibs, uint alphabet[]) THROW
{
    // Read encoding mode from bitstream
    const int alphabetType = ibs.readBit();

    if (alphabetType == FULL_ALPHABET) {
        int alphabetSize;

        if (ibs.readBit() == ALPHABET_256)
            alphabetSize = 256;
        else {
            int log = 1 + int(ibs.readBits(5));
            alphabetSize = int(ibs.readBits(log));
        }

        if (alphabetSize > 256) {
            stringstream ss;
            ss << "Invalid bitstream: incorrect alphabet size: " << alphabetSize;
            throw BitStreamException(ss.str(), BitStreamException::INVALID_STREAM);
        }

        // Full alphabet
        for (int i = 0; i < alphabetSize; i++)
            alphabet[i] = i;

        return alphabetSize;
    }

    int count = 0;
    const int mode = ibs.readBit();

    if (mode == BIT_ENCODED_ALPHABET_256) {
        // Decode presence flags
        for (int i = 0; i < 256; i += 64) {
            const uint64 val = ibs.readBits(64);

            for (int j = 0; j < 64; j++) {
                if ((val & ((uint64(1)) << j)) != 0) {
                    alphabet[count] = i + j;
                    count++;
                }
            }
        }

        return count;
    }

    // DELTA_ENCODED_ALPHABET
    int log = 1 + int(ibs.readBits(4));
    count = int(ibs.readBits(log));

    if (count == 0)
        return 0;

    const int ckSize = (count <= 64) ? 8 : 16;
    int n = 0;
    int symbol = 0;

    if (ibs.readBit() == ABSENT_SYMBOLS_MASK) {
        int alphabetSize = 1 << int(ibs.readBits(5));

        // Read missing symbols
        for (int i = 0; i < count; i += ckSize) {
            log = 1 + int(ibs.readBits(4));

            // Read deltas for this chunk
            for (int j = i; (j < count) && (j < i + ckSize); j++) {
                const int next = symbol + int(decodeSize(ibs, log));

                while ((symbol < next) && (n < alphabetSize)) {
                    alphabet[n] = symbol++;
                    n++;
                }

                symbol++;
            }
        }

        count = alphabetSize - count;

        while (n < count)
            alphabet[n++] = symbol++;
    }
    else {
        // Read present symbols
        for (int i = 0; i < count; i += ckSize) {
            log = 1 + int(ibs.readBits(4));

            // Read deltas for this chunk
            for (int j = i; (j < count) && (j < i + ckSize); j++) {
                symbol += int(decodeSize(ibs, log));
                alphabet[j] = symbol;
                symbol++;
            }
        }
    }

    return count;
}

// Returns the size of the alphabet
// length is the length of the alphabet array
// 'totalFreq 'is the sum of frequencies.
// 'scale' is the target new total of frequencies
// The alphabet and freqs parameters are updated
int EntropyUtils::normalizeFrequencies(uint freqs[], uint alphabet[], int length, uint totalFreq, uint scale) THROW
{
    if (length > 256) {
        stringstream ss;
        ss << "Invalid alphabet size parameter: " << scale << " (must be less than or eqaul to 256)";
        throw IllegalArgumentException(ss.str());
    }

    if ((scale < 1 << 8) || (scale > 1 << 16)) {
        stringstream ss;
        ss << "Invalid scale parameter: " << scale << " (must be in [256..65536])";
        throw IllegalArgumentException(ss.str());
    }

    if ((length == 0) || (totalFreq == 0))
        return 0;

    // Number of present symbols
    int alphabetSize = 0;

    // shortcut
    if (totalFreq == scale) {
        for (int i = 0; i < 256; i++) {
            if (freqs[i] != 0)
                alphabet[alphabetSize++] = i;
        }

        return alphabetSize;
    }

    uint sumScaledFreq = 0;
    uint sumFreq = 0;
    uint freqMax = 0;
    int idxMax = -1;

    // Scale frequencies by stretching distribution over complete range
    for (int i = 0; (i < length) && (sumFreq < totalFreq); i++) {
        alphabet[i] = 0;
        _errors[i] = 0;
        const uint f = freqs[i];

        if (f == 0)
            continue;

        if (f > freqMax) {
            freqMax = f;
            idxMax = i;
        }

        sumFreq += f;
        int64 sf = int64(f) * scale;
        uint scaledFreq;

        if (sf <= int64(totalFreq)) {
            // Quantum of frequency
            scaledFreq = 1;
        }
        else {
            // Find best frequency rounding value
            scaledFreq = uint(sf / totalFreq);
            int64 errCeiling = (scaledFreq + 1) * int64(totalFreq) - sf;
            int64 errFloor = sf - scaledFreq * int64(totalFreq);

            if (errCeiling < errFloor) {
                scaledFreq++;
                _errors[i] = int(errCeiling);
            }
            else {
                _errors[i] = int(errFloor);
            }
        }

        alphabet[alphabetSize++] = i;
        sumScaledFreq += scaledFreq;
        freqs[i] = scaledFreq;
    }

    if (alphabetSize == 0)
        return 0;

    if (alphabetSize == 1) {
        freqs[alphabet[0]] = scale;
        return 1;
    }

    if (sumScaledFreq != scale) {
        if (int(freqs[idxMax]) > int(sumScaledFreq - scale)) {
            // Fast path: just adjust the max frequency
            freqs[idxMax] += int(scale - sumScaledFreq);
        }
        else {
            // Slow path: spread error across frequencies
            const int inc = (sumScaledFreq > scale) ? -1 : 1;
            vector<FreqSortData*> queue;

            // Create sorted queue of present symbols (except those with 'quantum frequency')
            for (int i = 0; i < alphabetSize; i++) {
                if ((_errors[alphabet[i]] > 0) && (int(freqs[alphabet[i]]) != -inc)) {
                    queue.push_back(new FreqSortData(_errors, freqs, alphabet[i]));
                }
            }

            make_heap(queue.begin(), queue.end(), FreqDataComparator());

            while ((sumScaledFreq != scale) && (queue.size() > 0)) {
                // Remove symbol with highest error
                pop_heap(queue.begin(), queue.end(), FreqDataComparator());
                FreqSortData* fsd = queue.back();
                queue.pop_back();

                // Do not zero out any frequency
                if (int(freqs[fsd->_symbol]) == -inc) {
                    delete fsd;
                    continue;
                }

                // Distort frequency and error
                freqs[fsd->_symbol] += inc;
                _errors[fsd->_symbol] -= scale;
                sumScaledFreq += inc;
                queue.push_back(fsd);
                push_heap(queue.begin(), queue.end(), FreqDataComparator());
            }

            while (queue.size() > 0) {
                FreqSortData* fsd = queue.back();
                queue.pop_back();
                delete fsd;
            }
        }
    }

    return alphabetSize;
}
<|endoftext|>


<commit_before>/*
OFX RGBLut plugin, a plugin that illustrates the use of the OFX Support library.

Copyright (C) 2013 INRIA
Author Frederic Devernay frederic.devernay@inria.fr

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

  Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

  Redistributions in binary form must reproduce the above copyright notice, this
  list of conditions and the following disclaimer in the documentation and/or
  other materials provided with the distribution.

  Neither the name of the {organization} nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#ifdef _WINDOWS
#include <windows.h>
#endif

#ifdef __APPLE__
#include <OpenGL/gl.h>
#else
#include <GL/gl.h>
#endif

#include <stdio.h>
#include "ofxsImageEffect.h"
#include "ofxsMultiThread.h"

#include "ofxsProcessing.H"

class RGBLutBase : public OFX::ImageProcessor {
protected :
  OFX::Image *_srcImg;
public :
  RGBLutBase(OFX::ImageEffect &instance): OFX::ImageProcessor(instance), _srcImg(0)
  {
  }
  void setSrcImg(OFX::Image *v) {_srcImg = v;}
};

template <class PIX, int nComponents, int max>
class ImageRGBLutProcessor : public RGBLutBase
{
protected:
    PIX _lookupTable[3][max];
public :
  ImageRGBLutProcessor(OFX::ImageEffect &instance, const OFX::RenderArguments &args)
    : RGBLutBase(instance) {
        // build the LUT
        OFX::ParametricParam  *lookupTable = instance.fetchParametricParam("lookupTable");
        for(int component = 0; component < 3; ++component) {
            for(int position = 0; position < max; ++position) {
                // position to evaluate the param at
                float parametricPos = float(position)/(max-1);

                // evaluate the parametric param
                double value = lookupTable->getValue(component, args.time, parametricPos);

                // set that in the lut
                _lookupTable[component][position] = std::max(PIX(0),std::min(PIX(value*(max-1)+0.5), PIX(max-1)));
            }
        }
  }
  void multiThreadProcessImages(OfxRectI procWindow)
  {
    for(int y = procWindow.y1; y < procWindow.y2; y++)
    {
      if(_effect.abort())
        break;
      PIX *dstPix = (PIX *) _dstImg->getPixelAddress(procWindow.x1, y);
      for(int x = procWindow.x1; x < procWindow.x2; x++)
      {
        PIX *srcPix = (PIX *)  (_srcImg ? _srcImg->getPixelAddress(x, y) : 0);
        if(srcPix)
        {
          for(int c = 0; c < nComponents; c++)
            dstPix[c] = max - srcPix[c];
        }
        else
        {
          for(int c = 0; c < nComponents; c++)
            dstPix[c] = 0;
        }
        dstPix += nComponents;
      }
    }
  }
};

template <int nComponents, int max>
class ImageRGBLutProcessorFloat : public RGBLutBase
{
protected:
    typedef float PIX;
    PIX _lookupTable[3][max];
public :
  ImageRGBLutProcessorFloat(OFX::ImageEffect &instance, const OFX::RenderArguments &args)
    : RGBLutBase(instance)
  {
    // build the LUT
    OFX::ParametricParam  *lookupTable = instance.fetchParametricParam("lookupTable");
    for(int component = 0; component < 3; ++component) {
      for(int position = 0; position < max; ++position) {
        // position to evaluate the param at
        double parametricPos = float(position)/(max-1);

        // evaluate the parametric param
        double value = lookupTable->getValue(component, args.time, parametricPos);
        //value = value * (max-1);
        //value = clamp(value, 0, max-1);

        // set that in the lut
        _lookupTable[component][position] = (PIX)value;
      }
    }
  }
  float interpolate(int component, float value) {
      if (component == 3) { // alpha
          return value;
      }
      if (value < 0.) {
          return _lookupTable[component][0];
      } else if (value >= 1.) {
          return _lookupTable[component][max-1];
      } else {
          int i = (int)(value * (max-1));
          assert(i < max-1);
          float alpha = value - (float)i / (max-1);
          return _lookupTable[component][i] * (1.-alpha) + _lookupTable[component][i] * alpha;
      }
  }
  void multiThreadProcessImages(OfxRectI procWindow)
  {
    for(int y = procWindow.y1; y < procWindow.y2; y++)
    {
      if(_effect.abort())
        break;
      PIX *dstPix = (PIX *) _dstImg->getPixelAddress(procWindow.x1, y);
      for(int x = procWindow.x1; x < procWindow.x2; x++)
      {
        PIX *srcPix = (PIX *)  (_srcImg ? _srcImg->getPixelAddress(x, y) : 0);
        if(srcPix)
        {
          for(int c = 0; c < nComponents; c++)
            dstPix[c] = 1. - srcPix[c];
        }
        else
        {
          for(int c = 0; c < nComponents; c++)
            dstPix[c] = 0;
        }
        dstPix += nComponents;
      }
    }
  }
};

template<class TypeCarrier, int kComponents, int kMax>
class Analyser
{
public:
  Analyser(OFX::Clip* srcClip, OFX::DoubleParam* dbl)
  {
    OfxRangeD range = srcClip->getFrameRange();
    for(double d = range.min; d< range.max; ++d)
    {
      std::auto_ptr<OFX::Image> src(srcClip->fetchImage(d));
      dbl->setValueAtTime(d, d);
    }
  }
};

////////////////////////////////////////////////////////////////////////////////
/** @brief The plugin that does our work */
class RGBLutPlugin : public OFX::ImageEffect
{
protected :
  OFX::Clip *dstClip_;
  OFX::Clip *srcClip_;

public :
  RGBLutPlugin(OfxImageEffectHandle handle)
    : ImageEffect(handle)
    , dstClip_(0)
    , srcClip_(0)
  {
    dstClip_ = fetchClip(kOfxImageEffectOutputClipName);
    srcClip_ = fetchClip(kOfxImageEffectSimpleSourceClipName);
  }

  virtual void render(const OFX::RenderArguments &args);
  void setupAndProcess(RGBLutBase &, const OFX::RenderArguments &args);
};

void RGBLutPlugin::setupAndProcess(RGBLutBase &processor, const OFX::RenderArguments &args)
{
  std::auto_ptr<OFX::Image> dst(dstClip_->fetchImage(args.time));
  OFX::BitDepthEnum dstBitDepth       = dst->getPixelDepth();
  OFX::PixelComponentEnum dstComponents  = dst->getPixelComponents();
  std::auto_ptr<OFX::Image> src(srcClip_->fetchImage(args.time));

  if(src.get())
  {
    OFX::BitDepthEnum    srcBitDepth      = src->getPixelDepth();
    OFX::PixelComponentEnum srcComponents = src->getPixelComponents();

    // see if they have the same depths and bytes and all
    if(srcBitDepth != dstBitDepth || srcComponents != dstComponents)
      throw int(1); // HACK!! need to throw an sensible exception here!
  }

  processor.setDstImg(dst.get());
  processor.setSrcImg(src.get());
  processor.setRenderWindow(args.renderWindow);
  processor.process();
}

void RGBLutPlugin::render(const OFX::RenderArguments &args)
{
  OFX::BitDepthEnum       dstBitDepth    = dstClip_->getPixelDepth();
  OFX::PixelComponentEnum dstComponents  = dstClip_->getPixelComponents();

  if(dstComponents == OFX::ePixelComponentRGBA)
  {
    switch(dstBitDepth)
    {
    case OFX::eBitDepthUByte :
      {
        ImageRGBLutProcessor<unsigned char, 4, 255> fred(*this, args);
        setupAndProcess(fred, args);
      }
      break;

    case OFX::eBitDepthUShort :
      {
        ImageRGBLutProcessor<unsigned short, 4, 65535> fred(*this, args);
        setupAndProcess(fred, args);
      }
      break;

    case OFX::eBitDepthFloat :
      {
        ImageRGBLutProcessorFloat<4,100> fred(*this, args);
        setupAndProcess(fred, args);
      }
      break;
    }
  }
  else {
    switch(dstBitDepth)
    {
    case OFX::eBitDepthUByte :
      {
        ImageRGBLutProcessor<unsigned char, 1, 255> fred(*this, args);
        setupAndProcess(fred, args);
      }
      break;

    case OFX::eBitDepthUShort :
      {
        ImageRGBLutProcessor<unsigned short, 1, 65535> fred(*this, args);
        setupAndProcess(fred, args);
      }
      break;

    case OFX::eBitDepthFloat :
      {
        ImageRGBLutProcessorFloat<1,100> fred(*this, args);
        setupAndProcess(fred, args);
      }
      break;
    }
  }
}

mDeclarePluginFactory(RGBLutPluginFactory, {}, {});

using namespace OFX;
void RGBLutPluginFactory::describe(OFX::ImageEffectDescriptor &desc)
{
  desc.setLabels("RGBLut", "RGBLut", "RGBLut");
  desc.setPluginGrouping("OFX");
  desc.addSupportedContext(eContextFilter);
  desc.addSupportedBitDepth(eBitDepthUByte);
  desc.addSupportedBitDepth(eBitDepthUShort);
  desc.addSupportedBitDepth(eBitDepthFloat);

  desc.setSingleInstance(false);
  desc.setHostFrameThreading(false);
  desc.setSupportsMultiResolution(true);
  desc.setSupportsTiles(true);
  desc.setTemporalClipAccess(false);
  desc.setRenderTwiceAlways(false);
  desc.setSupportsMultipleClipPARs(false);

  if (!OFX::getImageEffectHostDescription()->supportsParametricParameter) {
	throwHostMissingSuiteException(kOfxParametricParameterSuite);
  }
}

void RGBLutPluginFactory::describeInContext(OFX::ImageEffectDescriptor &desc, OFX::ContextEnum context)
{
  ClipDescriptor *srcClip = desc.defineClip(kOfxImageEffectSimpleSourceClipName);
  srcClip->addSupportedComponent(ePixelComponentRGBA);
  srcClip->addSupportedComponent(ePixelComponentAlpha);
  srcClip->setTemporalClipAccess(false);
  srcClip->setSupportsTiles(true);
  srcClip->setIsMask(false);

  ClipDescriptor *dstClip = desc.defineClip(kOfxImageEffectOutputClipName);
  dstClip->addSupportedComponent(ePixelComponentRGBA);
  dstClip->addSupportedComponent(ePixelComponentAlpha);
  dstClip->setSupportsTiles(true);

  if (OFX::getImageEffectHostDescription()->supportsParametricParameter) {
    // define it
    OFX::ParametricParamDescriptor* lookupTable = desc.defineParametricParam("lookupTable");
    assert(lookupTable);
    lookupTable->setLabel("Lookup Table");
    lookupTable->setHint("Colour lookup table");
    lookupTable->setScriptName("lookupTable");

    // define it as three dimensional
    lookupTable->setDimension(3);

    // label our dimensions are r/g/b
    lookupTable->setDimensionLabel("red", 0);
    lookupTable->setDimensionLabel("green", 1);
    lookupTable->setDimensionLabel("blue", 2);

    // set the UI colour for each dimension
    const OfxRGBColourD red   = {1,0,0};		//set red color to red curve
    const OfxRGBColourD green = {0,1,0};		//set green color to green curve
    const OfxRGBColourD blue  = {0,0,1};		//set blue color to blue curve
    lookupTable->setUIColour( 0, red );
    lookupTable->setUIColour( 1, green );
    lookupTable->setUIColour( 2, blue );

    // set the min/max parametric range to 0..1
    lookupTable->setRange(0.0, 1.0);

    // set a default curve, this example sets an invert
    //OfxParamHandle descriptor;
    //gParamHost->paramGetHandle(paramSet, "lookupTable", &descriptor, NULL);
    for(int component = 0; component < 3; ++component) {
      // add a control point at 0, value is 1
      lookupTable->addControlPoint(component, // curve to set
                                   0.0,   // time, ignored in this case, as we are not adding a ket
                                   0.0,   // parametric position, zero
                                   1.0,   // value to be, 1
                                   false);   // don't add a key
      // add a control point at 1, value is 0
      lookupTable->addControlPoint(component, 0.0, 1.0, 0.0, false);
    }
  }
}

OFX::ImageEffect* RGBLutPluginFactory::createInstance(OfxImageEffectHandle handle, OFX::ContextEnum context)
{
  return new RGBLutPlugin(handle);
}

namespace OFX
{
  namespace Plugin
  {
    void getPluginIDs(OFX::PluginFactoryArray &ids)
    {
      static RGBLutPluginFactory p("net.sf.openfx:RGBLutPlugin", 1, 0);
      ids.push_back(&p);
    }
  }
}
<commit_msg>first complete version<commit_after>/*
OFX RGBLut plugin, a plugin that illustrates the use of the OFX Support library.

Copyright (C) 2013 INRIA
Author Frederic Devernay frederic.devernay@inria.fr

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

  Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

  Redistributions in binary form must reproduce the above copyright notice, this
  list of conditions and the following disclaimer in the documentation and/or
  other materials provided with the distribution.

  Neither the name of the {organization} nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#ifdef _WINDOWS
#include <windows.h>
#endif

#ifdef __APPLE__
#include <OpenGL/gl.h>
#else
#include <GL/gl.h>
#endif

#include <stdio.h>
#include "ofxsImageEffect.h"
#include "ofxsMultiThread.h"

#include "ofxsProcessing.H"

class RGBLutBase : public OFX::ImageProcessor {
protected :
  OFX::Image *_srcImg;
public :
  RGBLutBase(OFX::ImageEffect &instance): OFX::ImageProcessor(instance), _srcImg(0)
  {
  }
  void setSrcImg(OFX::Image *v) {_srcImg = v;}
};

// template to do the RGBA processing for discrete types
template <class PIX, int nComponents, int nValues>
class ImageRGBLutProcessor : public RGBLutBase
{
protected:
    PIX _lookupTable[3][nValues];
public :
  // ctor
  ImageRGBLutProcessor(OFX::ImageEffect &instance, const OFX::RenderArguments &args)
    : RGBLutBase(instance)
  {
    // build the LUT
    OFX::ParametricParam  *lookupTable = instance.fetchParametricParam("lookupTable");
    for(int component = 0; component < 3; ++component) {
      for(PIX position = 0; position < nValues; ++position) {
        // position to evaluate the param at
        float parametricPos = float(position)/(nValues-1);

        // evaluate the parametric param
        double value = lookupTable->getValue(component, args.time, parametricPos);

        // set that in the lut
        _lookupTable[component][position] = std::max(PIX(0),std::min(PIX(value*(nValues-1)+0.5), PIX(nValues-1)));
      }
    }
  }
  // and do some processing
  void multiThreadProcessImages(OfxRectI procWindow)
  {
    for(int y = procWindow.y1; y < procWindow.y2; y++)
    {
      if(_effect.abort())
        break;
      PIX *dstPix = (PIX *) _dstImg->getPixelAddress(procWindow.x1, y);
      for(int x = procWindow.x1; x < procWindow.x2; x++)
      {
        PIX *srcPix = (PIX *)  (_srcImg ? _srcImg->getPixelAddress(x, y) : 0);
        if(srcPix)
        {
          for(int c = 0; c < nComponents; c++) {
            assert(0 <= srcPix[c] && srcPix[c] < nValues);
            dstPix[c] = _lookupTable[c][srcPix[c]];
          }
        }
        else
        {
          // no src pixel here, be black and transparent
          for(int c = 0; c < nComponents; c++)
            dstPix[c] = 0;
        }
        // increment the dst pixel
        dstPix += nComponents;
      }
    }
  }
};

// template to do the RGBA processing for floating-point types
template <int nComponents, int nValues>
class ImageRGBLutProcessorFloat : public RGBLutBase
{
protected:
    typedef float PIX;
    PIX _lookupTable[3][nValues];
public :
  // ctor
  ImageRGBLutProcessorFloat(OFX::ImageEffect &instance, const OFX::RenderArguments &args)
    : RGBLutBase(instance)
  {
    // build the LUT
    OFX::ParametricParam  *lookupTable = instance.fetchParametricParam("lookupTable");
    for(int component = 0; component < 3; ++component) {
      for(int position = 0; position < nValues; ++position) {
        // position to evaluate the param at
        double parametricPos = float(position)/(nValues-1);

        // evaluate the parametric param
        double value = lookupTable->getValue(component, args.time, parametricPos);
        //value = value * (nValues-1);
        //value = clamp(value, 0, nValues-1);

        // set that in the lut
        _lookupTable[component][position] = (PIX)value;
      }
    }
  }
  // and do some processing
  void multiThreadProcessImages(OfxRectI procWindow)
  {
    for(int y = procWindow.y1; y < procWindow.y2; y++)
    {
      if(_effect.abort())
        break;
      PIX *dstPix = (PIX *) _dstImg->getPixelAddress(procWindow.x1, y);
      for(int x = procWindow.x1; x < procWindow.x2; x++)
      {
        PIX *srcPix = (PIX *)  (_srcImg ? _srcImg->getPixelAddress(x, y) : 0);
        if(srcPix)
        {
          for(int c = 0; c < nComponents; c++) {
            dstPix[c] = interpolate(c, srcPix[c]);
          }
        }
        else
        {
          // no src pixel here, be black and transparent
          for(int c = 0; c < nComponents; c++)
            dstPix[c] = 0;
        }
        // increment the dst pixel
        dstPix += nComponents;
      }
    }
  }
private:
  float interpolate(int component, float value) {
    if (component == 3) { // alpha
      return value;
    }
    if (value < 0.) {
      return _lookupTable[component][0];
    } else if (value >= 1.) {
      return _lookupTable[component][nValues-1];
    } else {
      int i = (int)(value * (nValues-1));
      assert(i < nValues-1);
      float alpha = value - (float)i / (nValues-1);
      return _lookupTable[component][i] * (1.-alpha) + _lookupTable[component][i] * alpha;
    }
  }
};

////////////////////////////////////////////////////////////////////////////////
/** @brief The plugin that does our work */
class RGBLutPlugin : public OFX::ImageEffect
{
protected :
  OFX::Clip *dstClip_;
  OFX::Clip *srcClip_;

public :
  RGBLutPlugin(OfxImageEffectHandle handle)
    : ImageEffect(handle)
    , dstClip_(0)
    , srcClip_(0)
  {
    dstClip_ = fetchClip(kOfxImageEffectOutputClipName);
    srcClip_ = fetchClip(kOfxImageEffectSimpleSourceClipName);
  }

  virtual void render(const OFX::RenderArguments &args);
  void setupAndProcess(RGBLutBase &, const OFX::RenderArguments &args);
};

void RGBLutPlugin::setupAndProcess(RGBLutBase &processor, const OFX::RenderArguments &args)
{
  std::auto_ptr<OFX::Image> dst(dstClip_->fetchImage(args.time));
  OFX::BitDepthEnum dstBitDepth       = dst->getPixelDepth();
  OFX::PixelComponentEnum dstComponents  = dst->getPixelComponents();
  std::auto_ptr<OFX::Image> src(srcClip_->fetchImage(args.time));

  if(src.get())
  {
    OFX::BitDepthEnum    srcBitDepth      = src->getPixelDepth();
    OFX::PixelComponentEnum srcComponents = src->getPixelComponents();

    // see if they have the same depths and bytes and all
    if(srcBitDepth != dstBitDepth || srcComponents != dstComponents)
      throw int(1); // HACK!! need to throw an sensible exception here!
  }

  processor.setDstImg(dst.get());
  processor.setSrcImg(src.get());
  processor.setRenderWindow(args.renderWindow);
  processor.process();
}

void RGBLutPlugin::render(const OFX::RenderArguments &args)
{
  OFX::BitDepthEnum       dstBitDepth    = dstClip_->getPixelDepth();
  OFX::PixelComponentEnum dstComponents  = dstClip_->getPixelComponents();

  if(dstComponents == OFX::ePixelComponentRGBA)
  {
    switch(dstBitDepth)
    {
    case OFX::eBitDepthUByte :
      {
        ImageRGBLutProcessor<unsigned char, 4, 255> fred(*this, args);
        setupAndProcess(fred, args);
      }
      break;

    case OFX::eBitDepthUShort :
      {
        ImageRGBLutProcessor<unsigned short, 4, 65535> fred(*this, args);
        setupAndProcess(fred, args);
      }
      break;

    case OFX::eBitDepthFloat :
      {
        ImageRGBLutProcessorFloat<4,100> fred(*this, args);
        setupAndProcess(fred, args);
      }
      break;
    }
  }
  else {
    switch(dstBitDepth)
    {
    case OFX::eBitDepthUByte :
      {
        ImageRGBLutProcessor<unsigned char, 1, 255> fred(*this, args);
        setupAndProcess(fred, args);
      }
      break;

    case OFX::eBitDepthUShort :
      {
        ImageRGBLutProcessor<unsigned short, 1, 65535> fred(*this, args);
        setupAndProcess(fred, args);
      }
      break;

    case OFX::eBitDepthFloat :
      {
        ImageRGBLutProcessorFloat<1,100> fred(*this, args);
        setupAndProcess(fred, args);
      }
      break;
    }
  }
}

mDeclarePluginFactory(RGBLutPluginFactory, {}, {});

using namespace OFX;
void RGBLutPluginFactory::describe(OFX::ImageEffectDescriptor &desc)
{
  desc.setLabels("RGBLut", "RGBLut", "RGBLut");
  desc.setPluginGrouping("OFX");
  desc.addSupportedContext(eContextFilter);
  desc.addSupportedBitDepth(eBitDepthUByte);
  desc.addSupportedBitDepth(eBitDepthUShort);
  desc.addSupportedBitDepth(eBitDepthFloat);

  desc.setSingleInstance(false);
  desc.setHostFrameThreading(false);
  desc.setSupportsMultiResolution(true);
  desc.setSupportsTiles(true);
  desc.setTemporalClipAccess(false);
  desc.setRenderTwiceAlways(false);
  desc.setSupportsMultipleClipPARs(false);

  if (!OFX::getImageEffectHostDescription()->supportsParametricParameter) {
	throwHostMissingSuiteException(kOfxParametricParameterSuite);
  }
}

void RGBLutPluginFactory::describeInContext(OFX::ImageEffectDescriptor &desc, OFX::ContextEnum context)
{
  ClipDescriptor *srcClip = desc.defineClip(kOfxImageEffectSimpleSourceClipName);
  srcClip->addSupportedComponent(ePixelComponentRGBA);
  srcClip->addSupportedComponent(ePixelComponentAlpha);
  srcClip->setTemporalClipAccess(false);
  srcClip->setSupportsTiles(true);
  srcClip->setIsMask(false);

  ClipDescriptor *dstClip = desc.defineClip(kOfxImageEffectOutputClipName);
  dstClip->addSupportedComponent(ePixelComponentRGBA);
  dstClip->addSupportedComponent(ePixelComponentAlpha);
  dstClip->setSupportsTiles(true);

  if (OFX::getImageEffectHostDescription()->supportsParametricParameter) {
    // define it
    OFX::ParametricParamDescriptor* lookupTable = desc.defineParametricParam("lookupTable");
    assert(lookupTable);
    lookupTable->setLabel("Lookup Table");
    lookupTable->setHint("Colour lookup table");
    lookupTable->setScriptName("lookupTable");

    // define it as three dimensional
    lookupTable->setDimension(3);

    // label our dimensions are r/g/b
    lookupTable->setDimensionLabel("red", 0);
    lookupTable->setDimensionLabel("green", 1);
    lookupTable->setDimensionLabel("blue", 2);

    // set the UI colour for each dimension
    const OfxRGBColourD red   = {1,0,0};		//set red color to red curve
    const OfxRGBColourD green = {0,1,0};		//set green color to green curve
    const OfxRGBColourD blue  = {0,0,1};		//set blue color to blue curve
    lookupTable->setUIColour( 0, red );
    lookupTable->setUIColour( 1, green );
    lookupTable->setUIColour( 2, blue );

    // set the min/max parametric range to 0..1
    lookupTable->setRange(0.0, 1.0);

    // set a default curve, this example sets an invert
    //OfxParamHandle descriptor;
    //gParamHost->paramGetHandle(paramSet, "lookupTable", &descriptor, NULL);
    for(int component = 0; component < 3; ++component) {
      // add a control point at 0, value is 1
      lookupTable->addControlPoint(component, // curve to set
                                   0.0,   // time, ignored in this case, as we are not adding a ket
                                   0.0,   // parametric position, zero
                                   1.0,   // value to be, 1
                                   false);   // don't add a key
      // add a control point at 1, value is 0
      lookupTable->addControlPoint(component, 0.0, 1.0, 0.0, false);
    }
  }
}

OFX::ImageEffect* RGBLutPluginFactory::createInstance(OfxImageEffectHandle handle, OFX::ContextEnum context)
{
  return new RGBLutPlugin(handle);
}

namespace OFX
{
  namespace Plugin
  {
    void getPluginIDs(OFX::PluginFactoryArray &ids)
    {
      static RGBLutPluginFactory p("net.sf.openfx:RGBLutPlugin", 1, 0);
      ids.push_back(&p);
    }
  }
}
<|endoftext|>


<commit_before>// This file is a part of the IncludeOS unikernel - www.includeos.org
//
// Copyright 2015 Oslo and Akershus University College of Applied Sciences
// and Alfred Bratterud
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <os>
#include <net/inet4>
#include <net/dhcp/dh4client.hpp>
#include <net/tcp/tcp.hpp>
#include <vector>
#include <info>
#include <timers>

using namespace net;
using namespace std::chrono; // For timers and MSL
tcp::Connection_ptr client;

/*
  TEST VARIABLES
*/
tcp::port_t
TEST1{8081}, TEST2{8082}, TEST3{8083}, TEST4{8084}, TEST5{8085};

using HostAddress = std::pair<std::string, tcp::port_t>;
HostAddress
TEST_ADDR_TIME{"india.colorado.edu", 13};

std::string
small, big, huge;

int
S{150}, B{1500}, H{150000};

std::string
TEST_STR {"Kappa!"};

// Default MSL is 30s. Timeout 2*MSL.
// To reduce test duration, lower MSL to 3s.
milliseconds MSL_TEST = 3s;

/*
  TEST: Release of resources/clean up.
*/
void FINISH_TEST() {
  INFO("TEST", "Started 2 x MSL + 100ms timeout.");
  Timers::oneshot(2 * MSL_TEST + 100ms,
  [] (Timers::id_t) {
      INFO("TEST", "Verify release of resources");
      CHECKSERT(Inet4::stack<0>().tcp().active_connections() == 0,
        "No (0) active connections");
      INFO("Buffers", "%u avail / %u total",
            Inet4::stack<0>().buffers_available(),
            Inet4::stack<0>().buffers_total());
      // unfortunately we can't know just how many buffers SHOULD be
      // available, because drivers take some buffers, but there should
      // be at least half the buffers left
      auto total = Inet4::stack<0>().buffers_total();
      CHECKSERT(Inet4::stack<0>().buffers_available() >= total / 2,
                "Free buffers (%u available)", total);
      printf("# TEST SUCCESS #\n");
    });
}

/*
  TEST: Outgoing Internet Connection
*/
void OUTGOING_TEST_INTERNET(const HostAddress& address) {
  auto port = address.second;
  // This needs correct setup to work
  INFO("TEST", "Outgoing Internet Connection (%s:%u)", address.first.c_str(), address.second);
  Inet4::stack<0>().resolve(address.first,
    [port](auto ip_address, const Error&) {
      CHECK(ip_address != 0, "Resolved host");

      if(ip_address != 0)
      {
        Inet4::stack<0>().tcp().connect({ip_address, port})
          ->on_connect([](tcp::Connection_ptr conn)
          {
            CHECKSERT(conn != nullptr, "Connected");
            conn->on_read(1024, [](tcp::buffer_t, size_t n) {
                CHECK(n > 0, "Received a response");
              });
          });
      }
    });
}

/*
  TEST: Outgoing Connection to Host
*/
void OUTGOING_TEST(Socket outgoing) {
  INFO("TEST", "Outgoing Connection (%s)", outgoing.to_string().c_str());
  Inet4::stack<0>().tcp().connect(outgoing, [](tcp::Connection_ptr conn)
  {
    CHECKSERT(conn != nullptr, "Connection successfully established.");
    conn->on_read(small.size(), [](tcp::buffer_t buffer, size_t n)
    {
      CHECKSERT(std::string((char*)buffer.get(), n) == small, "Received SMALL");
    });

    conn->write(small);

    conn->on_disconnect([](tcp::Connection_ptr conn, tcp::Connection::Disconnect) {
      CHECK(true, "Connection closed by server");
      CHECKSERT(conn->is_state({"CLOSE-WAIT"}), "State: CLOSE-WAIT");
      conn->close();
    })
    .on_close([]{ OUTGOING_TEST_INTERNET(TEST_ADDR_TIME); });
  });
}

// Used to send big data
struct Buffer {
  size_t written, read;
  char* data;
  const size_t size;

  Buffer(size_t length) :
    written(0), read(0), data(new char[length]), size(length) {}

  ~Buffer() { delete[] data; }

  std::string str() { return {data, size};}
};

void Service::start()
{
  for(int i = 0; i < S; i++) small += TEST_STR;

  big += "start-";
  for(int i = 0; i < B; i++) big += std::to_string(i) + "-";
  big += "-end";

  huge = "start-";
  for(int i = 0; i < H; i++) huge += TEST_STR;
  huge += "-end";

  auto& inet = Inet4::stack<0>(); // Inet4<VirtioNet>::stack<0>();
  inet.network_config(
    {  10,  0,  0, 44 },  // IP
    {  255,255,255, 0 },  // Netmask
    {  10,  0,  0,  1 },  // Gateway
    {   8,  8,  8,  8 }   // DNS
  );
  INFO("Buffers available", "%u", inet.buffers_available());

  auto& tcp = inet.tcp();
  // reduce test duration
  tcp.set_MSL(MSL_TEST);

  /*
    TEST: Send and receive small string.
  */
  INFO("TEST", "Listeners and connections allocation.");

  /*
    TEST: Nothing should be allocated.
  */
  CHECK(tcp.listening_ports() == 0, "No (0) open ports (listening connections)");
  CHECK(tcp.active_connections() == 0, "No (0) active connections");

  tcp.listen(TEST1).on_connect([](tcp::Connection_ptr conn) {
      INFO("Test 1", "SMALL string (%u)", small.size());
      conn->on_read(small.size(), [conn](tcp::buffer_t buffer, size_t n) {
          CHECKSERT(std::string((char*)buffer.get(), n) == small, "Received SMALL");
          INFO("Test 1", "Succeeded, TEST2");
          conn->close();
        });
      conn->write(small);
    });

  /*
    TEST: Server should be bound.
  */
  CHECK(tcp.listening_ports() == 1, "One (1) open port");

  /*
    TEST: Send and receive big string.
  */
  tcp.listen(TEST2).on_connect([](tcp::Connection_ptr conn) {
      INFO("Test 2", "BIG string (%u)", big.size());
      auto response = std::make_shared<std::string>();
      conn->on_read(big.size(),
      [response, conn] (tcp::buffer_t buffer, size_t n)
        {
          response->append(std::string{(char*)buffer.get(), n});
          if(response->size() == big.size()) {
            CHECKSERT((*response == big), "Received BIG");
            INFO("Test 2", "Succeeded, TEST3");
            conn->close();
          }
        });
      conn->write(big.data(), big.size());
    });

  /*
    TEST: Send and receive huge string.
  */
  tcp.listen(TEST3).on_connect([](tcp::Connection_ptr conn) {
      INFO("Test 3", "HUGE string (%u)", huge.size());
      auto temp = std::make_shared<Buffer>(huge.size());
      conn->on_read(16384, [temp, conn](tcp::buffer_t buffer, size_t n) {
          memcpy(temp->data + temp->written, buffer.get(), n);
          temp->written += n;
          // when all expected data is read
          if(temp->written == huge.size()) {
            bool OK = (temp->str() == huge);
            CHECKSERT(OK, "Received HUGE");
            INFO("Test 3", "Succeeded, TEST4");
          }
        });
      auto half = huge.size() / 2;
      conn->on_write([half](size_t n) {
        CHECKSERT(n == half, "Wrote half HUGE (%u bytes)", n);
      });
      conn->write(huge.data(), half);
      conn->write(huge.data()+half, half);
      conn->close();
    });

  /*
    TEST: More servers should be bound.
  */
  CHECK(tcp.listening_ports() == 3, "Three (3) open ports");

  /*
    TEST: Connection (Status etc.) and Active Close
  */
  tcp.listen(TEST4).on_connect([](tcp::Connection_ptr conn) {
      INFO("Test 4","Connection/TCP state");
      // There should be at least one connection.
      CHECKSERT(Inet4::stack<0>().tcp().active_connections() > 0, "There is (>0) open connection(s)");
      // Test if connected.
      CHECKSERT(conn->is_connected(), "Is connected");
      // Test if writable.
      CHECKSERT(conn->is_writable(), "Is writable");
      // Test if state is ESTABLISHED.
      CHECKSERT(conn->is_state({"ESTABLISHED"}), "State: ESTABLISHED");

      INFO("Test 4", "Active close");
      CHECKSERT(!conn->is_closing(), "Is NOT closing");
      // Setup on_disconnect event
      conn->on_disconnect([](auto conn, tcp::Connection::Disconnect) {
        CHECKSERT(conn->is_closing(), "Is closing");
        CHECKSERT(conn->is_state({"FIN-WAIT-2"}), "State: FIN-WAIT-2");
        Timers::oneshot(1s,
        [conn] (auto) {
            CHECKSERT(conn->is_state({"TIME-WAIT"}), "State: TIME-WAIT");
            INFO("Test 4", "Succeeded. Trigger TEST5");
            OUTGOING_TEST({Inet4::stack().gateway(), TEST5});
          });

        Timers::oneshot(5s, [] (Timers::id_t) { FINISH_TEST(); });
      });

      // Test for active close.
      conn->close();
      CHECKSERT(!conn->is_writable(), "Is NOT writable");
      CHECKSERT(conn->is_state({"FIN-WAIT-1"}), "State: FIN-WAIT-1");
    });

  printf ("IncludeOS TCP test\n");
  printf("TEST1 \n");

}
<commit_msg>test: Update TCP integration test<commit_after>// This file is a part of the IncludeOS unikernel - www.includeos.org
//
// Copyright 2015 Oslo and Akershus University College of Applied Sciences
// and Alfred Bratterud
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <os>
#include <net/inet4>
#include <net/dhcp/dh4client.hpp>
#include <net/tcp/tcp.hpp>
#include <vector>
#include <info>
#include <timers>

using namespace net;
using namespace std::chrono; // For timers and MSL
tcp::Connection_ptr client;

/*
  TEST VARIABLES
*/
tcp::port_t
TEST1{8081}, TEST2{8082}, TEST3{8083}, TEST4{8084}, TEST5{8085};

using HostAddress = std::pair<std::string, tcp::port_t>;
HostAddress
TEST_ADDR_TIME{"india.colorado.edu", 13};

std::string
small, big, huge;

int
S{150}, B{1500}, H{150000};

std::string
TEST_STR {"Kappa!"};

// Default MSL is 30s. Timeout 2*MSL.
// To reduce test duration, lower MSL to 3s.
milliseconds MSL_TEST = 3s;

/*
  TEST: Release of resources/clean up.
*/
void FINISH_TEST() {
  INFO("TEST", "Started 2 x MSL + 100ms timeout.");
  Timers::oneshot(2 * MSL_TEST + 100ms,
  [] (Timers::id_t) {
      INFO("TEST", "Verify release of resources");
      CHECKSERT(Inet4::stack<0>().tcp().active_connections() == 0,
        "No (0) active connections");
      INFO("Buffers", "%u avail / %u total",
            Inet4::stack<0>().buffers_available(),
            Inet4::stack<0>().buffers_total());
      // unfortunately we can't know just how many buffers SHOULD be
      // available, because drivers take some buffers, but there should
      // be at least half the buffers left
      auto total = Inet4::stack<0>().buffers_total();
      CHECKSERT(Inet4::stack<0>().buffers_available() >= total / 2,
                "Free buffers (%u available)", total);
      printf("# TEST SUCCESS #\n");
    });
}

/*
  TEST: Outgoing Internet Connection
*/
void OUTGOING_TEST_INTERNET(const HostAddress& address) {
  auto port = address.second;
  // This needs correct setup to work
  INFO("TEST", "Outgoing Internet Connection (%s:%u)", address.first.c_str(), address.second);
  Inet4::stack<0>().resolve(address.first,
    [port](auto ip_address, const Error&) {
      CHECK(ip_address != 0, "Resolved host");

      if(ip_address != 0)
      {
        Inet4::stack<0>().tcp().connect({ip_address, port})
          ->on_connect([](tcp::Connection_ptr conn)
          {
            CHECKSERT(conn != nullptr, "Connected");
            conn->on_read(1024,
              [](tcp::buffer_t buf) {
                CHECK(buf->size() > 0, "Received a response");
              });
          });
      }
    });
}

/*
  TEST: Outgoing Connection to Host
*/
void OUTGOING_TEST(Socket outgoing) {
  INFO("TEST", "Outgoing Connection (%s)", outgoing.to_string().c_str());
  Inet4::stack<0>().tcp().connect(outgoing, [](tcp::Connection_ptr conn)
  {
    CHECKSERT(conn != nullptr, "Connection successfully established.");
    conn->on_read(small.size(),
    [](tcp::buffer_t buffer)
    {
      const std::string str((char*)buffer->data(), buffer->size());
      CHECKSERT(str == small, "Received SMALL");
    });

    conn->write(small);

    conn->on_disconnect([](tcp::Connection_ptr conn, tcp::Connection::Disconnect) {
      CHECK(true, "Connection closed by server");
      CHECKSERT(conn->is_state({"CLOSE-WAIT"}), "State: CLOSE-WAIT");
      conn->close();
    })
    .on_close([]{ OUTGOING_TEST_INTERNET(TEST_ADDR_TIME); });
  });
}

// Used to send big data
struct Buffer {
  size_t written, read;
  char* data;
  const size_t size;

  Buffer(size_t length) :
    written(0), read(0), data(new char[length]), size(length) {}

  ~Buffer() { delete[] data; }

  std::string str() { return {data, size};}
};

void Service::start()
{
  for(int i = 0; i < S; i++) small += TEST_STR;

  big += "start-";
  for(int i = 0; i < B; i++) big += std::to_string(i) + "-";
  big += "-end";

  huge = "start-";
  for(int i = 0; i < H; i++) huge += TEST_STR;
  huge += "-end";

  auto& inet = Inet4::stack<0>(); // Inet4<VirtioNet>::stack<0>();
  inet.network_config(
    {  10,  0,  0, 44 },  // IP
    {  255,255,255, 0 },  // Netmask
    {  10,  0,  0,  1 },  // Gateway
    {   8,  8,  8,  8 }   // DNS
  );
  INFO("Buffers available", "%u", inet.buffers_available());

  auto& tcp = inet.tcp();
  // reduce test duration
  tcp.set_MSL(MSL_TEST);

  /*
    TEST: Send and receive small string.
  */
  INFO("TEST", "Listeners and connections allocation.");

  /*
    TEST: Nothing should be allocated.
  */
  CHECK(tcp.listening_ports() == 0, "No (0) open ports (listening connections)");
  CHECK(tcp.active_connections() == 0, "No (0) active connections");

  tcp.listen(TEST1).on_connect([](tcp::Connection_ptr conn) {
      INFO("Test 1", "SMALL string (%u)", small.size());
      conn->on_read(small.size(), [conn](tcp::buffer_t buffer) {
          CHECKSERT(std::string((char*)buffer->data(), buffer->size()) == small, "Received SMALL");
          INFO("Test 1", "Succeeded, TEST2");
          conn->close();
        });
      conn->write(small);
    });

  /*
    TEST: Server should be bound.
  */
  CHECK(tcp.listening_ports() == 1, "One (1) open port");

  /*
    TEST: Send and receive big string.
  */
  tcp.listen(TEST2).on_connect([](tcp::Connection_ptr conn) {
      INFO("Test 2", "BIG string (%u)", big.size());
      auto response = std::make_shared<std::string>();
      conn->on_read(big.size(),
      [response, conn] (tcp::buffer_t buffer)
        {
          response->append(std::string{(char*)buffer->data(), buffer->size()});
          if(response->size() == big.size()) {
            CHECKSERT((*response == big), "Received BIG");
            INFO("Test 2", "Succeeded, TEST3");
            conn->close();
          }
        });
      conn->write(big.data(), big.size());
    });

  /*
    TEST: Send and receive huge string.
  */
  tcp.listen(TEST3).on_connect([](tcp::Connection_ptr conn) {
      INFO("Test 3", "HUGE string (%u)", huge.size());
      auto temp = std::make_shared<Buffer>(huge.size());
      conn->on_read(16384,
        [temp, conn] (tcp::buffer_t buffer) {
          memcpy(temp->data + temp->written, buffer->data(), buffer->size());
          temp->written += buffer->size();
          // when all expected data is read
          if(temp->written == huge.size()) {
            bool OK = (temp->str() == huge);
            CHECKSERT(OK, "Received HUGE");
            INFO("Test 3", "Succeeded, TEST4");
          }
        });
      auto half = huge.size() / 2;
      conn->on_write([half](size_t n) {
        CHECKSERT(n == half, "Wrote half HUGE (%lu bytes)", n);
      });
      conn->write(huge.data(), half);
      conn->write(huge.data()+half, half);
      conn->close();
    });

  /*
    TEST: More servers should be bound.
  */
  CHECK(tcp.listening_ports() == 3, "Three (3) open ports");

  /*
    TEST: Connection (Status etc.) and Active Close
  */
  tcp.listen(TEST4).on_connect([](tcp::Connection_ptr conn) {
      INFO("Test 4","Connection/TCP state");
      // There should be at least one connection.
      CHECKSERT(Inet4::stack<0>().tcp().active_connections() > 0, "There is (>0) open connection(s)");
      // Test if connected.
      CHECKSERT(conn->is_connected(), "Is connected");
      // Test if writable.
      CHECKSERT(conn->is_writable(), "Is writable");
      // Test if state is ESTABLISHED.
      CHECKSERT(conn->is_state({"ESTABLISHED"}), "State: ESTABLISHED");

      INFO("Test 4", "Active close");
      CHECKSERT(!conn->is_closing(), "Is NOT closing");
      // Setup on_disconnect event
      conn->on_disconnect([](auto conn, tcp::Connection::Disconnect) {
        CHECKSERT(conn->is_closing(), "Is closing");
        CHECKSERT(conn->is_state({"FIN-WAIT-2"}), "State: FIN-WAIT-2");
        Timers::oneshot(1s,
        [conn] (auto) {
            CHECKSERT(conn->is_state({"TIME-WAIT"}), "State: TIME-WAIT");
            INFO("Test 4", "Succeeded. Trigger TEST5");
            OUTGOING_TEST({Inet4::stack().gateway(), TEST5});
          });

        Timers::oneshot(5s, [] (Timers::id_t) { FINISH_TEST(); });
      });

      // Test for active close.
      conn->close();
      CHECKSERT(!conn->is_writable(), "Is NOT writable");
      CHECKSERT(conn->is_state({"FIN-WAIT-1"}), "State: FIN-WAIT-1");
    });

  printf ("IncludeOS TCP test\n");
  printf("TEST1 \n");

}
<|endoftext|>


<commit_before>#include "common/stats/stats_impl.h"

#include "server/hot_restart_impl.h"

#include "test/mocks/api/mocks.h"
#include "test/mocks/server/mocks.h"

#include "gtest/gtest.h"

using testing::Invoke;
using testing::InvokeWithoutArgs;
using testing::Return;
using testing::WithArg;
using testing::_;

namespace Envoy {
namespace Server {

class HotRestartImplTest : public testing::Test {
public:
  void setup() {
    EXPECT_CALL(os_sys_calls_, shmUnlink(_));
    EXPECT_CALL(os_sys_calls_, shmOpen(_, _, _));
    EXPECT_CALL(os_sys_calls_, ftruncate(_, _)).WillOnce(WithArg<1>(Invoke([this](off_t size) {
      buffer_.resize(size);
      return 0;
    })));
    EXPECT_CALL(os_sys_calls_, mmap(_, _, _, _, _, _)).WillOnce(InvokeWithoutArgs([this]() {
      return buffer_.data();
    }));
    EXPECT_CALL(os_sys_calls_, bind(_, _, _));

    Stats::RawStatData::configureForTestsOnly(options_);

    // Test we match the correct stat with empty-slots before, after, or both.
    hot_restart_.reset(new HotRestartImpl(options_, os_sys_calls_));
    hot_restart_->drainParentListeners();
  }

  void TearDown() {
    // Configure it back so that later tests don't get the wonky values
    // used here
    NiceMock<MockOptions> default_options;
    Stats::RawStatData::configureForTestsOnly(default_options);
  }

  Api::MockOsSysCalls os_sys_calls_;
  NiceMock<MockOptions> options_;
  std::vector<uint8_t> buffer_;
  std::unique_ptr<HotRestartImpl> hot_restart_;
};

TEST_F(HotRestartImplTest, versionString) {
  setup();
  EXPECT_EQ(hot_restart_->version(),
            Envoy::Server::SharedMemory::version(options_.maxStats(),
                                                 options_.maxObjNameLength() +
                                                     Stats::RawStatData::maxStatSuffixLength()));
}

TEST_F(HotRestartImplTest, crossAlloc) {
  setup();

  Stats::RawStatData* stat1 = hot_restart_->alloc("stat1");
  Stats::RawStatData* stat2 = hot_restart_->alloc("stat2");
  Stats::RawStatData* stat3 = hot_restart_->alloc("stat3");
  Stats::RawStatData* stat4 = hot_restart_->alloc("stat4");
  Stats::RawStatData* stat5 = hot_restart_->alloc("stat5");
  hot_restart_->free(*stat2);
  hot_restart_->free(*stat4);
  stat2 = nullptr;
  stat4 = nullptr;

  EXPECT_CALL(options_, restartEpoch()).WillRepeatedly(Return(1));
  EXPECT_CALL(os_sys_calls_, shmOpen(_, _, _));
  EXPECT_CALL(os_sys_calls_, mmap(_, _, _, _, _, _)).WillOnce(Return(buffer_.data()));
  EXPECT_CALL(os_sys_calls_, bind(_, _, _));
  HotRestartImpl hot_restart2(options_, os_sys_calls_);
  Stats::RawStatData* stat1_prime = hot_restart2.alloc("stat1");
  Stats::RawStatData* stat3_prime = hot_restart2.alloc("stat3");
  Stats::RawStatData* stat5_prime = hot_restart2.alloc("stat5");
  EXPECT_EQ(stat1, stat1_prime);
  EXPECT_EQ(stat3, stat3_prime);
  EXPECT_EQ(stat5, stat5_prime);
}

TEST_F(HotRestartImplTest, allocFail) {
  EXPECT_CALL(options_, maxStats()).WillRepeatedly(Return(2));
  setup();

  Stats::RawStatData* s1 = hot_restart_->alloc("1");
  Stats::RawStatData* s2 = hot_restart_->alloc("2");
  Stats::RawStatData* s3 = hot_restart_->alloc("3");
  EXPECT_NE(s1, nullptr);
  EXPECT_NE(s2, nullptr);
  EXPECT_EQ(s3, nullptr);
}

// Because the shared memory is managed manually, make sure it meets
// basic requirements:
//   - Objects are correctly aligned so that std::atomic works properly
//   - Objects don't overlap
class HotRestartImplAlignmentTest : public HotRestartImplTest,
                                    public testing::WithParamInterface<uint64_t> {
public:
  HotRestartImplAlignmentTest() : name_len_(8 + GetParam()) {
    EXPECT_CALL(options_, maxStats()).WillRepeatedly(Return(num_stats_));
    EXPECT_CALL(options_, maxObjNameLength()).WillRepeatedly(Return(name_len_));
    setup();
    EXPECT_EQ(name_len_, Stats::RawStatData::maxObjNameLength());
  }

  static const uint64_t num_stats_ = 8;
  const uint64_t name_len_;
};

TEST_P(HotRestartImplAlignmentTest, objectAlignment) {

  std::set<Stats::RawStatData*> used;
  for (uint64_t i = 0; i < num_stats_; i++) {
    Stats::RawStatData* stat = hot_restart_->alloc(fmt::format("stat {}", i));
    EXPECT_TRUE((reinterpret_cast<uintptr_t>(stat) % alignof(decltype(*stat))) == 0);
    EXPECT_TRUE(used.find(stat) == used.end());
    used.insert(stat);
  }
}

TEST_P(HotRestartImplAlignmentTest, objectOverlap) {
  // Iterate through all stats forwards and backwards, writing to all fields, then read them back to
  // make sure that writing to an adjacent stat didn't overwrite
  struct TestStat {
    Stats::RawStatData* stat_;
    std::string name_;
    uint64_t index_;
  };
  std::vector<TestStat> stats;
  for (uint64_t i = 0; i < num_stats_; i++) {
    std::string name = fmt::format("{}zzzzzzzzzzzzzzzzzzzzzzzzzzzz", i)
                           .substr(0, Stats::RawStatData::maxObjNameLength());
    TestStat ts;
    ts.stat_ = hot_restart_->alloc(name);
    ts.name_ = ts.stat_->name_;
    ts.index_ = i;

    // If this isn't true then the hard coded part of the name isn't long enough to make the test
    // valid.
    EXPECT_EQ(ts.name_.size(), Stats::RawStatData::maxObjNameLength());

    stats.push_back(ts);
  }

  auto write = [](TestStat& ts) {
    ts.stat_->value_ = ts.index_;
    ts.stat_->pending_increment_ = ts.index_;
    ts.stat_->flags_ = ts.index_;
    ts.stat_->ref_count_ = ts.index_;
    ts.stat_->unused_ = ts.index_;
  };

  auto verify = [](TestStat& ts) {
    EXPECT_TRUE(ts.stat_->matches(ts.name_));
    EXPECT_EQ(ts.stat_->value_, ts.index_);
    EXPECT_EQ(ts.stat_->pending_increment_, ts.index_);
    EXPECT_EQ(ts.stat_->flags_, ts.index_);
    EXPECT_EQ(ts.stat_->ref_count_, ts.index_);
    EXPECT_EQ(ts.stat_->unused_, ts.index_);
  };

  for (TestStat& ts : stats) {
    write(ts);
  }

  for (TestStat& ts : stats) {
    verify(ts);
  }

  for (auto it = stats.rbegin(); it != stats.rend(); ++it) {
    write(*it);
  }

  for (auto it = stats.rbegin(); it != stats.rend(); ++it) {
    verify(*it);
  }
}

INSTANTIATE_TEST_CASE_P(HotRestartImplAlignmentTest, HotRestartImplAlignmentTest,
                        testing::Range(0UL, alignof(Stats::RawStatData) + 1));

} // namespace Server
} // namespace Envoy
<commit_msg>long string to fix hot_restart_test<commit_after>#include "common/stats/stats_impl.h"

#include "server/hot_restart_impl.h"

#include "test/mocks/api/mocks.h"
#include "test/mocks/server/mocks.h"

#include "gtest/gtest.h"

using testing::Invoke;
using testing::InvokeWithoutArgs;
using testing::Return;
using testing::WithArg;
using testing::_;

namespace Envoy {
namespace Server {

class HotRestartImplTest : public testing::Test {
public:
  void setup() {
    EXPECT_CALL(os_sys_calls_, shmUnlink(_));
    EXPECT_CALL(os_sys_calls_, shmOpen(_, _, _));
    EXPECT_CALL(os_sys_calls_, ftruncate(_, _)).WillOnce(WithArg<1>(Invoke([this](off_t size) {
      buffer_.resize(size);
      return 0;
    })));
    EXPECT_CALL(os_sys_calls_, mmap(_, _, _, _, _, _)).WillOnce(InvokeWithoutArgs([this]() {
      return buffer_.data();
    }));
    EXPECT_CALL(os_sys_calls_, bind(_, _, _));

    Stats::RawStatData::configureForTestsOnly(options_);

    // Test we match the correct stat with empty-slots before, after, or both.
    hot_restart_.reset(new HotRestartImpl(options_, os_sys_calls_));
    hot_restart_->drainParentListeners();
  }

  void TearDown() {
    // Configure it back so that later tests don't get the wonky values
    // used here
    NiceMock<MockOptions> default_options;
    Stats::RawStatData::configureForTestsOnly(default_options);
  }

  Api::MockOsSysCalls os_sys_calls_;
  NiceMock<MockOptions> options_;
  std::vector<uint8_t> buffer_;
  std::unique_ptr<HotRestartImpl> hot_restart_;
};

TEST_F(HotRestartImplTest, versionString) {
  setup();
  EXPECT_EQ(hot_restart_->version(),
            Envoy::Server::SharedMemory::version(options_.maxStats(),
                                                 options_.maxObjNameLength() +
                                                     Stats::RawStatData::maxStatSuffixLength()));
}

TEST_F(HotRestartImplTest, crossAlloc) {
  setup();

  Stats::RawStatData* stat1 = hot_restart_->alloc("stat1");
  Stats::RawStatData* stat2 = hot_restart_->alloc("stat2");
  Stats::RawStatData* stat3 = hot_restart_->alloc("stat3");
  Stats::RawStatData* stat4 = hot_restart_->alloc("stat4");
  Stats::RawStatData* stat5 = hot_restart_->alloc("stat5");
  hot_restart_->free(*stat2);
  hot_restart_->free(*stat4);
  stat2 = nullptr;
  stat4 = nullptr;

  EXPECT_CALL(options_, restartEpoch()).WillRepeatedly(Return(1));
  EXPECT_CALL(os_sys_calls_, shmOpen(_, _, _));
  EXPECT_CALL(os_sys_calls_, mmap(_, _, _, _, _, _)).WillOnce(Return(buffer_.data()));
  EXPECT_CALL(os_sys_calls_, bind(_, _, _));
  HotRestartImpl hot_restart2(options_, os_sys_calls_);
  Stats::RawStatData* stat1_prime = hot_restart2.alloc("stat1");
  Stats::RawStatData* stat3_prime = hot_restart2.alloc("stat3");
  Stats::RawStatData* stat5_prime = hot_restart2.alloc("stat5");
  EXPECT_EQ(stat1, stat1_prime);
  EXPECT_EQ(stat3, stat3_prime);
  EXPECT_EQ(stat5, stat5_prime);
}

TEST_F(HotRestartImplTest, allocFail) {
  EXPECT_CALL(options_, maxStats()).WillRepeatedly(Return(2));
  setup();

  Stats::RawStatData* s1 = hot_restart_->alloc("1");
  Stats::RawStatData* s2 = hot_restart_->alloc("2");
  Stats::RawStatData* s3 = hot_restart_->alloc("3");
  EXPECT_NE(s1, nullptr);
  EXPECT_NE(s2, nullptr);
  EXPECT_EQ(s3, nullptr);
}

// Because the shared memory is managed manually, make sure it meets
// basic requirements:
//   - Objects are correctly aligned so that std::atomic works properly
//   - Objects don't overlap
class HotRestartImplAlignmentTest : public HotRestartImplTest,
                                    public testing::WithParamInterface<uint64_t> {
public:
  HotRestartImplAlignmentTest() : name_len_(8 + GetParam()) {
    EXPECT_CALL(options_, maxStats()).WillRepeatedly(Return(num_stats_));
    EXPECT_CALL(options_, maxObjNameLength()).WillRepeatedly(Return(name_len_));
    setup();
    EXPECT_EQ(name_len_, Stats::RawStatData::maxObjNameLength());
  }

  static const uint64_t num_stats_ = 8;
  const uint64_t name_len_;
};

TEST_P(HotRestartImplAlignmentTest, objectAlignment) {

  std::set<Stats::RawStatData*> used;
  for (uint64_t i = 0; i < num_stats_; i++) {
    Stats::RawStatData* stat = hot_restart_->alloc(fmt::format("stat {}", i));
    EXPECT_TRUE((reinterpret_cast<uintptr_t>(stat) % alignof(decltype(*stat))) == 0);
    EXPECT_TRUE(used.find(stat) == used.end());
    used.insert(stat);
  }
}

TEST_P(HotRestartImplAlignmentTest, objectOverlap) {
  // Iterate through all stats forwards and backwards, writing to all fields, then read them back to
  // make sure that writing to an adjacent stat didn't overwrite
  struct TestStat {
    Stats::RawStatData* stat_;
    std::string name_;
    uint64_t index_;
  };
  std::vector<TestStat> stats;
  for (uint64_t i = 0; i < num_stats_; i++) {
    // 67 Z characters + 1 digit (num_stats == 8) for total of 68 characters
    std::string name = fmt::format("{}zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"
                                   "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"
                                   "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz",
                                   i)
                           .substr(0, Stats::RawStatData::maxNameLength());
    TestStat ts;
    ts.stat_ = hot_restart_->alloc(name);
    ts.name_ = ts.stat_->name_;
    ts.index_ = i;

    // If this isn't true then the hard coded part of the name isn't long enough to make the test
    // valid.
    EXPECT_EQ(ts.name_.size(), Stats::RawStatData::maxNameLength());

    stats.push_back(ts);
  }

  auto write = [](TestStat& ts) {
    ts.stat_->value_ = ts.index_;
    ts.stat_->pending_increment_ = ts.index_;
    ts.stat_->flags_ = ts.index_;
    ts.stat_->ref_count_ = ts.index_;
    ts.stat_->unused_ = ts.index_;
  };

  auto verify = [](TestStat& ts) {
    EXPECT_TRUE(ts.stat_->matches(ts.name_));
    EXPECT_EQ(ts.stat_->value_, ts.index_);
    EXPECT_EQ(ts.stat_->pending_increment_, ts.index_);
    EXPECT_EQ(ts.stat_->flags_, ts.index_);
    EXPECT_EQ(ts.stat_->ref_count_, ts.index_);
    EXPECT_EQ(ts.stat_->unused_, ts.index_);
  };

  for (TestStat& ts : stats) {
    write(ts);
  }

  for (TestStat& ts : stats) {
    verify(ts);
  }

  for (auto it = stats.rbegin(); it != stats.rend(); ++it) {
    write(*it);
  }

  for (auto it = stats.rbegin(); it != stats.rend(); ++it) {
    verify(*it);
  }
}

INSTANTIATE_TEST_CASE_P(HotRestartImplAlignmentTest, HotRestartImplAlignmentTest,
                        testing::Range(0UL, alignof(Stats::RawStatData) + 1));

} // namespace Server
} // namespace Envoy
<|endoftext|>