<commit_before>//-----------------------------------------------
//
//	This file is part of the Siv3D Engine.
//
//	Copyright (c) 2008-2022 Ryo Suzuki
//	Copyright (c) 2016-2022 OpenSiv3D Project
//
//	Licensed under the MIT License.
//
//-----------------------------------------------

# include <Siv3D/ParseFloat.hpp>
# include <Siv3D/Error.hpp>
# include <Siv3D/FormatLiteral.hpp>
# include <ThirdParty/double-conversion/double-conversion.h>

namespace s3d
{
	namespace detail
	{
		inline static constexpr double sNaN = std::numeric_limits<double>::signaling_NaN();

		static double ParseDouble(const StringView s)
		{
			using namespace double_conversion;

			const int flags =
				StringToDoubleConverter::ALLOW_LEADING_SPACES
				| StringToDoubleConverter::ALLOW_TRAILING_SPACES
				| StringToDoubleConverter::ALLOW_SPACES_AFTER_SIGN
				| StringToDoubleConverter::ALLOW_CASE_INSENSIBILITY;
			StringToDoubleConverter conv(flags, 0.0, sNaN, "inf", "nan");

			int unused;
			const double result = conv.Siv3D_StringToIeee(s.data(), static_cast<int>(s.length()), false, &unused);

			if (std::memcmp(&result, &sNaN, sizeof(double)) == 0)
			{
				throw ParseError(U"ParseFloat(\"{}\") failed"_fmt(s));
			}

			return result;
		}

		template <class FloatType>
		static Optional<FloatType> ParseFloatingPointOpt(const StringView s) noexcept
		{
			using namespace double_conversion;

			const int flags =
				StringToDoubleConverter::ALLOW_LEADING_SPACES
				| StringToDoubleConverter::ALLOW_TRAILING_SPACES
				| StringToDoubleConverter::ALLOW_SPACES_AFTER_SIGN
				| StringToDoubleConverter::ALLOW_CASE_INSENSIBILITY;
			StringToDoubleConverter conv(flags, 0.0, sNaN, "inf", "nan");

			int unused;
			const double result = conv.Siv3D_StringToIeee(s.data(), static_cast<int>(s.length()), false, &unused);

			if (std::memcmp(&result, &sNaN, sizeof(double)) == 0)
			{
				return none;
			}

			return static_cast<FloatType>(result);
		}
	}

	template <>
	float ParseFloat<float>(const StringView s)
	{
		return static_cast<float>(detail::ParseDouble(s));
	}

	template <>
	double ParseFloat<double>(const StringView s)
	{
		return detail::ParseDouble(s);
	}

	template <>
	long double ParseFloat<long double>(const StringView s)
	{
		return detail::ParseDouble(s);
	}

	template <>
	Optional<float> ParseFloatOpt<float>(const StringView s) noexcept
	{
		return detail::ParseFloatingPointOpt<float>(s);
	}

	template <>
	Optional<double> ParseFloatOpt<double>(const StringView s) noexcept
	{
		return detail::ParseFloatingPointOpt<double>(s);
	}

	template <>
	Optional<long double> ParseFloatOpt<long double>(const StringView s) noexcept
	{
		return detail::ParseFloatingPointOpt<long double>(s);
	}
}
<commit_msg>[共通] Parse<double> が float の精度で行われているバグの修正 #831<commit_after>//-----------------------------------------------
//
//	This file is part of the Siv3D Engine.
//
//	Copyright (c) 2008-2022 Ryo Suzuki
//	Copyright (c) 2016-2022 OpenSiv3D Project
//
//	Licensed under the MIT License.
//
//-----------------------------------------------

# include <Siv3D/ParseFloat.hpp>
# include <Siv3D/Error.hpp>
# include <Siv3D/FormatLiteral.hpp>
# include <ThirdParty/double-conversion/double-conversion.h>

namespace s3d
{
	namespace detail
	{
		inline static constexpr double sNaN = std::numeric_limits<double>::signaling_NaN();

		static double ParseDouble(const StringView s)
		{
			using namespace double_conversion;

			const int flags =
				StringToDoubleConverter::ALLOW_LEADING_SPACES
				| StringToDoubleConverter::ALLOW_TRAILING_SPACES
				| StringToDoubleConverter::ALLOW_SPACES_AFTER_SIGN
				| StringToDoubleConverter::ALLOW_CASE_INSENSIBILITY;
			StringToDoubleConverter conv(flags, 0.0, sNaN, "inf", "nan");

			int unused;
			const double result = conv.Siv3D_StringToIeee(s.data(), static_cast<int>(s.length()), true, &unused);

			if (std::memcmp(&result, &sNaN, sizeof(double)) == 0)
			{
				throw ParseError(U"ParseFloat(\"{}\") failed"_fmt(s));
			}

			return result;
		}

		template <class FloatType>
		static Optional<FloatType> ParseFloatingPointOpt(const StringView s) noexcept
		{
			using namespace double_conversion;

			const int flags =
				StringToDoubleConverter::ALLOW_LEADING_SPACES
				| StringToDoubleConverter::ALLOW_TRAILING_SPACES
				| StringToDoubleConverter::ALLOW_SPACES_AFTER_SIGN
				| StringToDoubleConverter::ALLOW_CASE_INSENSIBILITY;
			StringToDoubleConverter conv(flags, 0.0, sNaN, "inf", "nan");

			int unused;
			const double result = conv.Siv3D_StringToIeee(s.data(), static_cast<int>(s.length()), false, &unused);

			if (std::memcmp(&result, &sNaN, sizeof(double)) == 0)
			{
				return none;
			}

			return static_cast<FloatType>(result);
		}
	}

	template <>
	float ParseFloat<float>(const StringView s)
	{
		return static_cast<float>(detail::ParseDouble(s));
	}

	template <>
	double ParseFloat<double>(const StringView s)
	{
		return detail::ParseDouble(s);
	}

	template <>
	long double ParseFloat<long double>(const StringView s)
	{
		return detail::ParseDouble(s);
	}

	template <>
	Optional<float> ParseFloatOpt<float>(const StringView s) noexcept
	{
		return detail::ParseFloatingPointOpt<float>(s);
	}

	template <>
	Optional<double> ParseFloatOpt<double>(const StringView s) noexcept
	{
		return detail::ParseFloatingPointOpt<double>(s);
	}

	template <>
	Optional<long double> ParseFloatOpt<long double>(const StringView s) noexcept
	{
		return detail::ParseFloatingPointOpt<long double>(s);
	}
}
<|endoftext|>


<commit_before>#include <algorithm>
#include <array>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <map>
#include <memory>
#include <sstream>
#include <stdexcept>
#include <string>
#include <thread>
#include <vector>

extern "C" {
#include <sys/wait.h>
}

namespace {

const char* const USAGE = R"EOS(
Usage: kitty-colors [-h] [-c FILE] [-a | -p] [-f FRAMES] [-d DELAY]

This script changes the terminal colors in kitty.

Note: If you get "Connection refused" Python errors, it's probably because
there's a stale control file in ~/.local/share/kitty.

Flags:
    -h  display this help messge
    -p  print OSC codes only (no kitty remote-control or changing colors.conf)
    -a  animate the transition to the new colors

Options:
    -c FILE    specify colors conf file (if omitted, you pick using fzf)
    -f FRAMES  number of animation frames (default: 50)
    -d DELAY   delay in seconds between frames (default: 30 ms)
)EOS";

struct Options {
    bool print_only = false;
    bool animate = false;

    int frames = 100;
    int delay = 30;

    std::string target;
};

const char* PROGRAM = nullptr;

bool inside_tmux() {
    static bool tmux = std::getenv("TMUX") != nullptr;
    return tmux;
}

std::string base16_colors_dir() {
    return std::string(std::getenv("PROJECTS")) + "/base16-kitty/colors";
}

std::string kitty_colors_conf_file() {
    return std::string(std::getenv("HOME")) + "/.config/kitty/colors.conf";
}

std::string kitty_sockets_dir() {
    return std::string(std::getenv("HOME")) + "/.local/share/kitty";
}

std::string exec(const char* cmd) {
    std::array<char, 128> buffer;
    std::string result;
    std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd, "r"), pclose);
    if (!pipe) {
        throw std::runtime_error("popen() failed!");
    }
    while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
        result += buffer.data();
    }
    result.erase(result.find_last_not_of("\r\n") + 1);
    return result;
}

using Key = int;
using Color = unsigned;
using ColorSet = std::map<Key, Color>;

const Key FOREGROUND = -1;
const Key BACKGROUND = -2;

ColorSet parse_color_file(const char* filename) {
    ColorSet colors;
    std::ifstream infile(filename);
    std::string line;
    while (std::getline(infile, line)) {
        if (line.empty() || line[0] == '#') {
            continue;
        }
        Key key;
        std::istringstream iss(line);
        std::string key_str;
        iss >> key_str;
        if (key_str == "foreground") {
            key = FOREGROUND;
        } else if (key_str == "background") {
            key = BACKGROUND;
        } else if (key_str.substr(0, 5) == "color") {
            key = std::stoi(key_str.substr(5));
        } else {
            continue;
        }

        std::string color_str;
        iss >> color_str;
        if (color_str.empty() || color_str[0] != '#') {
            continue;
        }
        Color color =
            static_cast<unsigned>(std::stoi(color_str.substr(1), nullptr, 16));
        colors.emplace(key, color);
    }
    return colors;
}

void set_colors_osc(const ColorSet& colors) {
    const char* pre = inside_tmux() ? "\x1bPtmux;\x1b\x1b]" : "\x1b]";
    const char* post = inside_tmux() ? "\a\x1b\\\x1b\\" : "\a";
    std::ostringstream ss;
    char buffer[7];
    for (const auto& entry : colors) {
        ss << pre;
        switch (entry.first) {
        case FOREGROUND:
            ss << "10";
            break;
        case BACKGROUND:
            ss << "11";
            break;
        default:
            ss << "4;" << entry.first;
            break;
        }
        std::snprintf(buffer, sizeof buffer, "%06x", entry.second);
        ss << ";#" << buffer << post;
    }
    std::cout << ss.str();
    std::cout.flush();
}

double bit_to_linear(unsigned bit) {
    double x = static_cast<double>(bit) / 255;
    if (x < 0.04045) {
        return x / 12.92;
    }
    return std::pow((x + 0.055) / 1.055, 2.4);
}

unsigned linear_to_bit(double linear) {
    double x = linear <= 0.0031308 ? linear * 12.92
                                   : 1.055 * std::pow(linear, 1 / 2.4) - 0.055;
    return static_cast<unsigned>(
        std::max(0, std::min(255, static_cast<int>(std::lround(x * 255)))));
}

Color interpolate_color(Color c1, Color c2, double t) {
    double r1 = bit_to_linear((c1 >> 16) & 0xff);
    double g1 = bit_to_linear((c1 >> 8) & 0xff);
    double b1 = bit_to_linear(c1 & 0xff);

    double r2 = bit_to_linear((c2 >> 16) & 0xff);
    double g2 = bit_to_linear((c2 >> 8) & 0xff);
    double b2 = bit_to_linear(c2 & 0xff);

    unsigned r = linear_to_bit(r1 + (r2 - r1) * t);
    unsigned g = linear_to_bit(g1 + (g2 - g1) * t);
    unsigned b = linear_to_bit(b1 + (b2 - b1) * t);

    return (r << 16) + (g << 8) + b;
}

void animate_colors(const ColorSet& src, const ColorSet& dst, int frames,
                    int delay_ms) {
    const std::chrono::milliseconds delay(delay_ms);
    for (int i = 0; i < frames; ++i) {
        ColorSet colors;
        for (const auto& entry : src) {
            const double t = static_cast<double>(i + 1) / frames;
            Color interp =
                interpolate_color(entry.second, dst.at(entry.first), t);
            colors.emplace(entry.first, interp);
        }
        set_colors_osc(colors);
        std::this_thread::sleep_for(delay);
    }
}

void update_running_kitties(const std::string& colors_file) {
    for (const auto& entry :
         std::filesystem::directory_iterator(kitty_sockets_dir())) {
        if (entry.path().extension() != ".sock") {
            continue;
        }
        std::ostringstream ss;
        ss << "kitty @ --to 'unix:" << entry.path().string()
           << "' set-colors -a -c '" << colors_file << "' &";
        system(ss.str().c_str());
    }
    wait(nullptr);
}

void update_kitty_conf(const std::string& colors_file) {
    std::ofstream file(kitty_colors_conf_file());
    file << "include " << colors_file << "\n";
}

}  // namespace

int main(int argc, char** argv) {
    PROGRAM = argv[0];

    if (std::system("command -v kitty > /dev/null 2>&1") != 0) {
        std::cerr << PROGRAM << ": kitty: command not found\n";
        return 1;
    }

    Options options;
    for (int i = 1; i < argc; ++i) {
        if (std::strcmp(argv[i], "-h") == 0 || std::strcmp(argv[i], "--help") == 0) {
            std::cout << USAGE;
            return 0;
        }
        if (std::strcmp(argv[i], "-p") == 0) {
            options.print_only = true;
        } else if (std::strcmp(argv[i], "-a") == 0) {
            options.animate = true;
        } else if (std::strcmp(argv[i], "-c") == 0) {
            options.target = argv[++i];
        } else if (std::strcmp(argv[i], "-d") == 0) {
            options.delay = std::stoi(argv[++i]);
        } else if (std::strcmp(argv[i], "-f") == 0) {
            options.frames = std::stoi(argv[++i]);
        }
    }

    const auto colors_dir = base16_colors_dir();
    if (options.target.empty()) {
        if (!std::filesystem::is_directory(colors_dir)) {
            std::cerr << PROGRAM << ": " << colors_dir
                      << ": directory not found\n";
            return 1;
        }
        std::ostringstream ss;
        ss << "find " << colors_dir
           << " -name '*[^2][^5][^6].conf'"
              " | sed 's|^.*/base16-||;s/.conf$//' | sort | fzf";
        options.target = exec(ss.str().c_str());
        if (options.target.empty()) {
            return 0;
        }
    }
    if (!std::filesystem::is_regular_file(options.target)) {
        std::string original = options.target;
        options.target = colors_dir + "/base16-" + options.target + ".conf";
        if (!std::filesystem::is_regular_file(options.target)) {
            std::cerr << PROGRAM << ": " << original
                      << ": file or color scheme name not found\n";
            return 1;
        }
    }

    if (options.print_only) {
        set_colors_osc(parse_color_file(options.target.c_str()));
        return 0;
    }

    if (options.animate) {
        std::ifstream config_file(kitty_colors_conf_file());
        std::string token;
        config_file >> token;
        if (token != "include") {
            std::cerr << PROGRAM << ": " << kitty_colors_conf_file()
                      << ": malformed config file\n";
            return 1;
        }
        config_file >> token;
        const auto& path = token;
        if (!std::filesystem::is_regular_file(path)) {
            std::cerr << PROGRAM << ": " << kitty_colors_conf_file() << ": "
                      << path << ": file not found\n";
            return 1;
        }
        auto src_colors = parse_color_file(path.c_str());
        auto dst_colors = parse_color_file(options.target.c_str());
        animate_colors(src_colors, dst_colors, options.frames, options.delay);
    }

    update_running_kitties(options.target);
    update_kitty_conf(options.target);
    return 0;
}
<commit_msg>Run make fmt<commit_after>#include <algorithm>
#include <array>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <map>
#include <memory>
#include <sstream>
#include <stdexcept>
#include <string>
#include <thread>
#include <vector>

extern "C" {
#include <sys/wait.h>
}

namespace {

const char* const USAGE = R"EOS(
Usage: kitty-colors [-h] [-c FILE] [-a | -p] [-f FRAMES] [-d DELAY]

This script changes the terminal colors in kitty.

Note: If you get "Connection refused" Python errors, it's probably because
there's a stale control file in ~/.local/share/kitty.

Flags:
    -h  display this help messge
    -p  print OSC codes only (no kitty remote-control or changing colors.conf)
    -a  animate the transition to the new colors

Options:
    -c FILE    specify colors conf file (if omitted, you pick using fzf)
    -f FRAMES  number of animation frames (default: 50)
    -d DELAY   delay in seconds between frames (default: 30 ms)
)EOS";

struct Options {
    bool print_only = false;
    bool animate = false;

    int frames = 100;
    int delay = 30;

    std::string target;
};

const char* PROGRAM = nullptr;

bool inside_tmux() {
    static bool tmux = std::getenv("TMUX") != nullptr;
    return tmux;
}

std::string base16_colors_dir() {
    return std::string(std::getenv("PROJECTS")) + "/base16-kitty/colors";
}

std::string kitty_colors_conf_file() {
    return std::string(std::getenv("HOME")) + "/.config/kitty/colors.conf";
}

std::string kitty_sockets_dir() {
    return std::string(std::getenv("HOME")) + "/.local/share/kitty";
}

std::string exec(const char* cmd) {
    std::array<char, 128> buffer;
    std::string result;
    std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd, "r"), pclose);
    if (!pipe) {
        throw std::runtime_error("popen() failed!");
    }
    while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
        result += buffer.data();
    }
    result.erase(result.find_last_not_of("\r\n") + 1);
    return result;
}

using Key = int;
using Color = unsigned;
using ColorSet = std::map<Key, Color>;

const Key FOREGROUND = -1;
const Key BACKGROUND = -2;

ColorSet parse_color_file(const char* filename) {
    ColorSet colors;
    std::ifstream infile(filename);
    std::string line;
    while (std::getline(infile, line)) {
        if (line.empty() || line[0] == '#') {
            continue;
        }
        Key key;
        std::istringstream iss(line);
        std::string key_str;
        iss >> key_str;
        if (key_str == "foreground") {
            key = FOREGROUND;
        } else if (key_str == "background") {
            key = BACKGROUND;
        } else if (key_str.substr(0, 5) == "color") {
            key = std::stoi(key_str.substr(5));
        } else {
            continue;
        }

        std::string color_str;
        iss >> color_str;
        if (color_str.empty() || color_str[0] != '#') {
            continue;
        }
        Color color =
            static_cast<unsigned>(std::stoi(color_str.substr(1), nullptr, 16));
        colors.emplace(key, color);
    }
    return colors;
}

void set_colors_osc(const ColorSet& colors) {
    const char* pre = inside_tmux() ? "\x1bPtmux;\x1b\x1b]" : "\x1b]";
    const char* post = inside_tmux() ? "\a\x1b\\\x1b\\" : "\a";
    std::ostringstream ss;
    char buffer[7];
    for (const auto& entry : colors) {
        ss << pre;
        switch (entry.first) {
        case FOREGROUND:
            ss << "10";
            break;
        case BACKGROUND:
            ss << "11";
            break;
        default:
            ss << "4;" << entry.first;
            break;
        }
        std::snprintf(buffer, sizeof buffer, "%06x", entry.second);
        ss << ";#" << buffer << post;
    }
    std::cout << ss.str();
    std::cout.flush();
}

double bit_to_linear(unsigned bit) {
    double x = static_cast<double>(bit) / 255;
    if (x < 0.04045) {
        return x / 12.92;
    }
    return std::pow((x + 0.055) / 1.055, 2.4);
}

unsigned linear_to_bit(double linear) {
    double x = linear <= 0.0031308 ? linear * 12.92
                                   : 1.055 * std::pow(linear, 1 / 2.4) - 0.055;
    return static_cast<unsigned>(
        std::max(0, std::min(255, static_cast<int>(std::lround(x * 255)))));
}

Color interpolate_color(Color c1, Color c2, double t) {
    double r1 = bit_to_linear((c1 >> 16) & 0xff);
    double g1 = bit_to_linear((c1 >> 8) & 0xff);
    double b1 = bit_to_linear(c1 & 0xff);

    double r2 = bit_to_linear((c2 >> 16) & 0xff);
    double g2 = bit_to_linear((c2 >> 8) & 0xff);
    double b2 = bit_to_linear(c2 & 0xff);

    unsigned r = linear_to_bit(r1 + (r2 - r1) * t);
    unsigned g = linear_to_bit(g1 + (g2 - g1) * t);
    unsigned b = linear_to_bit(b1 + (b2 - b1) * t);

    return (r << 16) + (g << 8) + b;
}

void animate_colors(const ColorSet& src, const ColorSet& dst, int frames,
                    int delay_ms) {
    const std::chrono::milliseconds delay(delay_ms);
    for (int i = 0; i < frames; ++i) {
        ColorSet colors;
        for (const auto& entry : src) {
            const double t = static_cast<double>(i + 1) / frames;
            Color interp =
                interpolate_color(entry.second, dst.at(entry.first), t);
            colors.emplace(entry.first, interp);
        }
        set_colors_osc(colors);
        std::this_thread::sleep_for(delay);
    }
}

void update_running_kitties(const std::string& colors_file) {
    for (const auto& entry :
         std::filesystem::directory_iterator(kitty_sockets_dir())) {
        if (entry.path().extension() != ".sock") {
            continue;
        }
        std::ostringstream ss;
        ss << "kitty @ --to 'unix:" << entry.path().string()
           << "' set-colors -a -c '" << colors_file << "' &";
        system(ss.str().c_str());
    }
    wait(nullptr);
}

void update_kitty_conf(const std::string& colors_file) {
    std::ofstream file(kitty_colors_conf_file());
    file << "include " << colors_file << "\n";
}

}  // namespace

int main(int argc, char** argv) {
    PROGRAM = argv[0];

    if (std::system("command -v kitty > /dev/null 2>&1") != 0) {
        std::cerr << PROGRAM << ": kitty: command not found\n";
        return 1;
    }

    Options options;
    for (int i = 1; i < argc; ++i) {
        if (std::strcmp(argv[i], "-h") == 0 ||
            std::strcmp(argv[i], "--help") == 0) {
            std::cout << USAGE;
            return 0;
        }
        if (std::strcmp(argv[i], "-p") == 0) {
            options.print_only = true;
        } else if (std::strcmp(argv[i], "-a") == 0) {
            options.animate = true;
        } else if (std::strcmp(argv[i], "-c") == 0) {
            options.target = argv[++i];
        } else if (std::strcmp(argv[i], "-d") == 0) {
            options.delay = std::stoi(argv[++i]);
        } else if (std::strcmp(argv[i], "-f") == 0) {
            options.frames = std::stoi(argv[++i]);
        }
    }

    const auto colors_dir = base16_colors_dir();
    if (options.target.empty()) {
        if (!std::filesystem::is_directory(colors_dir)) {
            std::cerr << PROGRAM << ": " << colors_dir
                      << ": directory not found\n";
            return 1;
        }
        std::ostringstream ss;
        ss << "find " << colors_dir
           << " -name '*[^2][^5][^6].conf'"
              " | sed 's|^.*/base16-||;s/.conf$//' | sort | fzf";
        options.target = exec(ss.str().c_str());
        if (options.target.empty()) {
            return 0;
        }
    }
    if (!std::filesystem::is_regular_file(options.target)) {
        std::string original = options.target;
        options.target = colors_dir + "/base16-" + options.target + ".conf";
        if (!std::filesystem::is_regular_file(options.target)) {
            std::cerr << PROGRAM << ": " << original
                      << ": file or color scheme name not found\n";
            return 1;
        }
    }

    if (options.print_only) {
        set_colors_osc(parse_color_file(options.target.c_str()));
        return 0;
    }

    if (options.animate) {
        std::ifstream config_file(kitty_colors_conf_file());
        std::string token;
        config_file >> token;
        if (token != "include") {
            std::cerr << PROGRAM << ": " << kitty_colors_conf_file()
                      << ": malformed config file\n";
            return 1;
        }
        config_file >> token;
        const auto& path = token;
        if (!std::filesystem::is_regular_file(path)) {
            std::cerr << PROGRAM << ": " << kitty_colors_conf_file() << ": "
                      << path << ": file not found\n";
            return 1;
        }
        auto src_colors = parse_color_file(path.c_str());
        auto dst_colors = parse_color_file(options.target.c_str());
        animate_colors(src_colors, dst_colors, options.frames, options.delay);
    }

    update_running_kitties(options.target);
    update_kitty_conf(options.target);
    return 0;
}
<|endoftext|>


<commit_before>/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.

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 "tensorflow/core/framework/dataset.h"
#include "tensorflow/core/lib/io/buffered_inputstream.h"
#include "tensorflow/core/platform/file_system.h"
#include "tiff.h"
#include "tiffio.h"

namespace tensorflow {
namespace data {
namespace {

extern "C" {
  static tmsize_t tiffclient_read(thandle_t handle, void* buf, tmsize_t tsize);
  static tmsize_t tiffclient_write(thandle_t handle, void* buf, tmsize_t tsize);
  static toff_t tiffclient_seek(thandle_t handle, toff_t toffset, int whence);
  static int tiffclient_close(thandle_t handle);
  static toff_t tiffclient_size(thandle_t handle);
  static int tiffclient_map(thandle_t handle, void** base, toff_t* tsize);
  static void tiffclient_unmap(thandle_t handle, void* base, toff_t tsize);
}

// Base object for wrapping TIFFClientOpen file operation callbacks

class TiffFileBase {
  std::unique_ptr<TIFF, decltype(&TIFFClose)> tif_;

 public:
  TIFF* Tiff() { return tif_.get(); }

  TiffFileBase() :  tif_(nullptr, TIFFClose) {}
  virtual ~TiffFileBase() {}

 protected:
  Status ClientOpen(const char *name, const char* mode) {
    auto tif = TIFFClientOpen(name, mode, this,
                          tiffclient_read,
                          tiffclient_write,
                          tiffclient_seek,
                          tiffclient_close,
                          tiffclient_size,
                          tiffclient_map,
                          tiffclient_unmap);

    if (tif == NULL) {
      return errors::InvalidArgument("unable to open file:", name);
    }

    tif_.reset(tif);
    return Status::OK();
  }

  void ClientClose() {tif_.reset(); }

 protected:
  virtual size_t  TiffClientRead(void*, size_t) { return 0;}
  virtual size_t  TiffClientWrite(void*, size_t) { return 0; }
  virtual off_t   TiffClientSeek(off_t offset, int whence) = 0;
  virtual int     TiffClientClose() { return 0;}
  virtual off_t   TiffClientSize() = 0;
  virtual int     TiffClientMap(void** base, off_t* size) { return 0;}
  virtual void    TiffClientUnmap(void* base, off_t size) {}

  friend tmsize_t tiffclient_read(thandle_t handle, void* buf, tmsize_t tsize);
  friend tmsize_t tiffclient_write(thandle_t handle, void* buf, tmsize_t tsize);
  friend toff_t tiffclient_seek(thandle_t handle, toff_t toffset, int whence);
  friend int tiffclient_close(thandle_t handle);
  friend toff_t tiffclient_size(thandle_t handle);
  friend int tiffclient_map(thandle_t handle, void** base, toff_t* tsize);
  friend void tiffclient_unmap(thandle_t handle, void* base, toff_t tsize);
};

extern "C"  {

static tmsize_t
tiffclient_read(thandle_t handle, void* buf, tmsize_t tsize) {
  TiffFileBase *bobj = reinterpret_cast<TiffFileBase *>(handle);
  size_t size = static_cast<size_t>(tsize);
  if (static_cast<tmsize_t>(size) != tsize)
    return static_cast<tmsize_t>(-1);
  size_t result = bobj->TiffClientRead(buf, size);
  return static_cast<tmsize_t>(result);
}

static tmsize_t
tiffclient_write(thandle_t handle, void* buf, tmsize_t tsize) {
  TiffFileBase *bobj = reinterpret_cast<TiffFileBase *>(handle);
  size_t size = static_cast<size_t>(tsize);
  if (static_cast<tmsize_t>(size) != tsize)
    return static_cast<tmsize_t>(-1);
  size_t result =  bobj->TiffClientWrite(buf, size);
  return static_cast<tmsize_t>(result);
}

static toff_t
tiffclient_seek(thandle_t handle, toff_t toffset, int whence) {
  TiffFileBase *bobj = reinterpret_cast<TiffFileBase *>(handle);
  off_t offset = static_cast<off_t>(toffset);
  off_t result = bobj->TiffClientSeek(offset, whence);
  return static_cast<toff_t>(result);
}

static int
tiffclient_close(thandle_t handle) {
  TiffFileBase *bobj = reinterpret_cast<TiffFileBase *>(handle);
  return bobj->TiffClientClose();
}

static toff_t
tiffclient_size(thandle_t handle) {
  TiffFileBase *bobj = reinterpret_cast<TiffFileBase *>(handle);
  off_t result = bobj->TiffClientSize();
  return static_cast<toff_t>(result);
}

static int
tiffclient_map(thandle_t handle, void** base, toff_t* tsize) {
  TiffFileBase *bobj = reinterpret_cast<TiffFileBase *>(handle);
  off_t size;
  int result = bobj->TiffClientMap(base, &size);
  *tsize = static_cast<toff_t>(size);
  return result;
}

static void
tiffclient_unmap(thandle_t handle, void* base, toff_t tsize) {
  TiffFileBase *bobj = reinterpret_cast<TiffFileBase *>(handle);
  off_t size = static_cast<off_t>(tsize);
  return bobj->TiffClientUnmap(base, size);
}

}  //  extern "C"

// Use RandomAccessFile for tiff file system operation callbacks
class TiffRandomFile : public TiffFileBase {
 public:
  TiffRandomFile() {}
  ~TiffRandomFile() override  {
    Close();
  }

  Status Open(Env *env, const string& filename) {
    // Open Random access file
    std::unique_ptr<RandomAccessFile> file;
    TF_RETURN_IF_ERROR(env->NewRandomAccessFile(filename, &file));
    // Read Size and hope we get same file as above
    uint64 size = 0;
    TF_RETURN_IF_ERROR(env->GetFileSize(filename, &size));
    // Open Tiff
    fileSize_ = static_cast<off_t>(size);
    offset_ = 0;
    file_ = std::move(file);
    Status s = ClientOpen(filename.c_str(), "rm");
    if (!s.ok()) {
      file_.reset();
    }
    return s;
  }

  bool IsOpen() {
    return static_cast<bool>(file_);
  }

  void Close() {
    ClientClose();
    file_.reset();
  }

 private:
  size_t  TiffClientRead(void*, size_t) override;
  off_t   TiffClientSeek(off_t offset, int whence) override;
  int     TiffClientClose() override;
  off_t   TiffClientSize() override;

  std::unique_ptr<RandomAccessFile> file_;
  off_t fileSize_;
  off_t offset_;
};

size_t TiffRandomFile::TiffClientRead(void* data, size_t n) {
  StringPiece result;
  Status s;
  s = file_.get()->Read(offset_, n, &result, reinterpret_cast<char *>(data));
  if (result.data() != data) {
    memmove(data, result.data(), result.size());
  }
  if (s.ok() || errors::IsOutOfRange(s)) {
    offset_ += result.size();
  }
  return  result.size();
}

off_t TiffRandomFile::TiffClientSeek(off_t offset, int whence) {
  switch (whence) {
    case SEEK_SET:
      offset_ = offset;
      break;
    case SEEK_CUR:
      offset_ += offset;
      break;
    case SEEK_END:
      offset_ = fileSize_ + offset;
      break;
    default:
      break;
  }
  return offset_;
}

off_t TiffRandomFile::TiffClientSize() {
  return fileSize_;
}

int TiffRandomFile::TiffClientClose() {
  return 0;
}

class TIFFDatasetOp : public DatasetOpKernel {
 public:
  using DatasetOpKernel::DatasetOpKernel;
  explicit TIFFDatasetOp(OpKernelConstruction* ctx)
      : DatasetOpKernel(ctx) {
  }
  void MakeDataset(OpKernelContext* ctx, DatasetBase** output) override {
    const Tensor* filenames_tensor;
    OP_REQUIRES_OK(ctx, ctx->input("filenames", &filenames_tensor));
    OP_REQUIRES(
        ctx, filenames_tensor->dims() <= 1,
        errors::InvalidArgument("`filenames` must be a scalar or a vector."));

    std::vector<string> filenames;
    filenames.reserve(filenames_tensor->NumElements());
    for (int i = 0; i < filenames_tensor->NumElements(); ++i) {
      filenames.push_back(filenames_tensor->flat<string>()(i));
    }

    *output = new Dataset(ctx, filenames);
  }

 private:
  class Dataset : public DatasetBase {
   public:
    Dataset(OpKernelContext* ctx, const std::vector<string>& filenames)
        : DatasetBase(DatasetContext(ctx)),
          filenames_(filenames) {}

    std::unique_ptr<IteratorBase> MakeIteratorInternal(
        const string& prefix) const override {
      return std::unique_ptr<IteratorBase>(
          new Iterator({this, strings::StrCat(prefix, "::TIFF")}));
    }

    const DataTypeVector& output_dtypes() const override {
      static DataTypeVector* dtypes = new DataTypeVector({DT_UINT8});
      return *dtypes;
    }

    const std::vector<PartialTensorShape>& output_shapes() const override {
      static std::vector<PartialTensorShape>* shapes =
          new std::vector<PartialTensorShape>({{-1, -1, -1}});
      return *shapes;
    }

    string DebugString() const override {
      return "TIFFDatasetOp::Dataset";
    }

   protected:
    Status AsGraphDefInternal(SerializationContext* ctx,
                              DatasetGraphDefBuilder* b,
                              Node** output) const override {
      Node* filenames = nullptr;
      TF_RETURN_IF_ERROR(b->AddVector(filenames_, &filenames));
      TF_RETURN_IF_ERROR(b->AddDataset(this, {filenames}, output));
      return Status::OK();
    }

   private:
    class Iterator : public DatasetIterator<Dataset> {
     public:
      explicit Iterator(const Params& params)
          : DatasetIterator<Dataset>(params) {}

      Status GetNextInternal(IteratorContext* ctx,
                             std::vector<Tensor>* out_tensors,
                             bool* end_of_sequence) override {
        mutex_lock l(mu_);
        do {
          // We are currently processing a file, so try to read the next record.
          if (file_.IsOpen()) {
            unsigned int width, height;
            // get the size of the tiff
            TIFFGetField(file_.Tiff(), TIFFTAG_IMAGEWIDTH, &width);
            TIFFGetField(file_.Tiff(), TIFFTAG_IMAGELENGTH, &height);
            // get the total number of pixels
            unsigned int npixels = width*height;
            uint32* raster = (uint32*)_TIFFmalloc(npixels * sizeof(uint32));
            if (!TIFFReadRGBAImageOriented(file_.Tiff(), width, height, raster, ORIENTATION_TOPLEFT, 0)) {
              _TIFFfree(raster);
              return errors::InvalidArgument("unable to read file: ", dataset()->filenames_[current_file_index_]);
            }
           // RGBA
            static const int channel = 4;
            Tensor value_tensor(ctx->allocator({}), DT_UINT8, {height, width, channel});
            int num_bytes = npixels * sizeof(uint32);
            std::memcpy(reinterpret_cast<char*>(value_tensor.flat<uint8_t>().data()), raster, num_bytes * sizeof(uint8_t));
            out_tensors->emplace_back(std::move(value_tensor));
            _TIFFfree(raster);
            if (!TIFFReadDirectory(file_.Tiff())) {
              ResetStreamsLocked();
              ++current_file_index_;
            }
            *end_of_sequence = false;
            return Status::OK();
          }

          // Iteration ends when there are no more files to process.
          if (current_file_index_ == dataset()->filenames_.size()) {
            *end_of_sequence = true;
            return Status::OK();
          }

          TF_RETURN_IF_ERROR(SetupStreamsLocked(ctx->env()));
        } while (true);
      }

     protected:
      Status SaveInternal(IteratorStateWriter* writer) override {
        return errors::Unimplemented("SaveInternal is currently not supported");
      }

      Status RestoreInternal(IteratorContext* ctx,
                             IteratorStateReader* reader) override {
        return errors::Unimplemented(
            "RestoreInternal is currently not supported");
      }

     private:
      // Sets up TIFF streams to read from the topic at
      // `current_file_index_`.
      Status SetupStreamsLocked(Env* env) EXCLUSIVE_LOCKS_REQUIRED(mu_) {
        if (current_file_index_ >= dataset()->filenames_.size()) {
          return errors::InvalidArgument(
              "current_file_index_:", current_file_index_,
              " >= filenames_.size():", dataset()->filenames_.size());
        }

        // Actually move on to next file.
        const string& filename = dataset()->filenames_[current_file_index_];
        Status s = file_.Open(env, filename);
        return s;
      }

      // Resets all TIFF streams.
      void ResetStreamsLocked() EXCLUSIVE_LOCKS_REQUIRED(mu_) {
        file_.Close();
      }

      mutex mu_;
      size_t current_file_index_ GUARDED_BY(mu_) = 0;
      TiffRandomFile file_ GUARDED_BY(mu_);
    };

    const std::vector<string> filenames_;
    const DataTypeVector output_types_;
  };
  DataTypeVector output_types_;
};

REGISTER_KERNEL_BUILDER(Name("TIFFDataset").Device(DEVICE_CPU),
                        TIFFDatasetOp);

}  // namespace
}  // namespace data
}  // namespace tensorflow
<commit_msg>Decode TIFF directly into tensor buffer.<commit_after>/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.

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 "tensorflow/core/framework/dataset.h"
#include "tensorflow/core/lib/io/buffered_inputstream.h"
#include "tensorflow/core/platform/file_system.h"
#include "tiff.h"
#include "tiffio.h"

namespace tensorflow {
namespace data {
namespace {

extern "C" {
  static tmsize_t tiffclient_read(thandle_t handle, void* buf, tmsize_t tsize);
  static tmsize_t tiffclient_write(thandle_t handle, void* buf, tmsize_t tsize);
  static toff_t tiffclient_seek(thandle_t handle, toff_t toffset, int whence);
  static int tiffclient_close(thandle_t handle);
  static toff_t tiffclient_size(thandle_t handle);
  static int tiffclient_map(thandle_t handle, void** base, toff_t* tsize);
  static void tiffclient_unmap(thandle_t handle, void* base, toff_t tsize);
}

// Base object for wrapping TIFFClientOpen file operation callbacks

class TiffFileBase {
  std::unique_ptr<TIFF, decltype(&TIFFClose)> tif_;

 public:
  TIFF* Tiff() { return tif_.get(); }

  TiffFileBase() :  tif_(nullptr, TIFFClose) {}
  virtual ~TiffFileBase() {}

 protected:
  Status ClientOpen(const char *name, const char* mode) {
    auto tif = TIFFClientOpen(name, mode, this,
                          tiffclient_read,
                          tiffclient_write,
                          tiffclient_seek,
                          tiffclient_close,
                          tiffclient_size,
                          tiffclient_map,
                          tiffclient_unmap);

    if (tif == NULL) {
      return errors::InvalidArgument("unable to open file:", name);
    }

    tif_.reset(tif);
    return Status::OK();
  }

  void ClientClose() {tif_.reset(); }

 protected:
  virtual size_t  TiffClientRead(void*, size_t) { return 0;}
  virtual size_t  TiffClientWrite(void*, size_t) { return 0; }
  virtual off_t   TiffClientSeek(off_t offset, int whence) = 0;
  virtual int     TiffClientClose() { return 0;}
  virtual off_t   TiffClientSize() = 0;
  virtual int     TiffClientMap(void** base, off_t* size) { return 0;}
  virtual void    TiffClientUnmap(void* base, off_t size) {}

  friend tmsize_t tiffclient_read(thandle_t handle, void* buf, tmsize_t tsize);
  friend tmsize_t tiffclient_write(thandle_t handle, void* buf, tmsize_t tsize);
  friend toff_t tiffclient_seek(thandle_t handle, toff_t toffset, int whence);
  friend int tiffclient_close(thandle_t handle);
  friend toff_t tiffclient_size(thandle_t handle);
  friend int tiffclient_map(thandle_t handle, void** base, toff_t* tsize);
  friend void tiffclient_unmap(thandle_t handle, void* base, toff_t tsize);
};

extern "C"  {

static tmsize_t
tiffclient_read(thandle_t handle, void* buf, tmsize_t tsize) {
  TiffFileBase *bobj = reinterpret_cast<TiffFileBase *>(handle);
  size_t size = static_cast<size_t>(tsize);
  if (static_cast<tmsize_t>(size) != tsize)
    return static_cast<tmsize_t>(-1);
  size_t result = bobj->TiffClientRead(buf, size);
  return static_cast<tmsize_t>(result);
}

static tmsize_t
tiffclient_write(thandle_t handle, void* buf, tmsize_t tsize) {
  TiffFileBase *bobj = reinterpret_cast<TiffFileBase *>(handle);
  size_t size = static_cast<size_t>(tsize);
  if (static_cast<tmsize_t>(size) != tsize)
    return static_cast<tmsize_t>(-1);
  size_t result =  bobj->TiffClientWrite(buf, size);
  return static_cast<tmsize_t>(result);
}

static toff_t
tiffclient_seek(thandle_t handle, toff_t toffset, int whence) {
  TiffFileBase *bobj = reinterpret_cast<TiffFileBase *>(handle);
  off_t offset = static_cast<off_t>(toffset);
  off_t result = bobj->TiffClientSeek(offset, whence);
  return static_cast<toff_t>(result);
}

static int
tiffclient_close(thandle_t handle) {
  TiffFileBase *bobj = reinterpret_cast<TiffFileBase *>(handle);
  return bobj->TiffClientClose();
}

static toff_t
tiffclient_size(thandle_t handle) {
  TiffFileBase *bobj = reinterpret_cast<TiffFileBase *>(handle);
  off_t result = bobj->TiffClientSize();
  return static_cast<toff_t>(result);
}

static int
tiffclient_map(thandle_t handle, void** base, toff_t* tsize) {
  TiffFileBase *bobj = reinterpret_cast<TiffFileBase *>(handle);
  off_t size;
  int result = bobj->TiffClientMap(base, &size);
  *tsize = static_cast<toff_t>(size);
  return result;
}

static void
tiffclient_unmap(thandle_t handle, void* base, toff_t tsize) {
  TiffFileBase *bobj = reinterpret_cast<TiffFileBase *>(handle);
  off_t size = static_cast<off_t>(tsize);
  return bobj->TiffClientUnmap(base, size);
}

}  //  extern "C"

// Use RandomAccessFile for tiff file system operation callbacks
class TiffRandomFile : public TiffFileBase {
 public:
  TiffRandomFile() {}
  ~TiffRandomFile() override  {
    Close();
  }

  Status Open(Env *env, const string& filename) {
    // Open Random access file
    std::unique_ptr<RandomAccessFile> file;
    TF_RETURN_IF_ERROR(env->NewRandomAccessFile(filename, &file));
    // Read Size and hope we get same file as above
    uint64 size = 0;
    TF_RETURN_IF_ERROR(env->GetFileSize(filename, &size));
    // Open Tiff
    fileSize_ = static_cast<off_t>(size);
    offset_ = 0;
    file_ = std::move(file);
    Status s = ClientOpen(filename.c_str(), "rm");
    if (!s.ok()) {
      file_.reset();
    }
    return s;
  }

  bool IsOpen() {
    return static_cast<bool>(file_);
  }

  void Close() {
    ClientClose();
    file_.reset();
  }

 private:
  size_t  TiffClientRead(void*, size_t) override;
  off_t   TiffClientSeek(off_t offset, int whence) override;
  int     TiffClientClose() override;
  off_t   TiffClientSize() override;

  std::unique_ptr<RandomAccessFile> file_;
  off_t fileSize_;
  off_t offset_;
};

size_t TiffRandomFile::TiffClientRead(void* data, size_t n) {
  StringPiece result;
  Status s;
  s = file_.get()->Read(offset_, n, &result, reinterpret_cast<char *>(data));
  if (result.data() != data) {
    memmove(data, result.data(), result.size());
  }
  if (s.ok() || errors::IsOutOfRange(s)) {
    offset_ += result.size();
  }
  return  result.size();
}

off_t TiffRandomFile::TiffClientSeek(off_t offset, int whence) {
  switch (whence) {
    case SEEK_SET:
      offset_ = offset;
      break;
    case SEEK_CUR:
      offset_ += offset;
      break;
    case SEEK_END:
      offset_ = fileSize_ + offset;
      break;
    default:
      break;
  }
  return offset_;
}

off_t TiffRandomFile::TiffClientSize() {
  return fileSize_;
}

int TiffRandomFile::TiffClientClose() {
  return 0;
}

class TIFFDatasetOp : public DatasetOpKernel {
 public:
  using DatasetOpKernel::DatasetOpKernel;
  explicit TIFFDatasetOp(OpKernelConstruction* ctx)
      : DatasetOpKernel(ctx) {
  }
  void MakeDataset(OpKernelContext* ctx, DatasetBase** output) override {
    const Tensor* filenames_tensor;
    OP_REQUIRES_OK(ctx, ctx->input("filenames", &filenames_tensor));
    OP_REQUIRES(
        ctx, filenames_tensor->dims() <= 1,
        errors::InvalidArgument("`filenames` must be a scalar or a vector."));

    std::vector<string> filenames;
    filenames.reserve(filenames_tensor->NumElements());
    for (int i = 0; i < filenames_tensor->NumElements(); ++i) {
      filenames.push_back(filenames_tensor->flat<string>()(i));
    }

    *output = new Dataset(ctx, filenames);
  }

 private:
  class Dataset : public DatasetBase {
   public:
    Dataset(OpKernelContext* ctx, const std::vector<string>& filenames)
        : DatasetBase(DatasetContext(ctx)),
          filenames_(filenames) {}

    std::unique_ptr<IteratorBase> MakeIteratorInternal(
        const string& prefix) const override {
      return std::unique_ptr<IteratorBase>(
          new Iterator({this, strings::StrCat(prefix, "::TIFF")}));
    }

    const DataTypeVector& output_dtypes() const override {
      static DataTypeVector* dtypes = new DataTypeVector({DT_UINT8});
      return *dtypes;
    }

    const std::vector<PartialTensorShape>& output_shapes() const override {
      static std::vector<PartialTensorShape>* shapes =
          new std::vector<PartialTensorShape>({{-1, -1, -1}});
      return *shapes;
    }

    string DebugString() const override {
      return "TIFFDatasetOp::Dataset";
    }

   protected:
    Status AsGraphDefInternal(SerializationContext* ctx,
                              DatasetGraphDefBuilder* b,
                              Node** output) const override {
      Node* filenames = nullptr;
      TF_RETURN_IF_ERROR(b->AddVector(filenames_, &filenames));
      TF_RETURN_IF_ERROR(b->AddDataset(this, {filenames}, output));
      return Status::OK();
    }

   private:
    class Iterator : public DatasetIterator<Dataset> {
     public:
      explicit Iterator(const Params& params)
          : DatasetIterator<Dataset>(params) {}

      Status GetNextInternal(IteratorContext* ctx,
                             std::vector<Tensor>* out_tensors,
                             bool* end_of_sequence) override {
        mutex_lock l(mu_);
        do {
          // We are currently processing a file, so try to read the next record.
          if (file_.IsOpen()) {
            unsigned int width, height;
            // get the size of the tiff
            TIFFGetField(file_.Tiff(), TIFFTAG_IMAGEWIDTH, &width);
            TIFFGetField(file_.Tiff(), TIFFTAG_IMAGELENGTH, &height);
            // RGBA
            static const int channel = 4;
            Tensor value_tensor(ctx->allocator({}), DT_UINT8, {height, width, channel});
            // Tensor is aligned
            uint32* raster = reinterpret_cast<uint32*>(value_tensor.flat<uint8_t>().data());
            if (!TIFFReadRGBAImageOriented(file_.Tiff(), width, height, raster, ORIENTATION_TOPLEFT, 0)) {
              return errors::InvalidArgument("unable to read file: ", dataset()->filenames_[current_file_index_]);
            }
            out_tensors->emplace_back(std::move(value_tensor));
            if (!TIFFReadDirectory(file_.Tiff())) {
              ResetStreamsLocked();
              ++current_file_index_;
            }
            *end_of_sequence = false;
            return Status::OK();
          }

          // Iteration ends when there are no more files to process.
          if (current_file_index_ == dataset()->filenames_.size()) {
            *end_of_sequence = true;
            return Status::OK();
          }

          TF_RETURN_IF_ERROR(SetupStreamsLocked(ctx->env()));
        } while (true);
      }

     protected:
      Status SaveInternal(IteratorStateWriter* writer) override {
        return errors::Unimplemented("SaveInternal is currently not supported");
      }

      Status RestoreInternal(IteratorContext* ctx,
                             IteratorStateReader* reader) override {
        return errors::Unimplemented(
            "RestoreInternal is currently not supported");
      }

     private:
      // Sets up TIFF streams to read from the topic at
      // `current_file_index_`.
      Status SetupStreamsLocked(Env* env) EXCLUSIVE_LOCKS_REQUIRED(mu_) {
        if (current_file_index_ >= dataset()->filenames_.size()) {
          return errors::InvalidArgument(
              "current_file_index_:", current_file_index_,
              " >= filenames_.size():", dataset()->filenames_.size());
        }

        // Actually move on to next file.
        const string& filename = dataset()->filenames_[current_file_index_];
        Status s = file_.Open(env, filename);
        return s;
      }

      // Resets all TIFF streams.
      void ResetStreamsLocked() EXCLUSIVE_LOCKS_REQUIRED(mu_) {
        file_.Close();
      }

      mutex mu_;
      size_t current_file_index_ GUARDED_BY(mu_) = 0;
      TiffRandomFile file_ GUARDED_BY(mu_);
    };

    const std::vector<string> filenames_;
    const DataTypeVector output_types_;
  };
  DataTypeVector output_types_;
};

REGISTER_KERNEL_BUILDER(Name("TIFFDataset").Device(DEVICE_CPU),
                        TIFFDatasetOp);

}  // namespace
}  // namespace data
}  // namespace tensorflow
<|endoftext|>


<commit_before>/*
	This file is part of cpp-ethereum.

	cpp-ethereum is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	cpp-ethereum is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with cpp-ethereum.  If not, see <http://www.gnu.org/licenses/>.
*/
/** @file Transaction.cpp
 * @author Gav Wood <i@gavwood.com>
 * @date 2014
 */

#include <libdevcore/vector_ref.h>
#include <libdevcore/Log.h>
#include <libdevcrypto/Common.h>
#include <libethcore/Exceptions.h>
#include "Transaction.h"
using namespace std;
using namespace dev;
using namespace dev::eth;

#define ETH_ADDRESS_DEBUG 0

Transaction::Transaction(bytesConstRef _rlpData, CheckSignature _checkSig)
{
	int field = 0;
	RLP rlp(_rlpData);
	try
	{
		m_nonce = rlp[field = 0].toInt<u256>();
		m_gasPrice = rlp[field = 1].toInt<u256>();
		m_gas = rlp[field = 2].toInt<u256>();
		m_type = rlp[field = 3].isEmpty() ? ContractCreation : MessageCall;
		m_receiveAddress = rlp[field = 3].toHash<Address>();
		m_value = rlp[field = 4].toInt<u256>();
		m_data = rlp[field = 5].toBytes();
		byte v = rlp[field = 6].toInt<byte>() - 27;
		h256 r = rlp[field = 7].toInt<u256>();
		h256 s = rlp[field = 8].toInt<u256>();
		m_vrs = SignatureStruct{ r, s, v };
		if (_checkSig >= CheckSignature::Range && !m_vrs.isValid())
			BOOST_THROW_EXCEPTION(InvalidSignature());
		if (_checkSig == CheckSignature::Sender)
			m_sender = sender();
	}
	catch (Exception& _e)
	{
		_e << errinfo_name("invalid transaction format") << BadFieldError(field,toHex(rlp[field].data().toBytes()));
		throw;
	}
}

Address Transaction::safeSender() const noexcept
{
	try
	{
		return sender();
	}
	catch (...)
	{
		cwarn << "safeSender() did throw an exception: " <<  boost::current_exception_diagnostic_information();
		return Address();
	}
}

Address Transaction::sender() const
{
	if (!m_sender)
	{
		auto p = recover(m_vrs, sha3(WithoutSignature));
		if (!p)
			BOOST_THROW_EXCEPTION(InvalidSignature());
		m_sender = right160(dev::sha3(bytesConstRef(p.data(), sizeof(p))));
	}
	return m_sender;
}

void Transaction::sign(Secret _priv)
{
	auto sig = dev::sign(_priv, sha3(WithoutSignature));
	SignatureStruct sigStruct = *(SignatureStruct const*)&sig;
	if (sigStruct.isValid())
		m_vrs = sigStruct;
}

void Transaction::streamRLP(RLPStream& _s, IncludeSignature _sig) const
{
	if (m_type == NullTransaction)
		return;
	_s.appendList((_sig ? 3 : 0) + 6);
	_s << m_nonce << m_gasPrice << m_gas;
	if (m_type == MessageCall)
		_s << m_receiveAddress;
	else
		_s << "";
	_s << m_value << m_data;
	if (_sig)
		_s << (m_vrs.v + 27) << (u256)m_vrs.r << (u256)m_vrs.s;
}
<commit_msg>a transaction RLP with more fiels than 9 is invalid according to yellow paper<commit_after>/*
	This file is part of cpp-ethereum.

	cpp-ethereum is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	cpp-ethereum is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with cpp-ethereum.  If not, see <http://www.gnu.org/licenses/>.
*/
/** @file Transaction.cpp
 * @author Gav Wood <i@gavwood.com>
 * @date 2014
 */

#include <libdevcore/vector_ref.h>
#include <libdevcore/Log.h>
#include <libdevcrypto/Common.h>
#include <libethcore/Exceptions.h>
#include "Transaction.h"
using namespace std;
using namespace dev;
using namespace dev::eth;

#define ETH_ADDRESS_DEBUG 0

Transaction::Transaction(bytesConstRef _rlpData, CheckSignature _checkSig)
{
	int field = 0;
	RLP rlp(_rlpData);
	try
	{
		m_nonce = rlp[field = 0].toInt<u256>();
		m_gasPrice = rlp[field = 1].toInt<u256>();
		m_gas = rlp[field = 2].toInt<u256>();
		m_type = rlp[field = 3].isEmpty() ? ContractCreation : MessageCall;
		m_receiveAddress = rlp[field = 3].toHash<Address>();
		m_value = rlp[field = 4].toInt<u256>();
		m_data = rlp[field = 5].toBytes();
		byte v = rlp[field = 6].toInt<byte>() - 27;
		h256 r = rlp[field = 7].toInt<u256>();
		h256 s = rlp[field = 8].toInt<u256>();

		if (rlp.itemCount() > 9)
			BOOST_THROW_EXCEPTION(BadRLP() << errinfo_comment("to many fields in the transaction RLP"));

		m_vrs = SignatureStruct{ r, s, v };
		if (_checkSig >= CheckSignature::Range && !m_vrs.isValid())
			BOOST_THROW_EXCEPTION(InvalidSignature());
		if (_checkSig == CheckSignature::Sender)
			m_sender = sender();
	}
	catch (Exception& _e)
	{
		_e << errinfo_name("invalid transaction format") << BadFieldError(field,toHex(rlp[field].data().toBytes()));
		throw;
	}
}

Address Transaction::safeSender() const noexcept
{
	try
	{
		return sender();
	}
	catch (...)
	{
		cwarn << "safeSender() did throw an exception: " <<  boost::current_exception_diagnostic_information();
		return Address();
	}
}

Address Transaction::sender() const
{
	if (!m_sender)
	{
		auto p = recover(m_vrs, sha3(WithoutSignature));
		if (!p)
			BOOST_THROW_EXCEPTION(InvalidSignature());
		m_sender = right160(dev::sha3(bytesConstRef(p.data(), sizeof(p))));
	}
	return m_sender;
}

void Transaction::sign(Secret _priv)
{
	auto sig = dev::sign(_priv, sha3(WithoutSignature));
	SignatureStruct sigStruct = *(SignatureStruct const*)&sig;
	if (sigStruct.isValid())
		m_vrs = sigStruct;
}

void Transaction::streamRLP(RLPStream& _s, IncludeSignature _sig) const
{
	if (m_type == NullTransaction)
		return;
	_s.appendList((_sig ? 3 : 0) + 6);
	_s << m_nonce << m_gasPrice << m_gas;
	if (m_type == MessageCall)
		_s << m_receiveAddress;
	else
		_s << "";
	_s << m_value << m_data;
	if (_sig)
		_s << (m_vrs.v + 27) << (u256)m_vrs.r << (u256)m_vrs.s;
}
<|endoftext|>


<commit_before>#include "AliHFQnVectorHandler.h"

AliAnalysisTaskSEHFTenderQnVectors* AddTaskHFTenderQnVectors(TString taskname = "HFTenderQnVectors",
                                                             TString outputSuffix = "",
                                                             int harmonic = 2,
                                                             int normmethod = 1,//AliHFQnVectorHandler::kQoverM,
                                                             int calibType = 0,//AliHFQnVectorHandler::kQnCalib,
                                                             TString AODBfileName = "")
{
    AliAnalysisManager *mgr = AliAnalysisManager::GetAnalysisManager();
    if (!mgr) {
        ::Error("AliAnalysisTaskSEHFTenderQnVectors", "No analysis manager found.");
        return NULL;
    }
    if (!mgr->GetInputEventHandler()) {
        ::Error("AliAnalysisTaskSEHFTenderQnVectors", "This task requires an input event handler");
        return NULL;
    }

    TString type = mgr->GetInputEventHandler()->GetDataType(); // can be "ESD" or "AOD"
    if(type.Contains("ESD")){
        ::Error("AliAnalysisTaskSEHFTenderQnVectors", "This task requires to run on AOD");
        return NULL;
    }

    //========= Add task for standard analysis to the ANALYSIS manager ====
    AliAnalysisTaskSEHFTenderQnVectors *task = new AliAnalysisTaskSEHFTenderQnVectors(taskname.Data(),harmonic,calibType,AODBfileName);
    task->SetNormalisationMethod(normmethod);
    mgr->AddTask(task);

    TString outputfile = AliAnalysisManager::GetCommonFileName();
    outputfile += ":PWGHF_D2H_QnVectorTender";

    //define input container
    AliAnalysisDataContainer *cinput = mgr->CreateContainer(Form("cinputQnVectorTender%s",outputSuffix.Data()),TChain::Class(),AliAnalysisManager::kInputContainer);
    //define output containers
    AliAnalysisDataContainer *coutput = mgr->CreateContainer(Form("coutputQnVectorTender%s",outputSuffix.Data()), TList::Class(),AliAnalysisManager::kOutputContainer,outputfile.Data());

    //connect containers
    mgr->ConnectInput(task,0,mgr->GetCommonInputContainer());
    mgr->ConnectOutput(task, 1, coutput);
    return task;
}<commit_msg>Remove not-working include<commit_after>AliAnalysisTaskSEHFTenderQnVectors* AddTaskHFTenderQnVectors(TString taskname = "HFTenderQnVectors",
                                                             TString outputSuffix = "",
                                                             int harmonic = 2,
                                                             int normmethod = 1,//AliHFQnVectorHandler::kQoverM,
                                                             int calibType = 0,//AliHFQnVectorHandler::kQnCalib,
                                                             TString AODBfileName = "")
{
    AliAnalysisManager *mgr = AliAnalysisManager::GetAnalysisManager();
    if (!mgr) {
        ::Error("AliAnalysisTaskSEHFTenderQnVectors", "No analysis manager found.");
        return NULL;
    }
    if (!mgr->GetInputEventHandler()) {
        ::Error("AliAnalysisTaskSEHFTenderQnVectors", "This task requires an input event handler");
        return NULL;
    }

    TString type = mgr->GetInputEventHandler()->GetDataType(); // can be "ESD" or "AOD"
    if(type.Contains("ESD")){
        ::Error("AliAnalysisTaskSEHFTenderQnVectors", "This task requires to run on AOD");
        return NULL;
    }

    //========= Add task for standard analysis to the ANALYSIS manager ====
    AliAnalysisTaskSEHFTenderQnVectors *task = new AliAnalysisTaskSEHFTenderQnVectors(taskname.Data(),harmonic,calibType,AODBfileName);
    task->SetNormalisationMethod(normmethod);
    mgr->AddTask(task);

    TString outputfile = AliAnalysisManager::GetCommonFileName();
    outputfile += ":PWGHF_D2H_QnVectorTender";

    //define input container
    AliAnalysisDataContainer *cinput = mgr->CreateContainer(Form("cinputQnVectorTender%s",outputSuffix.Data()),TChain::Class(),AliAnalysisManager::kInputContainer);
    //define output containers
    AliAnalysisDataContainer *coutput = mgr->CreateContainer(Form("coutputQnVectorTender%s",outputSuffix.Data()), TList::Class(),AliAnalysisManager::kOutputContainer,outputfile.Data());

    //connect containers
    mgr->ConnectInput(task,0,mgr->GetCommonInputContainer());
    mgr->ConnectOutput(task, 1, coutput);
    return task;
}<|endoftext|>


<commit_before>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/file_path.h"
#include "base/file_util.h"
#include "base/test/test_timeouts.h"
#include "chrome/test/automation/tab_proxy.h"
#include "chrome/test/base/ui_test_utils.h"
#include "chrome/test/ui/ui_layout_test.h"
#include "content/public/common/content_switches.h"
#include "net/base/net_util.h"

static const char* kRootFiles[] = {
  "clear.html",
//  "complex-keys.html",  // Output too big for a cookie.  crbug.com/33472
//  "complex-values.html",  // crbug.com/33472
  "quota.html",
  "remove-item.html",
  "window-attributes-exist.html",
  NULL
};

static const char* kEventsFiles[] = {
//  "basic-body-attribute.html",  // crbug.com/33472
//  "basic.html",  // crbug.com/33472
//  "basic-setattribute.html",  // crbug.com/33472
  "case-sensitive.html",
  "documentURI.html",
  NULL
};

static const char* kStorageFiles[] = {
  "delete-removal.html",
  "enumerate-storage.html",
  "enumerate-with-length-and-key.html",
  "index-get-and-set.html",
  "simple-usage.html",
  "string-conversion.html",
//  "window-open.html", // TODO(jorlow): Fix
  NULL
};

class DOMStorageTest : public UILayoutTest {
 protected:
  DOMStorageTest()
      : UILayoutTest(),
        test_dir_(FilePath().
                  AppendASCII("storage").AppendASCII("domstorage")) {
  }

  virtual ~DOMStorageTest() { }

  virtual void SetUp() {
    launch_arguments_.AppendSwitch(switches::kDisablePopupBlocking);
    UILayoutTest::SetUp();
  }

  // We require fast/js/resources for most of the DOM Storage layout tests.
  // Add those to the list to be copied.
  void AddJSTestResources() {
    // Add other paths our tests require.
    FilePath js_dir = FilePath().
                      AppendASCII("fast").AppendASCII("js");
    AddResourceForLayoutTest(js_dir, FilePath().AppendASCII("resources"));
  }

  // This is somewhat of a hack because we're running a real browser that
  // actually persists the LocalStorage state vs. DRT and TestShell which don't.
  // The correct fix is to fix the LayoutTests, but similar patches have been
  // rejected in the past.
  void ClearDOMStorage() {
    scoped_refptr<TabProxy> tab(GetActiveTab());
    ASSERT_TRUE(tab.get());

    const FilePath dir(FILE_PATH_LITERAL("layout_tests"));
    const FilePath file(FILE_PATH_LITERAL("clear_dom_storage.html"));
    GURL url = ui_test_utils::GetTestUrl(dir, file);
    ASSERT_TRUE(tab->SetCookie(url, ""));
    ASSERT_TRUE(tab->NavigateToURL(url));

    WaitUntilCookieNonEmpty(tab.get(), url, "cleared",
                            TestTimeouts::action_max_timeout_ms());
  }

  // Runs each test in an array of strings until it hits a NULL.
  void RunTests(const char** files) {
    while (*files) {
      ClearDOMStorage();
      RunLayoutTest(*files, kNoHttpPort);
      ++files;
    }
  }

  FilePath test_dir_;
};

#if defined(OS_WIN)
// http://crbug.com/101996
#define RootLayoutTests FLAKY_RootLayoutTests
#endif
TEST_F(DOMStorageTest, RootLayoutTests) {
  InitializeForLayoutTest(test_dir_, FilePath(), kNoHttpPort);
  AddJSTestResources();
  AddResourceForLayoutTest(test_dir_, FilePath().AppendASCII("script-tests"));
  RunTests(kRootFiles);
}

// Flakily fails on all platforms.  http://crbug.com/102641
TEST_F(DOMStorageTest, FLAKY_EventLayoutTests) {
  InitializeForLayoutTest(test_dir_, FilePath().AppendASCII("events"),
                          kNoHttpPort);
  AddJSTestResources();
  AddResourceForLayoutTest(test_dir_, FilePath().AppendASCII("events").
                                      AppendASCII("resources"));
  AddResourceForLayoutTest(test_dir_, FilePath().AppendASCII("events").
                                      AppendASCII("script-tests"));
  RunTests(kEventsFiles);
}

#if defined(OS_LINUX)
// http://crbug.com/104872
#define MAYBE_LocalStorageLayoutTests FAILS_LocalStorageLayoutTests
#else
#define MAYBE_LocalStorageLayoutTests LocalStorageLayoutTests
#endif

TEST_F(DOMStorageTest, MAYBE_LocalStorageLayoutTests) {
  InitializeForLayoutTest(test_dir_, FilePath().AppendASCII("localstorage"),
                          kNoHttpPort);
  AddJSTestResources();
  AddResourceForLayoutTest(test_dir_, FilePath().AppendASCII("localstorage").
                                      AppendASCII("resources"));
  RunTests(kStorageFiles);
}

#if defined(OS_LINUX)
// http://crbug.com/104872
#define MAYBE_SessionStorageLayoutTests FAILS_SessionStorageLayoutTests
#else
#define MAYBE_SessionStorageLayoutTests SessionStorageLayoutTests
#endif

TEST_F(DOMStorageTest, MAYBE_SessionStorageLayoutTests) {
  InitializeForLayoutTest(test_dir_, FilePath().AppendASCII("sessionstorage"),
                          kNoHttpPort);
  AddJSTestResources();
  AddResourceForLayoutTest(test_dir_, FilePath().AppendASCII("sessionstorage").
                                      AppendASCII("resources"));
  RunTests(kStorageFiles);
}

class DomStorageEmptyDatabaseTest : public UITest {
 protected:
  FilePath StorageDir() const {
    FilePath storage_dir = user_data_dir();
    storage_dir = storage_dir.AppendASCII("Default");
    storage_dir = storage_dir.AppendASCII("Local Storage");
    return storage_dir;
  }

  bool StorageDirIsEmpty() const {
    FilePath storage_dir = StorageDir();
    if (!file_util::DirectoryExists(storage_dir))
      return true;
    return file_util::IsDirectoryEmpty(storage_dir);
  }

  GURL TestUrl() const {
    FilePath test_dir = test_data_directory_;
    FilePath test_file = test_dir.AppendASCII("dom_storage_empty_db.html");
    return net::FilePathToFileURL(test_file);
  }
};

TEST_F(DomStorageEmptyDatabaseTest, EmptyDirAfterClear) {
  NavigateToURL(TestUrl());
  ASSERT_TRUE(StorageDirIsEmpty());

  NavigateToURL(GURL("javascript:set()"));
  NavigateToURL(GURL("javascript:clear()"));
  QuitBrowser();
  EXPECT_TRUE(StorageDirIsEmpty());
}

TEST_F(DomStorageEmptyDatabaseTest, EmptyDirAfterGet) {
  NavigateToURL(TestUrl());
  ASSERT_TRUE(StorageDirIsEmpty());

  NavigateToURL(GURL("javascript:get()"));
  QuitBrowser();
  EXPECT_TRUE(StorageDirIsEmpty());
}

// Flaky, see http://crbug.com/73776
TEST_F(DomStorageEmptyDatabaseTest, FLAKY_NonEmptyDirAfterSet) {
  NavigateToURL(TestUrl());
  ASSERT_TRUE(StorageDirIsEmpty());

  NavigateToURL(GURL("javascript:set()"));
  QuitBrowser();
  EXPECT_FALSE(StorageDirIsEmpty());

  LaunchBrowserAndServer();
  NavigateToURL(TestUrl());
  NavigateToURL(GURL("javascript:clear()"));
  QuitBrowser();
  EXPECT_TRUE(StorageDirIsEmpty());
}
<commit_msg>Mark DOMStorageTest.RootLayoutTests as failing.<commit_after>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/file_path.h"
#include "base/file_util.h"
#include "base/test/test_timeouts.h"
#include "chrome/test/automation/tab_proxy.h"
#include "chrome/test/base/ui_test_utils.h"
#include "chrome/test/ui/ui_layout_test.h"
#include "content/public/common/content_switches.h"
#include "net/base/net_util.h"

static const char* kRootFiles[] = {
  "clear.html",
//  "complex-keys.html",  // Output too big for a cookie.  crbug.com/33472
//  "complex-values.html",  // crbug.com/33472
  "quota.html",
  "remove-item.html",
  "window-attributes-exist.html",
  NULL
};

static const char* kEventsFiles[] = {
//  "basic-body-attribute.html",  // crbug.com/33472
//  "basic.html",  // crbug.com/33472
//  "basic-setattribute.html",  // crbug.com/33472
  "case-sensitive.html",
  "documentURI.html",
  NULL
};

static const char* kStorageFiles[] = {
  "delete-removal.html",
  "enumerate-storage.html",
  "enumerate-with-length-and-key.html",
  "index-get-and-set.html",
  "simple-usage.html",
  "string-conversion.html",
//  "window-open.html", // TODO(jorlow): Fix
  NULL
};

class DOMStorageTest : public UILayoutTest {
 protected:
  DOMStorageTest()
      : UILayoutTest(),
        test_dir_(FilePath().
                  AppendASCII("storage").AppendASCII("domstorage")) {
  }

  virtual ~DOMStorageTest() { }

  virtual void SetUp() {
    launch_arguments_.AppendSwitch(switches::kDisablePopupBlocking);
    UILayoutTest::SetUp();
  }

  // We require fast/js/resources for most of the DOM Storage layout tests.
  // Add those to the list to be copied.
  void AddJSTestResources() {
    // Add other paths our tests require.
    FilePath js_dir = FilePath().
                      AppendASCII("fast").AppendASCII("js");
    AddResourceForLayoutTest(js_dir, FilePath().AppendASCII("resources"));
  }

  // This is somewhat of a hack because we're running a real browser that
  // actually persists the LocalStorage state vs. DRT and TestShell which don't.
  // The correct fix is to fix the LayoutTests, but similar patches have been
  // rejected in the past.
  void ClearDOMStorage() {
    scoped_refptr<TabProxy> tab(GetActiveTab());
    ASSERT_TRUE(tab.get());

    const FilePath dir(FILE_PATH_LITERAL("layout_tests"));
    const FilePath file(FILE_PATH_LITERAL("clear_dom_storage.html"));
    GURL url = ui_test_utils::GetTestUrl(dir, file);
    ASSERT_TRUE(tab->SetCookie(url, ""));
    ASSERT_TRUE(tab->NavigateToURL(url));

    WaitUntilCookieNonEmpty(tab.get(), url, "cleared",
                            TestTimeouts::action_max_timeout_ms());
  }

  // Runs each test in an array of strings until it hits a NULL.
  void RunTests(const char** files) {
    while (*files) {
      ClearDOMStorage();
      RunLayoutTest(*files, kNoHttpPort);
      ++files;
    }
  }

  FilePath test_dir_;
};

// http://crbug.com/113611
TEST_F(DOMStorageTest, FAILS_RootLayoutTests) {
  InitializeForLayoutTest(test_dir_, FilePath(), kNoHttpPort);
  AddJSTestResources();
  AddResourceForLayoutTest(test_dir_, FilePath().AppendASCII("script-tests"));
  RunTests(kRootFiles);
}

// Flakily fails on all platforms.  http://crbug.com/102641
TEST_F(DOMStorageTest, FLAKY_EventLayoutTests) {
  InitializeForLayoutTest(test_dir_, FilePath().AppendASCII("events"),
                          kNoHttpPort);
  AddJSTestResources();
  AddResourceForLayoutTest(test_dir_, FilePath().AppendASCII("events").
                                      AppendASCII("resources"));
  AddResourceForLayoutTest(test_dir_, FilePath().AppendASCII("events").
                                      AppendASCII("script-tests"));
  RunTests(kEventsFiles);
}

#if defined(OS_LINUX)
// http://crbug.com/104872
#define MAYBE_LocalStorageLayoutTests FAILS_LocalStorageLayoutTests
#else
#define MAYBE_LocalStorageLayoutTests LocalStorageLayoutTests
#endif

TEST_F(DOMStorageTest, MAYBE_LocalStorageLayoutTests) {
  InitializeForLayoutTest(test_dir_, FilePath().AppendASCII("localstorage"),
                          kNoHttpPort);
  AddJSTestResources();
  AddResourceForLayoutTest(test_dir_, FilePath().AppendASCII("localstorage").
                                      AppendASCII("resources"));
  RunTests(kStorageFiles);
}

#if defined(OS_LINUX)
// http://crbug.com/104872
#define MAYBE_SessionStorageLayoutTests FAILS_SessionStorageLayoutTests
#else
#define MAYBE_SessionStorageLayoutTests SessionStorageLayoutTests
#endif

TEST_F(DOMStorageTest, MAYBE_SessionStorageLayoutTests) {
  InitializeForLayoutTest(test_dir_, FilePath().AppendASCII("sessionstorage"),
                          kNoHttpPort);
  AddJSTestResources();
  AddResourceForLayoutTest(test_dir_, FilePath().AppendASCII("sessionstorage").
                                      AppendASCII("resources"));
  RunTests(kStorageFiles);
}

class DomStorageEmptyDatabaseTest : public UITest {
 protected:
  FilePath StorageDir() const {
    FilePath storage_dir = user_data_dir();
    storage_dir = storage_dir.AppendASCII("Default");
    storage_dir = storage_dir.AppendASCII("Local Storage");
    return storage_dir;
  }

  bool StorageDirIsEmpty() const {
    FilePath storage_dir = StorageDir();
    if (!file_util::DirectoryExists(storage_dir))
      return true;
    return file_util::IsDirectoryEmpty(storage_dir);
  }

  GURL TestUrl() const {
    FilePath test_dir = test_data_directory_;
    FilePath test_file = test_dir.AppendASCII("dom_storage_empty_db.html");
    return net::FilePathToFileURL(test_file);
  }
};

TEST_F(DomStorageEmptyDatabaseTest, EmptyDirAfterClear) {
  NavigateToURL(TestUrl());
  ASSERT_TRUE(StorageDirIsEmpty());

  NavigateToURL(GURL("javascript:set()"));
  NavigateToURL(GURL("javascript:clear()"));
  QuitBrowser();
  EXPECT_TRUE(StorageDirIsEmpty());
}

TEST_F(DomStorageEmptyDatabaseTest, EmptyDirAfterGet) {
  NavigateToURL(TestUrl());
  ASSERT_TRUE(StorageDirIsEmpty());

  NavigateToURL(GURL("javascript:get()"));
  QuitBrowser();
  EXPECT_TRUE(StorageDirIsEmpty());
}

// Flaky, see http://crbug.com/73776
TEST_F(DomStorageEmptyDatabaseTest, FLAKY_NonEmptyDirAfterSet) {
  NavigateToURL(TestUrl());
  ASSERT_TRUE(StorageDirIsEmpty());

  NavigateToURL(GURL("javascript:set()"));
  QuitBrowser();
  EXPECT_FALSE(StorageDirIsEmpty());

  LaunchBrowserAndServer();
  NavigateToURL(TestUrl());
  NavigateToURL(GURL("javascript:clear()"));
  QuitBrowser();
  EXPECT_TRUE(StorageDirIsEmpty());
}
<|endoftext|>


<commit_before>#include "Version-inl.h"

#include "gtest/gtest.h"

#include <utility>

using namespace Arbiter;

TEST(VersionTest, Initializes) {
  ArbiterSemanticVersion version(1, 0, 2);
  EXPECT_EQ(version._major, 1);
  EXPECT_EQ(version._minor, 0);
  EXPECT_EQ(version._patch, 2);
  EXPECT_EQ(version._prereleaseVersion.pointer(), nullptr);
  EXPECT_EQ(version._buildMetadata.pointer(), nullptr);
}

TEST(VersionTest, ParsesSimpleVersions) {
  EXPECT_EQ(ArbiterSemanticVersion::fromString("0.0.0").value(), ArbiterSemanticVersion(0, 0, 0));
  EXPECT_EQ(ArbiterSemanticVersion::fromString("1.0.2").value(), ArbiterSemanticVersion(1, 0, 2));
  EXPECT_EQ(ArbiterSemanticVersion::fromString("12.345.6789").value(), ArbiterSemanticVersion(12, 345, 6789));
}

TEST(VersionTest, ParsesPrereleaseVersion) {
  EXPECT_EQ(ArbiterSemanticVersion::fromString("1.0.2-alpha.1").value(), ArbiterSemanticVersion(1, 0, 2, Optional<std::string>("alpha.1")));
}

TEST(VersionTest, ParsesBuildMetadata) {
  EXPECT_EQ(ArbiterSemanticVersion::fromString("1.0.2+dailybuild").value(), ArbiterSemanticVersion(1, 0, 2, Optional<std::string>(), Optional<std::string>("dailybuild")));
}

TEST(VersionTest, ParsesPrereleaseVersionAndBuildMetadata) {
  EXPECT_EQ(ArbiterSemanticVersion::fromString("1.0.2-alpha.1+dailybuild").value(), ArbiterSemanticVersion(1, 0, 2, Optional<std::string>("alpha.1"), Optional<std::string>("dailybuild")));
}

TEST(VersionTest, FailsToParseMalformedVersions) {
  EXPECT_EQ(ArbiterSemanticVersion::fromString("0").pointer(), nullptr);
  EXPECT_EQ(ArbiterSemanticVersion::fromString("1.0").pointer(), nullptr);
  EXPECT_EQ(ArbiterSemanticVersion::fromString("-1.0.0").pointer(), nullptr);
  EXPECT_EQ(ArbiterSemanticVersion::fromString("01.0.0").pointer(), nullptr);
  EXPECT_EQ(ArbiterSemanticVersion::fromString("1.0.0a1").pointer(), nullptr);
  EXPECT_EQ(ArbiterSemanticVersion::fromString("1.0.0-alpha.01").pointer(), nullptr);
  EXPECT_EQ(ArbiterSemanticVersion::fromString("1.0.0-alpha$1").pointer(), nullptr);
  EXPECT_EQ(ArbiterSemanticVersion::fromString("1.0.0+build$1").pointer(), nullptr);
}

TEST(VersionTest, ComparesForEquality) {
  EXPECT_EQ(ArbiterSemanticVersion(0, 0, 0), ArbiterSemanticVersion(0, 0, 0));
  EXPECT_EQ(ArbiterSemanticVersion(1, 2, 3), ArbiterSemanticVersion(1, 2, 3));
  EXPECT_NE(ArbiterSemanticVersion(2, 3, 4), ArbiterSemanticVersion(1, 2, 3));
  EXPECT_NE(ArbiterSemanticVersion(1, 2, 3, Optional<std::string>("alpha.1")), ArbiterSemanticVersion(1, 2, 3, Optional<std::string>()));
  EXPECT_EQ(ArbiterSemanticVersion(1, 2, 3, Optional<std::string>("alpha.1")), ArbiterSemanticVersion(1, 2, 3, Optional<std::string>("alpha.1")));
  EXPECT_EQ(ArbiterSemanticVersion(1, 2, 3, Optional<std::string>("alpha.1"), Optional<std::string>("dailybuild")), ArbiterSemanticVersion(1, 2, 3, Optional<std::string>("alpha.1"), Optional<std::string>("dailybuild")));
  EXPECT_NE(ArbiterSemanticVersion(1, 2, 3, Optional<std::string>("alpha.1"), Optional<std::string>("dailybuild")), ArbiterSemanticVersion(1, 2, 3, Optional<std::string>(), Optional<std::string>("dailybuild")));
  EXPECT_NE(ArbiterSemanticVersion(1, 2, 3, Optional<std::string>("alpha.1"), Optional<std::string>("dailybuild")), ArbiterSemanticVersion(1, 2, 3, Optional<std::string>("alpha.1"), Optional<std::string>()));
  EXPECT_NE(ArbiterSemanticVersion(1, 2, 3, Optional<std::string>("alpha.1"), Optional<std::string>("dailybuild")), ArbiterSemanticVersion(1, 2, 3, Optional<std::string>("alpha.2"), Optional<std::string>("dailybuild")));
}

TEST(VersionTest, OrdersByPrecedence) {
  EXPECT_LT(ArbiterSemanticVersion(1, 2, 3), ArbiterSemanticVersion(1, 2, 4));
  EXPECT_LT(ArbiterSemanticVersion(1, 2, 3), ArbiterSemanticVersion(1, 3, 0));
  EXPECT_LT(ArbiterSemanticVersion(1, 2, 3), ArbiterSemanticVersion(2, 0, 0));
  EXPECT_LT(ArbiterSemanticVersion(1, 2, 3, Optional<std::string>("alpha.1")), ArbiterSemanticVersion(1, 2, 3));
  EXPECT_GE(ArbiterSemanticVersion(1, 2, 3, Optional<std::string>("alpha.2")), ArbiterSemanticVersion(1, 2, 3, Optional<std::string>("alpha.1")));
  EXPECT_GE(ArbiterSemanticVersion(1, 2, 3, Optional<std::string>(), Optional<std::string>("dailybuild")), ArbiterSemanticVersion(1, 2, 3));
}
<commit_msg>Add test converting version to a string<commit_after>#include "Version-inl.h"

#include "gtest/gtest.h"

#include <sstream>
#include <utility>

using namespace Arbiter;

TEST(VersionTest, Initializes) {
  ArbiterSemanticVersion version(1, 0, 2);
  EXPECT_EQ(version._major, 1);
  EXPECT_EQ(version._minor, 0);
  EXPECT_EQ(version._patch, 2);
  EXPECT_EQ(version._prereleaseVersion.pointer(), nullptr);
  EXPECT_EQ(version._buildMetadata.pointer(), nullptr);
}

TEST(VersionTest, ParsesSimpleVersions) {
  EXPECT_EQ(ArbiterSemanticVersion::fromString("0.0.0").value(), ArbiterSemanticVersion(0, 0, 0));
  EXPECT_EQ(ArbiterSemanticVersion::fromString("1.0.2").value(), ArbiterSemanticVersion(1, 0, 2));
  EXPECT_EQ(ArbiterSemanticVersion::fromString("12.345.6789").value(), ArbiterSemanticVersion(12, 345, 6789));
}

TEST(VersionTest, ParsesPrereleaseVersion) {
  EXPECT_EQ(ArbiterSemanticVersion::fromString("1.0.2-alpha.1").value(), ArbiterSemanticVersion(1, 0, 2, Optional<std::string>("alpha.1")));
}

TEST(VersionTest, ParsesBuildMetadata) {
  EXPECT_EQ(ArbiterSemanticVersion::fromString("1.0.2+dailybuild").value(), ArbiterSemanticVersion(1, 0, 2, Optional<std::string>(), Optional<std::string>("dailybuild")));
}

TEST(VersionTest, ParsesPrereleaseVersionAndBuildMetadata) {
  EXPECT_EQ(ArbiterSemanticVersion::fromString("1.0.2-alpha.1+dailybuild").value(), ArbiterSemanticVersion(1, 0, 2, Optional<std::string>("alpha.1"), Optional<std::string>("dailybuild")));
}

TEST(VersionTest, FailsToParseMalformedVersions) {
  EXPECT_EQ(ArbiterSemanticVersion::fromString("0").pointer(), nullptr);
  EXPECT_EQ(ArbiterSemanticVersion::fromString("1.0").pointer(), nullptr);
  EXPECT_EQ(ArbiterSemanticVersion::fromString("-1.0.0").pointer(), nullptr);
  EXPECT_EQ(ArbiterSemanticVersion::fromString("01.0.0").pointer(), nullptr);
  EXPECT_EQ(ArbiterSemanticVersion::fromString("1.0.0a1").pointer(), nullptr);
  EXPECT_EQ(ArbiterSemanticVersion::fromString("1.0.0-alpha.01").pointer(), nullptr);
  EXPECT_EQ(ArbiterSemanticVersion::fromString("1.0.0-alpha$1").pointer(), nullptr);
  EXPECT_EQ(ArbiterSemanticVersion::fromString("1.0.0+build$1").pointer(), nullptr);
}

TEST(VersionTest, ComparesForEquality) {
  EXPECT_EQ(ArbiterSemanticVersion(0, 0, 0), ArbiterSemanticVersion(0, 0, 0));
  EXPECT_EQ(ArbiterSemanticVersion(1, 2, 3), ArbiterSemanticVersion(1, 2, 3));
  EXPECT_NE(ArbiterSemanticVersion(2, 3, 4), ArbiterSemanticVersion(1, 2, 3));
  EXPECT_NE(ArbiterSemanticVersion(1, 2, 3, Optional<std::string>("alpha.1")), ArbiterSemanticVersion(1, 2, 3, Optional<std::string>()));
  EXPECT_EQ(ArbiterSemanticVersion(1, 2, 3, Optional<std::string>("alpha.1")), ArbiterSemanticVersion(1, 2, 3, Optional<std::string>("alpha.1")));
  EXPECT_EQ(ArbiterSemanticVersion(1, 2, 3, Optional<std::string>("alpha.1"), Optional<std::string>("dailybuild")), ArbiterSemanticVersion(1, 2, 3, Optional<std::string>("alpha.1"), Optional<std::string>("dailybuild")));
  EXPECT_NE(ArbiterSemanticVersion(1, 2, 3, Optional<std::string>("alpha.1"), Optional<std::string>("dailybuild")), ArbiterSemanticVersion(1, 2, 3, Optional<std::string>(), Optional<std::string>("dailybuild")));
  EXPECT_NE(ArbiterSemanticVersion(1, 2, 3, Optional<std::string>("alpha.1"), Optional<std::string>("dailybuild")), ArbiterSemanticVersion(1, 2, 3, Optional<std::string>("alpha.1"), Optional<std::string>()));
  EXPECT_NE(ArbiterSemanticVersion(1, 2, 3, Optional<std::string>("alpha.1"), Optional<std::string>("dailybuild")), ArbiterSemanticVersion(1, 2, 3, Optional<std::string>("alpha.2"), Optional<std::string>("dailybuild")));
}

TEST(VersionTest, ConvertsToString) {
  std::stringstream stream;
  stream << ArbiterSemanticVersion(1, 2, 3, Optional<std::string>("alpha.1"), Optional<std::string>("dailybuild"));
  EXPECT_EQ(stream.str(), "1.2.3-alpha.1+dailybuild");
}
<|endoftext|>


<commit_before>///	@brief 		Unit test for the circular storage container class
///	@author		Timothy Place, Nathan Wolek
//	@copyright	Copyright (c) 2017, Cycling '74
///	@license	This project is released under the terms of the MIT License.

#define CATCH_CONFIG_MAIN
#include "c74_min_catch.h"

TEST_CASE ("Basic usage of the Circular Storage class") {
	INFO ("Using an 8-sample circular buffer")
	c74::min::lib::circular_storage<c74::min::sample>	circ(8);	// 8 samples
	c74::min::sample_vector								samples = {1,2,3,4,5};
	c74::min::sample_vector								head_samples(3);
	c74::min::sample_vector								tail_samples(3);

	INFO("When writing 5 samples into the 8 sample buffer the first time")
	circ.write(samples);
	INFO("Those 5 samples occupy the first 5 slots and the other slots are still initialized to 0.");
	REQUIRE( circ.item(0) == 1 );
	REQUIRE( circ.item(1) == 2 );
	REQUIRE( circ.item(2) == 3 );
	REQUIRE( circ.item(3) == 4 );
	REQUIRE( circ.item(4) == 5 );
	REQUIRE( circ.item(5) == 0 );
	REQUIRE( circ.item(6) == 0 );
	REQUIRE( circ.item(7) == 0 );

	INFO("When writing 5 more samples into the 8 sample buffer")
	samples = {6,7,8,9,10};
	circ.write(samples);
	INFO("Those samples begin writing where the previous write left off, and they wrap around and overwrite the beginning (oldest) samples.");
	REQUIRE(  circ.item(0) == 9 );
	REQUIRE(  circ.item(1) == 10 );
	REQUIRE(  circ.item(2) == 3 );
	REQUIRE(  circ.item(3) == 4 );
	REQUIRE(  circ.item(4) == 5 );
	REQUIRE(  circ.item(5) == 6 );
	REQUIRE(  circ.item(6) == 7 );
	REQUIRE(  circ.item(7) == 8 );

	INFO("When reading 3 samples from the head with no previous reads")
	circ.head(head_samples);
	INFO("We get the most recently written 3 sample values");
	REQUIRE( head_samples[0] == 8 );
	REQUIRE( head_samples[1] == 9 );
	REQUIRE( head_samples[2] == 10 );

	INFO("When we then read 3 samples from the tail")
	circ.tail(tail_samples);
	INFO("We get the oldest 3 sample values that were written");
	REQUIRE( tail_samples[0] == 3 );
	REQUIRE( tail_samples[1] == 4 );
	REQUIRE( tail_samples[2] == 5 );

	INFO("When we read 3 more samples from the head")
	circ.head(head_samples);
	INFO("We get the most recently written 3 sample values, just as before -- nothing has changed");
	REQUIRE( head_samples[0] == 8 );
	REQUIRE( head_samples[1] == 9 );
	REQUIRE( head_samples[2] == 10 );

	INFO("And when we then read 3 more samples from the tail")
	circ.tail(tail_samples);
	INFO("We still get the oldest 3 sample values that were written -- nothing has changed");
	REQUIRE( tail_samples[0] == 3 );
	REQUIRE( tail_samples[1] == 4 );
	REQUIRE( tail_samples[2] == 5 );

	INFO("We then WRITE 3 more samples")
	samples = {20, 21, 22};
	circ.write(samples);

	INFO("And the internal storage is updated with the new items in the correct locations, overwriting the 3 oldest values");
	REQUIRE( circ.item(0) == 9 );
	REQUIRE( circ.item(1) == 10 );
	REQUIRE( circ.item(2) == 20 );
	REQUIRE( circ.item(3) == 21 );
	REQUIRE( circ.item(4) == 22 );
	REQUIRE( circ.item(5) == 6 );
	REQUIRE( circ.item(6) == 7 );
	REQUIRE( circ.item(7) == 8 );

	INFO("If we read 3 samples from the head now")
	circ.head(head_samples);
	INFO("We get the 3 sample values we just wrote");
	REQUIRE( head_samples[0] == 20 );
	REQUIRE( head_samples[1] == 21 );
	REQUIRE( head_samples[2] == 22 );

	INFO("And when we read 3 more samples from the tail")
	circ.tail(tail_samples);
	INFO("We now have advanced 3 samples in the buffer since the last time we read the tail, because 3 items were written since then.");
	REQUIRE( tail_samples[0] == 6 );
	REQUIRE( tail_samples[1] == 7 );
	REQUIRE( tail_samples[2] == 8 );

	INFO("If we then WRITE another 3 samples")
	samples = {100, 101, 102};
	circ.write(samples);
	INFO("The internal storage is updated with the new items in the correct locations, overwriting the 3 oldest values");
	REQUIRE( circ.item(0) == 9 );
	REQUIRE( circ.item(1) == 10 );
	REQUIRE( circ.item(2) == 20 );
	REQUIRE( circ.item(3) == 21 );
	REQUIRE( circ.item(4) == 22 );
	REQUIRE( circ.item(5) == 100 );
	REQUIRE( circ.item(6) == 101 );
	REQUIRE( circ.item(7) == 102 );

	INFO("When we read 3 samples from the head now")
	circ.head(head_samples);
	INFO("We get the 3 sample values we just wrote");
	REQUIRE( head_samples[0] == 100 );
	REQUIRE( head_samples[1] == 101 );
	REQUIRE( head_samples[2] == 102 );

	INFO("And when read 3 more samples from the tail")
	circ.tail(tail_samples);
	INFO("We now have advanced 3 samples in the buffer since the last time we read the tail, because 3 items were written since then.");
	REQUIRE( tail_samples[0] == 9 );
	REQUIRE( tail_samples[1] == 10 );
	REQUIRE( tail_samples[2] == 20 );

	INFO("If we read 5 samples from the head now, having not written since the last time")
	head_samples.resize(5);
	tail_samples.resize(5);
	circ.head(head_samples);
	INFO("We get the 5 newest sample values, which includes the same 3 we got the last time we read.");
	REQUIRE( head_samples[0] == 21 );
	REQUIRE( head_samples[1] == 22 );
	REQUIRE( head_samples[2] == 100 );
	REQUIRE( head_samples[3] == 101 );
	REQUIRE( head_samples[4] == 102 );

	INFO("And when we read 5 samples from the tail")
	circ.tail(tail_samples);
	INFO("We read the fove oldest sample values, which includes the same 3 oldest values we got the last time we read the tail.");
	REQUIRE( tail_samples[0] == 9 );
	REQUIRE( tail_samples[1] == 10 );
	REQUIRE( tail_samples[2] == 20 );
	REQUIRE( tail_samples[3] == 21 );
	REQUIRE( tail_samples[4] == 22 );
}

TEST_CASE ("Using Circular Storage as the basis for an Audio Delay") {
	INFO("Using a 16-sample circular buffer and a processing vector-size of 4")

	c74::min::lib::circular_storage<c74::min::sample>	circ(16);
	c74::min::sample_vector								samples(4);
	c74::min::sample_vector								output(4);

	INFO("The default is that delay will be the size of the capacity of the buffer (16 samples)");
	REQUIRE(circ.size() == 16);

	INFO("When Reading from the delay line the first time (first 4 vectors)")
	INFO("The first 16 samples (4 vectors) will be zero because we read from it before we write into it");

	circ.tail(output);
	REQUIRE( output[0] == 0 );
	REQUIRE( output[1] == 0 );
	REQUIRE( output[2] == 0 );
	REQUIRE( output[3] == 0 );
	samples = {1,2,3,4};
	circ.write(samples);

	circ.tail(output);
	REQUIRE( output[0] == 0 );
	REQUIRE( output[1] == 0 );
	REQUIRE( output[2] == 0 );
	REQUIRE( output[3] == 0 );
	samples = {5,6,7,8};
	circ.write(samples);

	circ.tail(output);
	REQUIRE( output[0] == 0 );
	REQUIRE( output[1] == 0 );
	REQUIRE( output[2] == 0 );
	REQUIRE( output[3] == 0 );
	samples = {9,10,11,12};
	circ.write(samples);

	circ.tail(output);
	REQUIRE( output[0] == 0 );
	REQUIRE( output[1] == 0 );
	REQUIRE( output[2] == 0 );
	REQUIRE( output[3] == 0 );
	samples = {13,14,15,16};
	circ.write(samples);

	INFO("When Reading from the delay line for the 17th sample")
	INFO("The tail should produce what happened 16 samples ago (our delay time is 16 samples): 1,2,3,4");

	circ.tail(output);
	REQUIRE( output[0] == 1 );
	REQUIRE( output[1] == 2 );
	REQUIRE( output[2] == 3 );
	REQUIRE( output[3] == 4 );
	samples = {17,18,19,20};
	circ.write(samples);

	circ.tail(output);
	REQUIRE( output[0] == 5 );
	REQUIRE( output[1] == 6 );
	REQUIRE( output[2] == 7 );
	REQUIRE( output[3] == 8 );
	samples = {21,22,23,24};
	circ.write(samples);

	INFO("We change the delay time from 16-samples to 10-samples")
	circ.resize(10);
	circ.tail(output);

	INFO("if tail produced what happened 10 samples ago it would be: 15,16,17,18 -- but we just shortened the delay, which means some samples are going to get dropped");
	REQUIRE( output[0] == 9 );
	REQUIRE( output[1] == 10 );
	REQUIRE( output[2] == 17 );
	REQUIRE( output[3] == 18 );
	samples = {25,26,27,28};
	circ.write(samples);

	INFO("When we process the next vector after the delay time change")
	INFO("tail produces what happened 10 samples ago: 19,20,21,22");
	circ.tail(output);
	REQUIRE( output[0] == 19 );
	REQUIRE( output[1] == 20 );
	REQUIRE( output[2] == 21 );
	REQUIRE( output[3] == 22 );
	samples = {29,30,31,32};
	circ.write(samples);
}

TEST_CASE ("Test that clear() does not resize Circular Storage") {
    INFO ("Using an 8-sample circular buffer")
    c74::min::lib::circular_storage<c74::min::sample>	circ(8);	// 8 samples

    REQUIRE( circ.size() == 8 );

    circ.clear();

    REQUIRE( circ.size() == 8 );

}
<commit_msg>circular_storage: creating tests for clear() and zero(). See Cycling74/min-lib/pull #21<commit_after>///	@brief 		Unit test for the circular storage container class
///	@author		Timothy Place, Nathan Wolek
//	@copyright	Copyright (c) 2017, Cycling '74
///	@license	This project is released under the terms of the MIT License.

#define CATCH_CONFIG_MAIN
#include "c74_min_catch.h"

TEST_CASE ("Basic usage of the Circular Storage class") {
	INFO ("Using an 8-sample circular buffer")
	c74::min::lib::circular_storage<c74::min::sample>	circ(8);	// 8 samples
	c74::min::sample_vector								samples = {1,2,3,4,5};
	c74::min::sample_vector								head_samples(3);
	c74::min::sample_vector								tail_samples(3);

	INFO("When writing 5 samples into the 8 sample buffer the first time")
	circ.write(samples);
	INFO("Those 5 samples occupy the first 5 slots and the other slots are still initialized to 0.");
	REQUIRE( circ.item(0) == 1 );
	REQUIRE( circ.item(1) == 2 );
	REQUIRE( circ.item(2) == 3 );
	REQUIRE( circ.item(3) == 4 );
	REQUIRE( circ.item(4) == 5 );
	REQUIRE( circ.item(5) == 0 );
	REQUIRE( circ.item(6) == 0 );
	REQUIRE( circ.item(7) == 0 );

	INFO("When writing 5 more samples into the 8 sample buffer")
	samples = {6,7,8,9,10};
	circ.write(samples);
	INFO("Those samples begin writing where the previous write left off, and they wrap around and overwrite the beginning (oldest) samples.");
	REQUIRE(  circ.item(0) == 9 );
	REQUIRE(  circ.item(1) == 10 );
	REQUIRE(  circ.item(2) == 3 );
	REQUIRE(  circ.item(3) == 4 );
	REQUIRE(  circ.item(4) == 5 );
	REQUIRE(  circ.item(5) == 6 );
	REQUIRE(  circ.item(6) == 7 );
	REQUIRE(  circ.item(7) == 8 );

	INFO("When reading 3 samples from the head with no previous reads")
	circ.head(head_samples);
	INFO("We get the most recently written 3 sample values");
	REQUIRE( head_samples[0] == 8 );
	REQUIRE( head_samples[1] == 9 );
	REQUIRE( head_samples[2] == 10 );

	INFO("When we then read 3 samples from the tail")
	circ.tail(tail_samples);
	INFO("We get the oldest 3 sample values that were written");
	REQUIRE( tail_samples[0] == 3 );
	REQUIRE( tail_samples[1] == 4 );
	REQUIRE( tail_samples[2] == 5 );

	INFO("When we read 3 more samples from the head")
	circ.head(head_samples);
	INFO("We get the most recently written 3 sample values, just as before -- nothing has changed");
	REQUIRE( head_samples[0] == 8 );
	REQUIRE( head_samples[1] == 9 );
	REQUIRE( head_samples[2] == 10 );

	INFO("And when we then read 3 more samples from the tail")
	circ.tail(tail_samples);
	INFO("We still get the oldest 3 sample values that were written -- nothing has changed");
	REQUIRE( tail_samples[0] == 3 );
	REQUIRE( tail_samples[1] == 4 );
	REQUIRE( tail_samples[2] == 5 );

	INFO("We then WRITE 3 more samples")
	samples = {20, 21, 22};
	circ.write(samples);

	INFO("And the internal storage is updated with the new items in the correct locations, overwriting the 3 oldest values");
	REQUIRE( circ.item(0) == 9 );
	REQUIRE( circ.item(1) == 10 );
	REQUIRE( circ.item(2) == 20 );
	REQUIRE( circ.item(3) == 21 );
	REQUIRE( circ.item(4) == 22 );
	REQUIRE( circ.item(5) == 6 );
	REQUIRE( circ.item(6) == 7 );
	REQUIRE( circ.item(7) == 8 );

	INFO("If we read 3 samples from the head now")
	circ.head(head_samples);
	INFO("We get the 3 sample values we just wrote");
	REQUIRE( head_samples[0] == 20 );
	REQUIRE( head_samples[1] == 21 );
	REQUIRE( head_samples[2] == 22 );

	INFO("And when we read 3 more samples from the tail")
	circ.tail(tail_samples);
	INFO("We now have advanced 3 samples in the buffer since the last time we read the tail, because 3 items were written since then.");
	REQUIRE( tail_samples[0] == 6 );
	REQUIRE( tail_samples[1] == 7 );
	REQUIRE( tail_samples[2] == 8 );

	INFO("If we then WRITE another 3 samples")
	samples = {100, 101, 102};
	circ.write(samples);
	INFO("The internal storage is updated with the new items in the correct locations, overwriting the 3 oldest values");
	REQUIRE( circ.item(0) == 9 );
	REQUIRE( circ.item(1) == 10 );
	REQUIRE( circ.item(2) == 20 );
	REQUIRE( circ.item(3) == 21 );
	REQUIRE( circ.item(4) == 22 );
	REQUIRE( circ.item(5) == 100 );
	REQUIRE( circ.item(6) == 101 );
	REQUIRE( circ.item(7) == 102 );

	INFO("When we read 3 samples from the head now")
	circ.head(head_samples);
	INFO("We get the 3 sample values we just wrote");
	REQUIRE( head_samples[0] == 100 );
	REQUIRE( head_samples[1] == 101 );
	REQUIRE( head_samples[2] == 102 );

	INFO("And when read 3 more samples from the tail")
	circ.tail(tail_samples);
	INFO("We now have advanced 3 samples in the buffer since the last time we read the tail, because 3 items were written since then.");
	REQUIRE( tail_samples[0] == 9 );
	REQUIRE( tail_samples[1] == 10 );
	REQUIRE( tail_samples[2] == 20 );

	INFO("If we read 5 samples from the head now, having not written since the last time")
	head_samples.resize(5);
	tail_samples.resize(5);
	circ.head(head_samples);
	INFO("We get the 5 newest sample values, which includes the same 3 we got the last time we read.");
	REQUIRE( head_samples[0] == 21 );
	REQUIRE( head_samples[1] == 22 );
	REQUIRE( head_samples[2] == 100 );
	REQUIRE( head_samples[3] == 101 );
	REQUIRE( head_samples[4] == 102 );

	INFO("And when we read 5 samples from the tail")
	circ.tail(tail_samples);
	INFO("We read the fove oldest sample values, which includes the same 3 oldest values we got the last time we read the tail.");
	REQUIRE( tail_samples[0] == 9 );
	REQUIRE( tail_samples[1] == 10 );
	REQUIRE( tail_samples[2] == 20 );
	REQUIRE( tail_samples[3] == 21 );
	REQUIRE( tail_samples[4] == 22 );
}

TEST_CASE ("Using Circular Storage as the basis for an Audio Delay") {
	INFO("Using a 16-sample circular buffer and a processing vector-size of 4")

	c74::min::lib::circular_storage<c74::min::sample>	circ(16);
	c74::min::sample_vector								samples(4);
	c74::min::sample_vector								output(4);

	INFO("The default is that delay will be the size of the capacity of the buffer (16 samples)");
	REQUIRE(circ.size() == 16);

	INFO("When Reading from the delay line the first time (first 4 vectors)")
	INFO("The first 16 samples (4 vectors) will be zero because we read from it before we write into it");

	circ.tail(output);
	REQUIRE( output[0] == 0 );
	REQUIRE( output[1] == 0 );
	REQUIRE( output[2] == 0 );
	REQUIRE( output[3] == 0 );
	samples = {1,2,3,4};
	circ.write(samples);

	circ.tail(output);
	REQUIRE( output[0] == 0 );
	REQUIRE( output[1] == 0 );
	REQUIRE( output[2] == 0 );
	REQUIRE( output[3] == 0 );
	samples = {5,6,7,8};
	circ.write(samples);

	circ.tail(output);
	REQUIRE( output[0] == 0 );
	REQUIRE( output[1] == 0 );
	REQUIRE( output[2] == 0 );
	REQUIRE( output[3] == 0 );
	samples = {9,10,11,12};
	circ.write(samples);

	circ.tail(output);
	REQUIRE( output[0] == 0 );
	REQUIRE( output[1] == 0 );
	REQUIRE( output[2] == 0 );
	REQUIRE( output[3] == 0 );
	samples = {13,14,15,16};
	circ.write(samples);

	INFO("When Reading from the delay line for the 17th sample")
	INFO("The tail should produce what happened 16 samples ago (our delay time is 16 samples): 1,2,3,4");

	circ.tail(output);
	REQUIRE( output[0] == 1 );
	REQUIRE( output[1] == 2 );
	REQUIRE( output[2] == 3 );
	REQUIRE( output[3] == 4 );
	samples = {17,18,19,20};
	circ.write(samples);

	circ.tail(output);
	REQUIRE( output[0] == 5 );
	REQUIRE( output[1] == 6 );
	REQUIRE( output[2] == 7 );
	REQUIRE( output[3] == 8 );
	samples = {21,22,23,24};
	circ.write(samples);

	INFO("We change the delay time from 16-samples to 10-samples")
	circ.resize(10);
	circ.tail(output);

	INFO("if tail produced what happened 10 samples ago it would be: 15,16,17,18 -- but we just shortened the delay, which means some samples are going to get dropped");
	REQUIRE( output[0] == 9 );
	REQUIRE( output[1] == 10 );
	REQUIRE( output[2] == 17 );
	REQUIRE( output[3] == 18 );
	samples = {25,26,27,28};
	circ.write(samples);

	INFO("When we process the next vector after the delay time change")
	INFO("tail produces what happened 10 samples ago: 19,20,21,22");
	circ.tail(output);
	REQUIRE( output[0] == 19 );
	REQUIRE( output[1] == 20 );
	REQUIRE( output[2] == 21 );
	REQUIRE( output[3] == 22 );
	samples = {29,30,31,32};
	circ.write(samples);
}

TEST_CASE ("Test zero() and clear() functions of Circular Storage") {
    INFO ("Using an 8-sample circular buffer")
    c74::min::lib::circular_storage<c74::min::sample>	circ(8);	// 8 samples
    c74::min::sample_vector								samples = {1,2,3,4,5,6,7,8};

    INFO("The default size will be the capacity of the circular buffer (8 samples)");
    REQUIRE( circ.size() == 8 );

    INFO("After we write in 8 values, the internal storage is updated with the new items in the correct locations");
    circ.write(samples);
    REQUIRE( circ.item(0) == 1 );
    REQUIRE( circ.item(1) == 2 );
    REQUIRE( circ.item(2) == 3 );
    REQUIRE( circ.item(3) == 4 );
    REQUIRE( circ.item(4) == 5 );
    REQUIRE( circ.item(5) == 6 );
    REQUIRE( circ.item(6) == 7 );
    REQUIRE( circ.item(7) == 8 );

    INFO("After we call zero(), the size of the internal storage in unchanged...");
    circ.zero();
    REQUIRE( circ.size() == 8 );

    INFO("...but all values have been set to zero");
    REQUIRE( circ.item(0) == 0 );
    REQUIRE( circ.item(1) == 0 );
    REQUIRE( circ.item(2) == 0 );
    REQUIRE( circ.item(3) == 0 );
    REQUIRE( circ.item(4) == 0 );
    REQUIRE( circ.item(5) == 0 );
    REQUIRE( circ.item(6) == 0 );
    REQUIRE( circ.item(7) == 0 );

    INFO("After we call clear(), the internal storage has a size of 0");
    circ.clear();
    REQUIRE( circ.size() == 0 );

}
<|endoftext|>