<commit_before>/** \file    create_author_synomym_map.cc
 *  \brief   Creates a SOLR-MARC syonym map for authors from norm data.
 *  \author  Dr. Johannes Ruscheinski
 */

/*
    Copyright (C) 2016, Library of the University of Tübingen

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

    This program 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 Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <iostream>
#include <vector>
#include <cstdlib>
#include "Compiler.h"
#include "MarcUtil.h"
#include "MediaTypeUtil.h"
#include "StringUtil.h"
#include "util.h"

void Usage() {
    std::cerr << "Usage: " << ::progname << " norm_data_marc_input synonym_map_output\n";
    std::exit(EXIT_FAILURE);
}

void RemoveCommasDuplicatesAndEmptyEntries(std::vector<std::string> * const vector) {
    std::vector<std::string> cleaned_up_vector;
    std::set<std::string> uniqe_entries;

    for (auto &entry : *vector) {
        std::cout << "- " << entry << "\n";
	StringUtil::RemoveChars(",", &entry);

        if (entry.empty())
            continue;

        const bool is_new_entry(uniqe_entries.emplace(entry).second);
        if (is_new_entry)
            cleaned_up_vector.emplace_back(std::move(entry));
    }

    for (auto entry : cleaned_up_vector) {
        std::cout << "+ " << entry << "\n";
    }

    vector->swap(cleaned_up_vector);
}

std::string ExtractName(const MarcUtil::Record &record, const std::string &field) {
    std::vector<std::string> subfield_values;

    record.extractSubfields(field.substr(0, 3), field.substr(3), &subfield_values);
    RemoveCommasDuplicatesAndEmptyEntries(&subfield_values);

    if (subfield_values.empty())
        return "";

    std::sort(subfield_values.begin(), subfield_values.end());
    return StringUtil::Join(subfield_values, ' ');
}

void ExtractSynonymsAndWriteSynonymMap(File * const marc_input, File * const synonym_output, const std::string &field_list) {
    std::set<std::string> synonyms;
    std::vector<std::string> fields;
    if (unlikely(StringUtil::Split(field_list, ':', &fields) < 2))
	Error("in ExtractSynonymsAndWriteSynonymMap: need at least two fields!");

    unsigned synomym_line_count(0), count(0);
    while (const MarcUtil::Record record = MarcUtil::Record::XmlFactory(marc_input)) {
        ++count;
	if (record.getFieldIndex(fields[0].substr(0, 3)) == -1)
	    continue;

	std::vector<std::string> alternatives;
	for (const auto &field : fields)
            alternatives.emplace_back(ExtractName(record, field));

        RemoveCommasDuplicatesAndEmptyEntries(&alternatives);
        if (alternatives.size() <= 1)
            continue;

        std::string synonym(StringUtil::Join(alternatives, ','));

        const bool is_new_entry(synonyms.emplace(synonym).second);
        if (not is_new_entry)
            continue;

        (*synonym_output) << synonym << '\n';
	++synomym_line_count;

        if (synomym_line_count == 5)
            break;
    }

    std::cout << "Created " << synomym_line_count << " lines in the synonym map while processing " << count
	      << " norm data records.\n";
}

std::unique_ptr<File> OpenInputFile(const std::string &filename) {
    std::string mode("r");
    mode += MediaTypeUtil::GetFileMediaType(filename) == "application/lz4" ? "u" : "m";
    std::unique_ptr<File> file(new File(filename, mode));
    if (file == nullptr)
        Error("can't open \"" + filename + "\" for reading!");

    return file;
}

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

    if (argc != 3)
	Usage();

    std::unique_ptr<File> marc_input(OpenInputFile(argv[1]));

    const std::string synonym_map_filename(argv[2]);
    File synonym_output(synonym_map_filename, "w");
    if (not synonym_output)
        Error("can't open \"" + synonym_map_filename + "\" for writing!");

    try {
	ExtractSynonymsAndWriteSynonymMap(marc_input.get(), &synonym_output, "100abcd:400abcd");
    } catch (const std::exception &x) {
	Error("caught exception: " + std::string(x.what()));
    }
}
<commit_msg>Work in progress.  To be completed by Oliver Obenland.<commit_after>/** \file    create_author_synomym_map.cc
 *  \brief   Creates a SOLR-MARC syonym map for authors from norm data.
 *  \author  Dr. Johannes Ruscheinski
 */

/*
    Copyright (C) 2016, Library of the University of Tübingen

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

    This program 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 Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <iostream>
#include <vector>
#include <cstdlib>
#include "Compiler.h"
#include "MarcUtil.h"
#include "MediaTypeUtil.h"
#include "StringUtil.h"
#include "Subfields.h"
#include "util.h"

void Usage() {
    std::cerr << "Usage: " << ::progname << " norm_data_marc_input synonym_map_output\n";
    std::exit(EXIT_FAILURE);
}

void RemoveCommasDuplicatesAndEmptyEntries(std::vector<std::string> * const vector) {
    std::vector<std::string> cleaned_up_vector;
    std::set<std::string> uniqe_entries;

    for (auto &entry : *vector) {
        std::cout << "- " << entry << "\n";
	StringUtil::RemoveChars(",", &entry);

        if (entry.empty())
            continue;

        const bool is_new_entry(uniqe_entries.emplace(entry).second);
        if (is_new_entry)
            cleaned_up_vector.emplace_back(std::move(entry));
    }

    for (auto entry : cleaned_up_vector) {
        std::cout << "+ " << entry << "\n";
    }

    vector->swap(cleaned_up_vector);
}

std::string ExtractNameFromSubfields(const std::string &field_contents, const std::string &subfield_codes) {
    const Subfields subfields(field_contents);
    std::vector<std::string> subfield_values;
    if (subfields.extractSubfields(subfield_codes, &subfield_values) == 0)
        return "";

    std::sort(subfield_values.begin(), subfield_values.end());
    return StringUtil::Join(subfield_values, ' ');
}

void ExtractSynonymsAndWriteSynonymMap(File * const marc_input, File * const synonym_output, const std::string &field_list) {
    std::set<std::string> synonyms;
    std::vector<std::string> tags_and_subfield_codes;
    if (unlikely(StringUtil::Split(field_list, ':', &tags_and_subfield_codes) < 2))
	Error("in ExtractSynonymsAndWriteSynonymMap: need at least two fields!");

    unsigned synomym_line_count(0), count(0);
    while (const MarcUtil::Record record = MarcUtil::Record::XmlFactory(marc_input)) {
        ++count;

	const int primary_name_field_index(record.getFieldIndex(tags_and_subfield_codes[0].substr(0, 3)));
	if (primary_name_field_index == -1)
	    continue;

	const std::vector<std::string> &fields(record.getFields());
	const std::string primary_field_subfield_codes(tags_and_subfield_codes[primary_name_field_index].substr(3));
	const std::string primary_name(ExtractNameFromSubfields(fields[primary_name_field_index], primary_field_subfield_codes));
	if (unlikely(primary_name.empty()))
	    continue;

	std::vector<std::string> alternatives;
	alternatives.emplace_back(primary_name);

	const std::vector<DirectoryEntry> &dir_entries(record.getDirEntries());

	for (unsigned i(1); i < tags_and_subfield_codes.size(); ++i) {
	    const std::string tag(tags_and_subfield_codes[i].substr(0, 3));
	    const std::string secondary_field_subfield_codes(tags_and_subfield_codes[i].substr(3));
	    int secondary_name_field_index(record.getFieldIndex(tag));
	    while (secondary_name_field_index != -1 and static_cast<size_t>(secondary_name_field_index) < dir_entries.size()
		   and dir_entries[secondary_name_field_index].getTag() == tag)
	    {
		const std::string secondary_name(ExtractNameFromSubfields(fields[secondary_name_field_index],
									  secondary_field_subfield_codes));
		if (not secondary_name.empty())
		    alternatives.emplace_back(secondary_name);
		++secondary_name_field_index;
	    }
	}

        RemoveCommasDuplicatesAndEmptyEntries(&alternatives);
        if (alternatives.size() <= 1)
            continue;

        std::string synonym(StringUtil::Join(alternatives, ','));

        const bool is_new_entry(synonyms.emplace(synonym).second);
        if (not is_new_entry)
            continue;

        (*synonym_output) << synonym << '\n';
	++synomym_line_count;

        if (synomym_line_count == 5)
            break;
    }

    std::cout << "Created " << synomym_line_count << " lines in the synonym map while processing " << count
	      << " norm data records.\n";
}

std::unique_ptr<File> OpenInputFile(const std::string &filename) {
    std::string mode("r");
    mode += MediaTypeUtil::GetFileMediaType(filename) == "application/lz4" ? "u" : "m";
    std::unique_ptr<File> file(new File(filename, mode));
    if (file == nullptr)
        Error("can't open \"" + filename + "\" for reading!");

    return file;
}

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

    if (argc != 3)
	Usage();

    std::unique_ptr<File> marc_input(OpenInputFile(argv[1]));

    const std::string synonym_map_filename(argv[2]);
    File synonym_output(synonym_map_filename, "w");
    if (not synonym_output)
        Error("can't open \"" + synonym_map_filename + "\" for writing!");

    try {
	ExtractSynonymsAndWriteSynonymMap(marc_input.get(), &synonym_output, "100abcd:400abcd");
    } catch (const std::exception &x) {
	Error("caught exception: " + std::string(x.what()));
    }
}
<|endoftext|>


<commit_before>// Copyright (C) 2013 Google Inc.
//
// 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 "testdata_source.h"

#include <libaddressinput/callback.h>
#include <libaddressinput/source.h>

#include <cstddef>
#include <memory>
#include <string>

#include <gtest/gtest.h>

#include "region_data_constants.h"

namespace {

using i18n::addressinput::BuildCallback;
using i18n::addressinput::RegionDataConstants;
using i18n::addressinput::Source;
using i18n::addressinput::TestdataSource;

// The name of the test data file.
const char kDataFileName[] = "src/third_party/libaddressinput/src/testdata/countryinfo.txt";

// Tests for TestdataSource object.
class TestdataSourceTest : public testing::TestWithParam<std::string> {
 public:
  TestdataSourceTest(const TestdataSourceTest&) = delete;
  TestdataSourceTest& operator=(const TestdataSourceTest&) = delete;

 protected:
  TestdataSourceTest()
      : source_(false),
        source_with_path_(false, kDataFileName),
        aggregate_source_(true),
        aggregate_source_with_path_(true, kDataFileName),
        success_(false),
        key_(),
        data_(),
        data_ready_(BuildCallback(this, &TestdataSourceTest::OnDataReady)) {}

  TestdataSource source_;
  TestdataSource source_with_path_;
  TestdataSource aggregate_source_;
  TestdataSource aggregate_source_with_path_;
  bool success_;
  std::string key_;
  std::string data_;
  const std::unique_ptr<const Source::Callback> data_ready_;

 private:
  void OnDataReady(bool success, const std::string& key, std::string* data) {
    ASSERT_FALSE(success && data == nullptr);
    success_ = success;
    key_ = key;
    if (data != nullptr) {
      data_ = *data;
      delete data;
    }
  }
};

// Returns testing::AssertionSuccess if |data| is valid callback data for
// |key|.
testing::AssertionResult DataIsValid(const std::string& data,
                                     const std::string& key) {
  if (data.empty()) {
    return testing::AssertionFailure() << "empty data";
  }

  std::string expected_data_begin = "{\"id\":\"" + key + "\"";
  if (data.compare(0, expected_data_begin.length(), expected_data_begin) != 0) {
    return testing::AssertionFailure() << data << " does not begin with "
                                       << expected_data_begin;
  }

  // Verify that the data ends on "}.
  static const char kDataEnd[] = "\"}";
  static const size_t kDataEndLength = sizeof kDataEnd - 1;
  if (data.compare(data.length() - kDataEndLength,
                   kDataEndLength,
                   kDataEnd,
                   kDataEndLength) != 0) {
    return testing::AssertionFailure() << data << " does not end with "
                                       << kDataEnd;
  }

  return testing::AssertionSuccess();
}

// Verifies that TestdataSource gets valid data for a region code.
TEST_P(TestdataSourceTest, TestdataSourceHasValidDataForRegion) {
  std::string key = "data/" + GetParam();
  source_.Get(key, *data_ready_);

  EXPECT_TRUE(success_);
  EXPECT_EQ(key, key_);
  EXPECT_TRUE(DataIsValid(data_, key));
};

// Verifies that TestdataSource gets valid data for a region code.
TEST_P(TestdataSourceTest, TestdataSourceWithPathHasValidDataForRegion) {
  std::string key = "data/" + GetParam();
  source_with_path_.Get(key, *data_ready_);

  EXPECT_TRUE(success_);
  EXPECT_EQ(key, key_);
  EXPECT_TRUE(DataIsValid(data_, key));
};

// Returns testing::AssertionSuccess if |data| is valid aggregated callback
// data for |key|.
testing::AssertionResult AggregateDataIsValid(const std::string& data,
                                              const std::string& key) {
  if (data.empty()) {
    return testing::AssertionFailure() << "empty data";
  }

  std::string expected_data_begin = "{\"" + key;
  if (data.compare(0, expected_data_begin.length(), expected_data_begin) != 0) {
    return testing::AssertionFailure() << data << " does not begin with "
                                       << expected_data_begin;
  }

  // Verify that the data ends on "}}.
  static const char kDataEnd[] = "\"}}";
  static const size_t kDataEndLength = sizeof kDataEnd - 1;
  if (data.compare(data.length() - kDataEndLength,
                   kDataEndLength,
                   kDataEnd,
                   kDataEndLength) != 0) {
    return testing::AssertionFailure() << data << " does not end with "
                                       << kDataEnd;
  }

  return testing::AssertionSuccess();
}

// Verifies that TestdataSource gets valid aggregated data for a region code.
TEST_P(TestdataSourceTest, TestdataSourceHasValidAggregatedDataForRegion) {
  std::string key = "data/" + GetParam();
  aggregate_source_.Get(key, *data_ready_);

  EXPECT_TRUE(success_);
  EXPECT_EQ(key, key_);
  EXPECT_TRUE(AggregateDataIsValid(data_, key));
};

// Verifies that TestdataSource gets valid aggregated data for a region code.
TEST_P(TestdataSourceTest,
    TestdataSourceWithPathHasValidAggregatedDataForRegion) {

  std::string key = "data/" + GetParam();
  aggregate_source_with_path_.Get(key, *data_ready_);

  EXPECT_TRUE(success_);
  EXPECT_EQ(key, key_);
  EXPECT_TRUE(AggregateDataIsValid(data_, key));
};

// Test all region codes.
INSTANTIATE_TEST_CASE_P(
    AllRegions, TestdataSourceTest,
    testing::ValuesIn(RegionDataConstants::GetRegionCodes()));

// Verifies that the key "data" also contains valid data.
TEST_F(TestdataSourceTest, GetExistingData) {
  static const std::string kKey = "data";
  source_.Get(kKey, *data_ready_);

  EXPECT_TRUE(success_);
  EXPECT_EQ(kKey, key_);
  EXPECT_TRUE(DataIsValid(data_, kKey));
}

// Verifies that requesting a missing key will return "{}".
TEST_F(TestdataSourceTest, GetMissingKeyReturnsEmptyDictionary) {
  static const std::string kJunkKey = "junk";
  source_.Get(kJunkKey, *data_ready_);

  EXPECT_TRUE(success_);
  EXPECT_EQ(kJunkKey, key_);
  EXPECT_EQ("{}", data_);
}

// Verifies that aggregate requesting of a missing key will also return "{}".
TEST_F(TestdataSourceTest, AggregateGetMissingKeyReturnsEmptyDictionary) {
  static const std::string kJunkKey = "junk";
  aggregate_source_.Get(kJunkKey, *data_ready_);

  EXPECT_TRUE(success_);
  EXPECT_EQ(kJunkKey, key_);
  EXPECT_EQ("{}", data_);
}

// Verifies that requesting an empty key will return "{}".
TEST_F(TestdataSourceTest, GetEmptyKeyReturnsEmptyDictionary) {
  static const std::string kEmptyKey;
  source_.Get(kEmptyKey, *data_ready_);

  EXPECT_TRUE(success_);
  EXPECT_EQ(kEmptyKey, key_);
  EXPECT_EQ("{}", data_);
}

}  // namespace
<commit_msg>Update testdata_source_test.cc<commit_after>// Copyright (C) 2013 Google Inc.
//
// 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 "testdata_source.h"

#include <libaddressinput/callback.h>
#include <libaddressinput/source.h>

#include <cstddef>
#include <memory>
#include <string>

#include <gtest/gtest.h>

#include "region_data_constants.h"

namespace {

using i18n::addressinput::BuildCallback;
using i18n::addressinput::RegionDataConstants;
using i18n::addressinput::Source;
using i18n::addressinput::TestdataSource;
using i18n::addressinput::kDataFileName;

// Tests for TestdataSource object.
class TestdataSourceTest : public testing::TestWithParam<std::string> {
 public:
  TestdataSourceTest(const TestdataSourceTest&) = delete;
  TestdataSourceTest& operator=(const TestdataSourceTest&) = delete;

 protected:
  TestdataSourceTest()
      : source_(false),
        source_with_path_(false, kDataFileName),
        aggregate_source_(true),
        aggregate_source_with_path_(true, kDataFileName),
        success_(false),
        key_(),
        data_(),
        data_ready_(BuildCallback(this, &TestdataSourceTest::OnDataReady)) {}

  TestdataSource source_;
  TestdataSource source_with_path_;
  TestdataSource aggregate_source_;
  TestdataSource aggregate_source_with_path_;
  bool success_;
  std::string key_;
  std::string data_;
  const std::unique_ptr<const Source::Callback> data_ready_;

 private:
  void OnDataReady(bool success, const std::string& key, std::string* data) {
    ASSERT_FALSE(success && data == nullptr);
    success_ = success;
    key_ = key;
    if (data != nullptr) {
      data_ = *data;
      delete data;
    }
  }
};

// Returns testing::AssertionSuccess if |data| is valid callback data for
// |key|.
testing::AssertionResult DataIsValid(const std::string& data,
                                     const std::string& key) {
  if (data.empty()) {
    return testing::AssertionFailure() << "empty data";
  }

  std::string expected_data_begin = "{\"id\":\"" + key + "\"";
  if (data.compare(0, expected_data_begin.length(), expected_data_begin) != 0) {
    return testing::AssertionFailure() << data << " does not begin with "
                                       << expected_data_begin;
  }

  // Verify that the data ends on "}.
  static const char kDataEnd[] = "\"}";
  static const size_t kDataEndLength = sizeof kDataEnd - 1;
  if (data.compare(data.length() - kDataEndLength,
                   kDataEndLength,
                   kDataEnd,
                   kDataEndLength) != 0) {
    return testing::AssertionFailure() << data << " does not end with "
                                       << kDataEnd;
  }

  return testing::AssertionSuccess();
}

// Verifies that TestdataSource gets valid data for a region code.
TEST_P(TestdataSourceTest, TestdataSourceHasValidDataForRegion) {
  std::string key = "data/" + GetParam();
  source_.Get(key, *data_ready_);

  EXPECT_TRUE(success_);
  EXPECT_EQ(key, key_);
  EXPECT_TRUE(DataIsValid(data_, key));
};

// Verifies that TestdataSource gets valid data for a region code.
TEST_P(TestdataSourceTest, TestdataSourceWithPathHasValidDataForRegion) {
  std::string key = "data/" + GetParam();
  source_with_path_.Get(key, *data_ready_);

  EXPECT_TRUE(success_);
  EXPECT_EQ(key, key_);
  EXPECT_TRUE(DataIsValid(data_, key));
};

// Returns testing::AssertionSuccess if |data| is valid aggregated callback
// data for |key|.
testing::AssertionResult AggregateDataIsValid(const std::string& data,
                                              const std::string& key) {
  if (data.empty()) {
    return testing::AssertionFailure() << "empty data";
  }

  std::string expected_data_begin = "{\"" + key;
  if (data.compare(0, expected_data_begin.length(), expected_data_begin) != 0) {
    return testing::AssertionFailure() << data << " does not begin with "
                                       << expected_data_begin;
  }

  // Verify that the data ends on "}}.
  static const char kDataEnd[] = "\"}}";
  static const size_t kDataEndLength = sizeof kDataEnd - 1;
  if (data.compare(data.length() - kDataEndLength,
                   kDataEndLength,
                   kDataEnd,
                   kDataEndLength) != 0) {
    return testing::AssertionFailure() << data << " does not end with "
                                       << kDataEnd;
  }

  return testing::AssertionSuccess();
}

// Verifies that TestdataSource gets valid aggregated data for a region code.
TEST_P(TestdataSourceTest, TestdataSourceHasValidAggregatedDataForRegion) {
  std::string key = "data/" + GetParam();
  aggregate_source_.Get(key, *data_ready_);

  EXPECT_TRUE(success_);
  EXPECT_EQ(key, key_);
  EXPECT_TRUE(AggregateDataIsValid(data_, key));
};

// Verifies that TestdataSource gets valid aggregated data for a region code.
TEST_P(TestdataSourceTest,
    TestdataSourceWithPathHasValidAggregatedDataForRegion) {

  std::string key = "data/" + GetParam();
  aggregate_source_with_path_.Get(key, *data_ready_);

  EXPECT_TRUE(success_);
  EXPECT_EQ(key, key_);
  EXPECT_TRUE(AggregateDataIsValid(data_, key));
};

// Test all region codes.
INSTANTIATE_TEST_CASE_P(
    AllRegions, TestdataSourceTest,
    testing::ValuesIn(RegionDataConstants::GetRegionCodes()));

// Verifies that the key "data" also contains valid data.
TEST_F(TestdataSourceTest, GetExistingData) {
  static const std::string kKey = "data";
  source_.Get(kKey, *data_ready_);

  EXPECT_TRUE(success_);
  EXPECT_EQ(kKey, key_);
  EXPECT_TRUE(DataIsValid(data_, kKey));
}

// Verifies that requesting a missing key will return "{}".
TEST_F(TestdataSourceTest, GetMissingKeyReturnsEmptyDictionary) {
  static const std::string kJunkKey = "junk";
  source_.Get(kJunkKey, *data_ready_);

  EXPECT_TRUE(success_);
  EXPECT_EQ(kJunkKey, key_);
  EXPECT_EQ("{}", data_);
}

// Verifies that aggregate requesting of a missing key will also return "{}".
TEST_F(TestdataSourceTest, AggregateGetMissingKeyReturnsEmptyDictionary) {
  static const std::string kJunkKey = "junk";
  aggregate_source_.Get(kJunkKey, *data_ready_);

  EXPECT_TRUE(success_);
  EXPECT_EQ(kJunkKey, key_);
  EXPECT_EQ("{}", data_);
}

// Verifies that requesting an empty key will return "{}".
TEST_F(TestdataSourceTest, GetEmptyKeyReturnsEmptyDictionary) {
  static const std::string kEmptyKey;
  source_.Get(kEmptyKey, *data_ready_);

  EXPECT_TRUE(success_);
  EXPECT_EQ(kEmptyKey, key_);
  EXPECT_EQ("{}", data_);
}

}  // namespace
<|endoftext|>


<commit_before>/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
 *
 * This library is open source and may be redistributed and/or modified under
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
 * (at your option) any later version.  The full license is in LICENSE file
 * included with this distribution, and on the openscenegraph.org website.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRA;NTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * OpenSceneGraph Public License for more details.
*/
#include <osg/View>
#include <osg/Notify>
#include <osg/TexEnv>
#include <osg/DeleteHandler>

using namespace osg;

View::View()
{
    // osg::notify(osg::NOTICE)<<"Constructing osg::View"<<std::endl;

    setLightingMode(HEADLIGHT);

    _camera = new osg::Camera;
    _camera->setView(this);

    double height = osg::DisplaySettings::instance()->getScreenHeight();
    double width = osg::DisplaySettings::instance()->getScreenWidth();
    double distance = osg::DisplaySettings::instance()->getScreenDistance();
    double vfov = osg::RadiansToDegrees(atan2(height/2.0f,distance)*2.0);

    _camera->setProjectionMatrixAsPerspective( vfov, width/height, 1.0f,10000.0f);

    _camera->setClearColor(osg::Vec4f(0.2f, 0.2f, 0.4f, 1.0f));

    osg::StateSet* stateset = _camera->getOrCreateStateSet();
    stateset->setGlobalDefaults();
}

View::View(const osg::View& view, const osg::CopyOp& copyop):
    Object(view,copyop),
    _lightingMode(view._lightingMode),
    _light(view._light),
    _camera(view._camera),
    _slaves(view._slaves)
{
}

View::~View()
{
    osg::notify(osg::INFO)<<"Destructing osg::View"<<std::endl;

    if (_camera.valid())
    {
        _camera->setView(0);
        _camera->setCullCallback(0);
    }

    // detatch the cameras from this View to prevent dangling pointers
    for(Slaves::iterator itr = _slaves.begin();
        itr != _slaves.end();
        ++itr)
    {
        Slave& cd = *itr;
        cd._camera->setView(0);
        cd._camera->setCullCallback(0);
    }

    _camera = 0;
    _slaves.clear();
    _light = 0;

    if (osg::Referenced::getDeleteHandler())
    {
        // osg::Referenced::getDeleteHandler()->setNumFramesToRetainObjects(0);
        osg::Referenced::getDeleteHandler()->flushAll();
    }

    osg::notify(osg::INFO)<<"Done destructing osg::View"<<std::endl;
}

void View::setLightingMode(LightingMode lightingMode)
{
    _lightingMode = lightingMode;
    if (_lightingMode != NO_LIGHT && !_light)
    {
        _light = new osg::Light;
        _light->setLightNum(0);
        _light->setAmbient(Vec4(0.00f,0.0f,0.00f,1.0f));
        _light->setDiffuse(Vec4(0.8f,0.8f,0.8f,1.0f));
        _light->setSpecular(Vec4(1.0f,1.0f,1.0f,1.0f));
    }
}

void View::setCamera(osg::Camera* camera)
{
    if (_camera.valid()) _camera->setView(0);

    _camera = camera;

    if (_camera.valid())
    {
        _camera->setView(this);

        _camera->setRenderer(createRenderer(camera));
    }
}

void View::updateSlaves()
{
    for(unsigned int i=0; i<_slaves.size(); ++i)
    {
        updateSlave(i);
    }
}

void View::updateSlave(unsigned int i)
{
    if (i >= _slaves.size() || !_camera) return;

    Slave& slave = _slaves[i];

    if (slave._camera->getReferenceFrame()==osg::Transform::RELATIVE_RF)
    {
        slave._camera->setProjectionMatrix(_camera->getProjectionMatrix() * slave._projectionOffset);
        slave._camera->setViewMatrix(_camera->getViewMatrix() * slave._viewOffset);
    }

    slave._camera->inheritCullSettings(*_camera);
    if (slave._camera->getInheritanceMask() & osg::CullSettings::CLEAR_COLOR) slave._camera->setClearColor(_camera->getClearColor());
}

bool View::addSlave(osg::Camera* camera, const osg::Matrix& projectionOffset, const osg::Matrix& viewOffset, bool useMastersSceneData)
{
    if (!camera) return false;

    camera->setView(this);

    unsigned int i = _slaves.size();

    _slaves.push_back(Slave(camera, projectionOffset, viewOffset, useMastersSceneData));

    updateSlave(i);

    camera->setRenderer(createRenderer(camera));

    return true;
}

bool View::removeSlave(unsigned int pos)
{
    if (pos >= _slaves.size()) return false;

    _slaves[pos]._camera->setView(0);
    _slaves[pos]._camera->setCullCallback(0);

    _slaves.erase(_slaves.begin()+pos);

    return true;
}

View::Slave* View::findSlaveForCamera(osg::Camera* camera)
{
    if (_camera == camera) return 0;

    for(unsigned int i=0; i<_slaves.size(); ++i)
    {
        if (_slaves[i]._camera == camera) return &(_slaves[i]);
    }

    return 0;
}

<commit_msg>Added thread safe ref/unref<commit_after>/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
 *
 * This library is open source and may be redistributed and/or modified under
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
 * (at your option) any later version.  The full license is in LICENSE file
 * included with this distribution, and on the openscenegraph.org website.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRA;NTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * OpenSceneGraph Public License for more details.
*/
#include <osg/View>
#include <osg/Notify>
#include <osg/TexEnv>
#include <osg/DeleteHandler>

using namespace osg;

View::View():
    Object(true)
{
    // osg::notify(osg::NOTICE)<<"Constructing osg::View"<<std::endl;

    setLightingMode(HEADLIGHT);

    _camera = new osg::Camera;
    _camera->setView(this);

    double height = osg::DisplaySettings::instance()->getScreenHeight();
    double width = osg::DisplaySettings::instance()->getScreenWidth();
    double distance = osg::DisplaySettings::instance()->getScreenDistance();
    double vfov = osg::RadiansToDegrees(atan2(height/2.0f,distance)*2.0);

    _camera->setProjectionMatrixAsPerspective( vfov, width/height, 1.0f,10000.0f);

    _camera->setClearColor(osg::Vec4f(0.2f, 0.2f, 0.4f, 1.0f));

    osg::StateSet* stateset = _camera->getOrCreateStateSet();
    stateset->setGlobalDefaults();
}

View::View(const osg::View& view, const osg::CopyOp& copyop):
    Object(view,copyop),
    _lightingMode(view._lightingMode),
    _light(view._light),
    _camera(view._camera),
    _slaves(view._slaves)
{
}

View::~View()
{
    osg::notify(osg::INFO)<<"Destructing osg::View"<<std::endl;

    if (_camera.valid())
    {
        _camera->setView(0);
        _camera->setCullCallback(0);
    }

    // detatch the cameras from this View to prevent dangling pointers
    for(Slaves::iterator itr = _slaves.begin();
        itr != _slaves.end();
        ++itr)
    {
        Slave& cd = *itr;
        cd._camera->setView(0);
        cd._camera->setCullCallback(0);
    }

    _camera = 0;
    _slaves.clear();
    _light = 0;

#if 0
    if (osg::Referenced::getDeleteHandler())
    {
        // osg::Referenced::getDeleteHandler()->setNumFramesToRetainObjects(0);
        osg::Referenced::getDeleteHandler()->flushAll();
    }
#endif

    osg::notify(osg::INFO)<<"Done destructing osg::View"<<std::endl;
}

void View::setLightingMode(LightingMode lightingMode)
{
    _lightingMode = lightingMode;
    if (_lightingMode != NO_LIGHT && !_light)
    {
        _light = new osg::Light;
        _light->setLightNum(0);
        _light->setAmbient(Vec4(0.00f,0.0f,0.00f,1.0f));
        _light->setDiffuse(Vec4(0.8f,0.8f,0.8f,1.0f));
        _light->setSpecular(Vec4(1.0f,1.0f,1.0f,1.0f));
    }
}

void View::setCamera(osg::Camera* camera)
{
    if (_camera.valid()) _camera->setView(0);

    _camera = camera;

    if (_camera.valid())
    {
        _camera->setView(this);

        _camera->setRenderer(createRenderer(camera));
    }
}

void View::updateSlaves()
{
    for(unsigned int i=0; i<_slaves.size(); ++i)
    {
        updateSlave(i);
    }
}

void View::updateSlave(unsigned int i)
{
    if (i >= _slaves.size() || !_camera) return;

    Slave& slave = _slaves[i];

    if (slave._camera->getReferenceFrame()==osg::Transform::RELATIVE_RF)
    {
        slave._camera->setProjectionMatrix(_camera->getProjectionMatrix() * slave._projectionOffset);
        slave._camera->setViewMatrix(_camera->getViewMatrix() * slave._viewOffset);
    }

    slave._camera->inheritCullSettings(*_camera);
    if (slave._camera->getInheritanceMask() & osg::CullSettings::CLEAR_COLOR) slave._camera->setClearColor(_camera->getClearColor());
}

bool View::addSlave(osg::Camera* camera, const osg::Matrix& projectionOffset, const osg::Matrix& viewOffset, bool useMastersSceneData)
{
    if (!camera) return false;

    camera->setView(this);

    unsigned int i = _slaves.size();

    _slaves.push_back(Slave(camera, projectionOffset, viewOffset, useMastersSceneData));

    updateSlave(i);

    camera->setRenderer(createRenderer(camera));

    return true;
}

bool View::removeSlave(unsigned int pos)
{
    if (pos >= _slaves.size()) return false;

    _slaves[pos]._camera->setView(0);
    _slaves[pos]._camera->setCullCallback(0);

    _slaves.erase(_slaves.begin()+pos);

    return true;
}

View::Slave* View::findSlaveForCamera(osg::Camera* camera)
{
    if (_camera == camera) return 0;

    for(unsigned int i=0; i<_slaves.size(); ++i)
    {
        if (_slaves[i]._camera == camera) return &(_slaves[i]);
    }

    return 0;
}

<|endoftext|>


<commit_before>#include <kernel/timer.hpp>

#include <map>
#include <vector>
#include <kernel/os.hpp>

using namespace std::chrono;
typedef Timers::id_t       id_t;
typedef Timers::duration_t duration_t;
typedef Timers::handler_t  handler_t;

static void sched_timer(duration_t when, id_t id);

struct Timer
{
  Timer(duration_t p, handler_t cb)
    : period(p), callback(cb), deferred_destruct(false) {}

  bool is_alive() const noexcept {
    return deferred_destruct == false;
  }

  bool is_oneshot() const noexcept {
    return period.count() == 0;
  }

  void reset() {
    callback.reset();
    deferred_destruct = false;
  }

  duration_t period;
  handler_t  callback;
  bool deferred_destruct = false;
};

/**
 * 1. There are no restrictions on when timers can be started or stopped
 * 2. A period of 0 means start a one-shot timer
 * 3. Each timer is a separate object living in a "fixed" vector
 * 4. A dead timer is simply a timer which has its handler reset, as well as
 *     having been removed from schedule
 * 5. No timer may be scheduled more than once at a time, as that will needlessly
 *     inflate the schedule container, as well as complicate stopping timers
 * 6. Free timer IDs are retrieved from a stack of free timer IDs (or through
 *     expanding the "fixed" vector)
**/

static bool signal_ready = false;
static bool is_running   = false;
static Timers::start_func_t arch_start_func;
static Timers::stop_func_t  arch_stop_func;
static std::vector<Timer>   timers;
static std::vector<id_t>    free_timers;
// timers sorted by timestamp
static std::multimap<duration_t, id_t> scheduled;
static int timer_stats = 0;

void Timers::init(const start_func_t& start, const stop_func_t& stop)
{
  // architecture specific start and stop functions
  arch_start_func = start;
  arch_stop_func  = stop;
}

void Timers::ready()
{
  assert(signal_ready == false);
  signal_ready = true;
  // begin processing timers if any are queued
  if (is_running == false) {
    timers_handler();
  }
}

id_t Timers::periodic(duration_t when, duration_t period, const handler_t& handler)
{
  id_t id;

  if (UNLIKELY(free_timers.empty())) {

    auto it = scheduled.begin();
    while (it != scheduled.end()) {
      /// shortcut for when there are lots of dead timers
      id_t id = it->second;

      if (timers[id].deferred_destruct) {
        // remove from schedule
        scheduled.erase(it);
        // reset timer
        timers[id].reset();
        // reuse timer
        sched_timer(when, id);
        return id;
      }
      ++it;
    }

    id = timers.size();
    // occupy new slot
    timers.emplace_back(period, handler);
  }
  else {
    // get free timer slot
    id = free_timers.back();
    free_timers.pop_back();

    // occupy free slot
    new (&timers[id]) Timer(period, handler);
  }

  // immediately schedule timer
  sched_timer(when, id);
  return id;
}

void Timers::stop(id_t id)
{
  // mark as dead already
  timers[id].deferred_destruct = true;
  // free resources immediately
  timers[id].callback.reset();
}

size_t Timers::active()
{
  return scheduled.size();
}

/// time functions ///

inline std::chrono::microseconds now() noexcept
{
  return microseconds(OS::micros_since_boot());
}

/// scheduling ///

void Timers::timers_handler()
{
  // assume the hardware timer called this function
  is_running = false;

  while (LIKELY(!scheduled.empty()))
  {
    auto it = scheduled.begin();
    auto when = it->first;
    id_t id   = it->second;

    // remove dead timers
    if (timers[id].deferred_destruct) {
      // remove from schedule
      scheduled.erase(it);
      // delete timer
      timers[id].reset();
      free_timers.push_back(id);
    }
    else
    {
      auto ts_now = now();

      if (ts_now >= when) {
        // erase immediately
        scheduled.erase(it);

        // call the users callback function
        timers[id].callback(id);
        // if the timers struct was modified in callback, eg. due to
        // creating a timer, then the timer reference below would have
        // been invalidated, hence why its BELOW, AND MUST STAY THERE
        auto& timer = timers[id];

        // oneshot timers are automatically freed
        if (timer.deferred_destruct || timer.is_oneshot())
        {
          timer.reset();
          free_timers.push_back(id);
        }
        else if (timer.is_oneshot() == false)
        {
          // if the timer is recurring, we will simply reschedule it
          // NOTE: we are carefully using (when + period) to avoid drift
          scheduled.emplace(std::piecewise_construct,
                    std::forward_as_tuple(when + timer.period),
                    std::forward_as_tuple(id));
        }

      } else {
        // not yet time, so schedule it for later
        is_running = true;
        arch_start_func(when - ts_now);
        timer_stats ++;
        // exit early, because we have nothing more to do,
        // and there is a deferred handler
        return;
      }
    }
  }
  // stop hardware timer, since no timers are enabled
  arch_stop_func();
}
static void sched_timer(duration_t when, id_t id)
{
  scheduled.emplace(std::piecewise_construct,
            std::forward_as_tuple(now() + when),
            std::forward_as_tuple(id));

  // dont start any hardware until after calibration
  if (UNLIKELY(!signal_ready)) return;

  // if the hardware timer is not running, try starting it
  if (UNLIKELY(is_running == false)) {
    Timers::timers_handler();
    return;
  }
  // if the scheduled timer is the new front, restart timer
  auto it = scheduled.begin();
  if (it->second == id)
      Timers::timers_handler();
}

int _get_timer_stats()
{
  int x = timer_stats;
  timer_stats = 0;
  return x;
}
size_t _get_timer_ubound()
{
  return timers.size();
}
<commit_msg>timer: Track dead timers to skip looking sometimes<commit_after>#include <kernel/timer.hpp>

#include <map>
#include <vector>
#include <kernel/os.hpp>

using namespace std::chrono;
typedef Timers::id_t       id_t;
typedef Timers::duration_t duration_t;
typedef Timers::handler_t  handler_t;

static void sched_timer(duration_t when, id_t id);

struct Timer
{
  Timer(duration_t p, handler_t cb)
    : period(p), callback(cb), deferred_destruct(false) {}

  bool is_alive() const noexcept {
    return deferred_destruct == false;
  }

  bool is_oneshot() const noexcept {
    return period.count() == 0;
  }

  void reset() {
    callback.reset();
    deferred_destruct = false;
  }

  duration_t period;
  handler_t  callback;
  bool deferred_destruct = false;
};

/**
 * 1. There are no restrictions on when timers can be started or stopped
 * 2. A period of 0 means start a one-shot timer
 * 3. Each timer is a separate object living in a "fixed" vector
 * 4. A dead timer is simply a timer which has its handler reset, as well as
 *     having been removed from schedule
 * 5. No timer may be scheduled more than once at a time, as that will needlessly
 *     inflate the schedule container, as well as complicate stopping timers
 * 6. Free timer IDs are retrieved from a stack of free timer IDs (or through
 *     expanding the "fixed" vector)
**/

static bool signal_ready = false;
static bool is_running   = false;
static uint32_t dead_timers = 0;
static Timers::start_func_t arch_start_func;
static Timers::stop_func_t  arch_stop_func;
static std::vector<Timer>   timers;
static std::vector<id_t>    free_timers;
// timers sorted by timestamp
static std::multimap<duration_t, id_t> scheduled;
static int timer_stats = 0;

void Timers::init(const start_func_t& start, const stop_func_t& stop)
{
  // architecture specific start and stop functions
  arch_start_func = start;
  arch_stop_func  = stop;
}

void Timers::ready()
{
  assert(signal_ready == false);
  signal_ready = true;
  // begin processing timers if any are queued
  if (is_running == false) {
    timers_handler();
  }
}

id_t Timers::periodic(duration_t when, duration_t period, const handler_t& handler)
{
  id_t id;

  if (UNLIKELY(free_timers.empty())) {

    if (LIKELY(dead_timers)) {
      // look for dead timer
      auto it = scheduled.begin();
      while (it != scheduled.end()) {
        // take over this timer, if dead
        id_t id = it->second;

        if (timers[id].deferred_destruct) {
          dead_timers--;
          // remove from schedule
          scheduled.erase(it);
          // reset timer
          timers[id].reset();
          // reuse timer
          new (&timers[id]) Timer(period, handler);
          sched_timer(when, id);
          return id;
        }
        ++it;
      }
    }
    id = timers.size();
    // occupy new slot
    timers.emplace_back(period, handler);
  }
  else {
    // get free timer slot
    id = free_timers.back();
    free_timers.pop_back();

    // occupy free slot
    new (&timers[id]) Timer(period, handler);
  }

  // immediately schedule timer
  sched_timer(when, id);
  return id;
}

void Timers::stop(id_t id)
{
  if (LIKELY(timers[id].deferred_destruct == false)) {
    // mark as dead already
    timers[id].deferred_destruct = true;
    // free resources immediately
    timers[id].callback.reset();
    dead_timers++;
  }
}

size_t Timers::active()
{
  return scheduled.size();
}

/// time functions ///

inline std::chrono::microseconds now() noexcept
{
  return microseconds(OS::micros_since_boot());
}

/// scheduling ///

void Timers::timers_handler()
{
  // assume the hardware timer called this function
  is_running = false;

  while (LIKELY(!scheduled.empty()))
  {
    auto it = scheduled.begin();
    auto when = it->first;
    id_t id   = it->second;

    // remove dead timers
    if (timers[id].deferred_destruct) {
      dead_timers--;
      // remove from schedule
      scheduled.erase(it);
      // delete timer
      timers[id].reset();
      free_timers.push_back(id);
    }
    else
    {
      auto ts_now = now();

      if (ts_now >= when) {
        // erase immediately
        scheduled.erase(it);

        // call the users callback function
        timers[id].callback(id);
        // if the timers struct was modified in callback, eg. due to
        // creating a timer, then the timer reference below would have
        // been invalidated, hence why its BELOW, AND MUST STAY THERE
        auto& timer = timers[id];

        // oneshot timers are automatically freed
        if (timer.deferred_destruct || timer.is_oneshot())
        {
          timer.reset();
          if (timer.deferred_destruct) dead_timers--;
          free_timers.push_back(id);
        }
        else if (timer.is_oneshot() == false)
        {
          // if the timer is recurring, we will simply reschedule it
          // NOTE: we are carefully using (when + period) to avoid drift
          scheduled.emplace(std::piecewise_construct,
                    std::forward_as_tuple(when + timer.period),
                    std::forward_as_tuple(id));
        }

      } else {
        // not yet time, so schedule it for later
        is_running = true;
        arch_start_func(when - ts_now);
        timer_stats ++;
        // exit early, because we have nothing more to do,
        // and there is a deferred handler
        return;
      }
    }
  }
  // stop hardware timer, since no timers are enabled
  arch_stop_func();
}
static void sched_timer(duration_t when, id_t id)
{
  scheduled.emplace(std::piecewise_construct,
            std::forward_as_tuple(now() + when),
            std::forward_as_tuple(id));

  // dont start any hardware until after calibration
  if (UNLIKELY(!signal_ready)) return;

  // if the hardware timer is not running, try starting it
  if (UNLIKELY(is_running == false)) {
    Timers::timers_handler();
    return;
  }
  // if the scheduled timer is the new front, restart timer
  auto it = scheduled.begin();
  if (it->second == id)
      Timers::timers_handler();
}

int _get_timers_stats()
{
  int x = timer_stats;
  timer_stats = 0;
  return x;
}
size_t _get_timers_ubound()
{
  return timers.size();
}
size_t _get_timers_dead()
{
  return dead_timers;
}
<|endoftext|>


<commit_before>/*
 * Copyright 2016 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkSLHCodeGenerator.h"

#include "SkSLParser.h"
#include "SkSLUtil.h"
#include "ir/SkSLEnum.h"
#include "ir/SkSLFunctionDeclaration.h"
#include "ir/SkSLFunctionDefinition.h"
#include "ir/SkSLSection.h"
#include "ir/SkSLVarDeclarations.h"

namespace SkSL {

HCodeGenerator::HCodeGenerator(const Context* context, const Program* program,
                               ErrorReporter* errors, String name, OutputStream* out)
: INHERITED(program, errors, out)
, fContext(*context)
, fName(std::move(name))
, fFullName(String::printf("Gr%s", fName.c_str()))
, fSectionAndParameterHelper(*program, *errors) {}

String HCodeGenerator::ParameterType(const Context& context, const Type& type,
                                     const Layout& layout) {
    if (layout.fCType != "") {
        return layout.fCType;
    } else if (type == *context.fFloat_Type || type == *context.fHalf_Type) {
        return "float";
    } else if (type == *context.fFloat2_Type || type == *context.fHalf2_Type) {
        return "SkPoint";
    } else if (type == *context.fInt4_Type || type == *context.fShort4_Type) {
        return "SkIRect";
    } else if (type == *context.fFloat4_Type || type == *context.fHalf4_Type) {
        return "SkRect";
    } else if (type == *context.fFloat4x4_Type || type == *context.fHalf4x4_Type) {
        return "SkMatrix44";
    } else if (type.kind() == Type::kSampler_Kind) {
        return "sk_sp<GrTextureProxy>";
    } else if (type == *context.fFragmentProcessor_Type) {
        return "std::unique_ptr<GrFragmentProcessor>";
    }
    return type.name();
}

String HCodeGenerator::FieldType(const Context& context, const Type& type,
                                 const Layout& layout) {
    if (type.kind() == Type::kSampler_Kind) {
        return "TextureSampler";
    } else if (type == *context.fFragmentProcessor_Type) {
        // we don't store fragment processors in fields, they get registered via
        // registerChildProcessor instead
        ASSERT(false);
        return "<error>";
    }
    return ParameterType(context, type, layout);
}

void HCodeGenerator::writef(const char* s, va_list va) {
    static constexpr int BUFFER_SIZE = 1024;
    va_list copy;
    va_copy(copy, va);
    char buffer[BUFFER_SIZE];
    int length = vsnprintf(buffer, BUFFER_SIZE, s, va);
    if (length < BUFFER_SIZE) {
        fOut->write(buffer, length);
    } else {
        std::unique_ptr<char[]> heap(new char[length + 1]);
        vsprintf(heap.get(), s, copy);
        fOut->write(heap.get(), length);
    }
}

void HCodeGenerator::writef(const char* s, ...) {
    va_list va;
    va_start(va, s);
    this->writef(s, va);
    va_end(va);
}

bool HCodeGenerator::writeSection(const char* name, const char* prefix) {
    const Section* s = fSectionAndParameterHelper.getSection(name);
    if (s) {
        this->writef("%s%s", prefix, s->fText.c_str());
        return true;
    }
    return false;
}

void HCodeGenerator::writeExtraConstructorParams(const char* separator) {
    // super-simple parse, just assume the last token before a comma is the name of a parameter
    // (which is true as long as there are no multi-parameter template types involved). Will replace
    // this with something more robust if the need arises.
    const Section* section = fSectionAndParameterHelper.getSection(CONSTRUCTOR_PARAMS_SECTION);
    if (section) {
        const char* s = section->fText.c_str();
        #define BUFFER_SIZE 64
        char lastIdentifier[BUFFER_SIZE];
        int lastIdentifierLength = 0;
        bool foundBreak = false;
        while (*s) {
            char c = *s;
            ++s;
            if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') ||
                c == '_') {
                if (foundBreak) {
                    lastIdentifierLength = 0;
                    foundBreak = false;
                }
                ASSERT(lastIdentifierLength < BUFFER_SIZE);
                lastIdentifier[lastIdentifierLength] = c;
                ++lastIdentifierLength;
            } else {
                foundBreak = true;
                if (c == ',') {
                    ASSERT(lastIdentifierLength < BUFFER_SIZE);
                    lastIdentifier[lastIdentifierLength] = 0;
                    this->writef("%s%s", separator, lastIdentifier);
                    separator = ", ";
                } else if (c != ' ' && c != '\t' && c != '\n' && c != '\r') {
                    lastIdentifierLength = 0;
                }
            }
        }
        if (lastIdentifierLength) {
            ASSERT(lastIdentifierLength < BUFFER_SIZE);
            lastIdentifier[lastIdentifierLength] = 0;
            this->writef("%s%s", separator, lastIdentifier);
        }
    }
}

void HCodeGenerator::writeMake() {
    const char* separator;
    if (!this->writeSection(MAKE_SECTION)) {
        this->writef("    static std::unique_ptr<GrFragmentProcessor> Make(");
        separator = "";
        for (const auto& param : fSectionAndParameterHelper.getParameters()) {
            this->writef("%s%s %s", separator, ParameterType(fContext, param->fType,
                                                             param->fModifiers.fLayout).c_str(),
                         String(param->fName).c_str());
            separator = ", ";
        }
        this->writeSection(CONSTRUCTOR_PARAMS_SECTION, separator);
        this->writef(") {\n"
                     "        return std::unique_ptr<GrFragmentProcessor>(new %s(",
                     fFullName.c_str());
        separator = "";
        for (const auto& param : fSectionAndParameterHelper.getParameters()) {
            if (param->fType == *fContext.fFragmentProcessor_Type) {
                this->writef("%sstd::move(%s)", separator, String(param->fName).c_str());
            } else {
                this->writef("%s%s", separator, String(param->fName).c_str());
            }
            separator = ", ";
        }
        this->writeExtraConstructorParams(separator);
        this->writef("));\n"
                     "    }\n");
    }
}

void HCodeGenerator::failOnSection(const char* section, const char* msg) {
    std::vector<const Section*> s = fSectionAndParameterHelper.getSections(section);
    if (s.size()) {
        fErrors.error(s[0]->fOffset, String("@") + section + " " + msg);
    }
}

void HCodeGenerator::writeConstructor() {
    if (this->writeSection(CONSTRUCTOR_SECTION)) {
        const char* msg = "may not be present when constructor is overridden";
        this->failOnSection(CONSTRUCTOR_CODE_SECTION, msg);
        this->failOnSection(CONSTRUCTOR_PARAMS_SECTION, msg);
        this->failOnSection(COORD_TRANSFORM_SECTION, msg);
        this->failOnSection(INITIALIZERS_SECTION, msg);
        this->failOnSection(OPTIMIZATION_FLAGS_SECTION, msg);
    }
    this->writef("    %s(", fFullName.c_str());
    const char* separator = "";
    for (const auto& param : fSectionAndParameterHelper.getParameters()) {
        this->writef("%s%s %s", separator, ParameterType(fContext, param->fType,
                                                         param->fModifiers.fLayout).c_str(),
                     String(param->fName).c_str());
        separator = ", ";
    }
    this->writeSection(CONSTRUCTOR_PARAMS_SECTION, separator);
    this->writef(")\n"
                 "    : INHERITED(k%s_ClassID", fFullName.c_str());
    if (!this->writeSection(OPTIMIZATION_FLAGS_SECTION, ", (OptimizationFlags) ")) {
        this->writef(", kNone_OptimizationFlags");
    }
    this->writef(")");
    this->writeSection(INITIALIZERS_SECTION, "\n    , ");
    for (const auto& param : fSectionAndParameterHelper.getParameters()) {
        String nameString(param->fName);
        const char* name = nameString.c_str();
        if (param->fType.kind() == Type::kSampler_Kind) {
            this->writef("\n    , %s(std::move(%s)", FieldName(name).c_str(), name);
            for (const Section* s : fSectionAndParameterHelper.getSections(
                                                                          SAMPLER_PARAMS_SECTION)) {
                if (s->fArgument == name) {
                    this->writef(", %s", s->fText.c_str());
                }
            }
            this->writef(")");
        } else if (param->fType == *fContext.fFragmentProcessor_Type) {
            // do nothing
        } else {
            this->writef("\n    , %s(%s)", FieldName(name).c_str(), name);
        }
    }
    for (const Section* s : fSectionAndParameterHelper.getSections(COORD_TRANSFORM_SECTION)) {
        String field = FieldName(s->fArgument.c_str());
        this->writef("\n    , %sCoordTransform(%s, %s.proxy())", field.c_str(), s->fText.c_str(),
                     field.c_str());
    }
    this->writef(" {\n");
    this->writeSection(CONSTRUCTOR_CODE_SECTION);
    for (const auto& param : fSectionAndParameterHelper.getParameters()) {
        if (param->fType.kind() == Type::kSampler_Kind) {
            this->writef("        this->addTextureSampler(&%s);\n",
                         FieldName(String(param->fName).c_str()).c_str());
        } else if (param->fType == *fContext.fFragmentProcessor_Type) {
            this->writef("        this->registerChildProcessor(std::move(%s));",
                         String(param->fName).c_str());
        }
    }
    for (const Section* s : fSectionAndParameterHelper.getSections(COORD_TRANSFORM_SECTION)) {
        String field = FieldName(s->fArgument.c_str());
        this->writef("        this->addCoordTransform(&%sCoordTransform);\n", field.c_str());
    }
    this->writef("    }\n");
}

void HCodeGenerator::writeFields() {
    this->writeSection(FIELDS_SECTION);
    for (const auto& param : fSectionAndParameterHelper.getParameters()) {
        if (param->fType == *fContext.fFragmentProcessor_Type) {
            continue;
        }
        this->writef("    %s %s;\n", FieldType(fContext, param->fType,
                                               param->fModifiers.fLayout).c_str(),
                     FieldName(String(param->fName).c_str()).c_str());
    }
    for (const Section* s : fSectionAndParameterHelper.getSections(COORD_TRANSFORM_SECTION)) {
        this->writef("    GrCoordTransform %sCoordTransform;\n",
                     FieldName(s->fArgument.c_str()).c_str());
    }
}

String HCodeGenerator::GetHeader(const Program& program, ErrorReporter& errors) {
    SymbolTable types(&errors);
    Parser parser(program.fSource->c_str(), program.fSource->length(), types, errors);
    for (;;) {
        Token header = parser.nextRawToken();
        switch (header.fKind) {
            case Token::WHITESPACE:
                break;
            case Token::BLOCK_COMMENT:
                return String(program.fSource->c_str() + header.fOffset, header.fLength);
            default:
                return "";
        }
    }
}

bool HCodeGenerator::generateCode() {
    this->writef("%s\n", GetHeader(fProgram, fErrors).c_str());
    this->writef(kFragmentProcessorHeader, fFullName.c_str());
    this->writef("#ifndef %s_DEFINED\n"
                 "#define %s_DEFINED\n",
                 fFullName.c_str(),
                 fFullName.c_str());
    this->writef("#include \"SkTypes.h\"\n"
                 "#if SK_SUPPORT_GPU\n");
    this->writeSection(HEADER_SECTION);
    this->writef("#include \"GrFragmentProcessor.h\"\n"
                 "#include \"GrCoordTransform.h\"\n");
    this->writef("class %s : public GrFragmentProcessor {\n"
                 "public:\n",
                 fFullName.c_str());
    for (const auto& p : fProgram.fElements) {
        if (ProgramElement::kEnum_Kind == p->fKind && !((Enum&) *p).fBuiltin) {
            this->writef("%s\n", p->description().c_str());
        }
    }
    this->writeSection(CLASS_SECTION);
    for (const auto& param : fSectionAndParameterHelper.getParameters()) {
        if (param->fType.kind() == Type::kSampler_Kind ||
            param->fType.kind() == Type::kOther_Kind) {
            continue;
        }
        String nameString(param->fName);
        const char* name = nameString.c_str();
        this->writef("    %s %s() const { return %s; }\n",
                     FieldType(fContext, param->fType, param->fModifiers.fLayout).c_str(), name,
                     FieldName(name).c_str());
    }
    this->writeMake();
    this->writef("    %s(const %s& src);\n"
                 "    std::unique_ptr<GrFragmentProcessor> clone() const override;\n"
                 "    const char* name() const override { return \"%s\"; }\n"
                 "private:\n",
                 fFullName.c_str(), fFullName.c_str(), fName.c_str());
    this->writeConstructor();
    this->writef("    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;\n"
                 "    void onGetGLSLProcessorKey(const GrShaderCaps&,"
                                                "GrProcessorKeyBuilder*) const override;\n"
                 "    bool onIsEqual(const GrFragmentProcessor&) const override;\n"
                 "    GR_DECLARE_FRAGMENT_PROCESSOR_TEST\n");
    this->writeFields();
    this->writef("    typedef GrFragmentProcessor INHERITED;\n"
                "};\n");
    this->writeSection(HEADER_END_SECTION);
    this->writef("#endif\n"
                 "#endif\n");
    return 0 == fErrors.errorCount();
}

} // namespace
<commit_msg>Get @constructor feature of SKSL working<commit_after>/*
 * Copyright 2016 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkSLHCodeGenerator.h"

#include "SkSLParser.h"
#include "SkSLUtil.h"
#include "ir/SkSLEnum.h"
#include "ir/SkSLFunctionDeclaration.h"
#include "ir/SkSLFunctionDefinition.h"
#include "ir/SkSLSection.h"
#include "ir/SkSLVarDeclarations.h"

namespace SkSL {

HCodeGenerator::HCodeGenerator(const Context* context, const Program* program,
                               ErrorReporter* errors, String name, OutputStream* out)
: INHERITED(program, errors, out)
, fContext(*context)
, fName(std::move(name))
, fFullName(String::printf("Gr%s", fName.c_str()))
, fSectionAndParameterHelper(*program, *errors) {}

String HCodeGenerator::ParameterType(const Context& context, const Type& type,
                                     const Layout& layout) {
    if (layout.fCType != "") {
        return layout.fCType;
    } else if (type == *context.fFloat_Type || type == *context.fHalf_Type) {
        return "float";
    } else if (type == *context.fFloat2_Type || type == *context.fHalf2_Type) {
        return "SkPoint";
    } else if (type == *context.fInt4_Type || type == *context.fShort4_Type) {
        return "SkIRect";
    } else if (type == *context.fFloat4_Type || type == *context.fHalf4_Type) {
        return "SkRect";
    } else if (type == *context.fFloat4x4_Type || type == *context.fHalf4x4_Type) {
        return "SkMatrix44";
    } else if (type.kind() == Type::kSampler_Kind) {
        return "sk_sp<GrTextureProxy>";
    } else if (type == *context.fFragmentProcessor_Type) {
        return "std::unique_ptr<GrFragmentProcessor>";
    }
    return type.name();
}

String HCodeGenerator::FieldType(const Context& context, const Type& type,
                                 const Layout& layout) {
    if (type.kind() == Type::kSampler_Kind) {
        return "TextureSampler";
    } else if (type == *context.fFragmentProcessor_Type) {
        // we don't store fragment processors in fields, they get registered via
        // registerChildProcessor instead
        ASSERT(false);
        return "<error>";
    }
    return ParameterType(context, type, layout);
}

void HCodeGenerator::writef(const char* s, va_list va) {
    static constexpr int BUFFER_SIZE = 1024;
    va_list copy;
    va_copy(copy, va);
    char buffer[BUFFER_SIZE];
    int length = vsnprintf(buffer, BUFFER_SIZE, s, va);
    if (length < BUFFER_SIZE) {
        fOut->write(buffer, length);
    } else {
        std::unique_ptr<char[]> heap(new char[length + 1]);
        vsprintf(heap.get(), s, copy);
        fOut->write(heap.get(), length);
    }
}

void HCodeGenerator::writef(const char* s, ...) {
    va_list va;
    va_start(va, s);
    this->writef(s, va);
    va_end(va);
}

bool HCodeGenerator::writeSection(const char* name, const char* prefix) {
    const Section* s = fSectionAndParameterHelper.getSection(name);
    if (s) {
        this->writef("%s%s", prefix, s->fText.c_str());
        return true;
    }
    return false;
}

void HCodeGenerator::writeExtraConstructorParams(const char* separator) {
    // super-simple parse, just assume the last token before a comma is the name of a parameter
    // (which is true as long as there are no multi-parameter template types involved). Will replace
    // this with something more robust if the need arises.
    const Section* section = fSectionAndParameterHelper.getSection(CONSTRUCTOR_PARAMS_SECTION);
    if (section) {
        const char* s = section->fText.c_str();
        #define BUFFER_SIZE 64
        char lastIdentifier[BUFFER_SIZE];
        int lastIdentifierLength = 0;
        bool foundBreak = false;
        while (*s) {
            char c = *s;
            ++s;
            if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') ||
                c == '_') {
                if (foundBreak) {
                    lastIdentifierLength = 0;
                    foundBreak = false;
                }
                ASSERT(lastIdentifierLength < BUFFER_SIZE);
                lastIdentifier[lastIdentifierLength] = c;
                ++lastIdentifierLength;
            } else {
                foundBreak = true;
                if (c == ',') {
                    ASSERT(lastIdentifierLength < BUFFER_SIZE);
                    lastIdentifier[lastIdentifierLength] = 0;
                    this->writef("%s%s", separator, lastIdentifier);
                    separator = ", ";
                } else if (c != ' ' && c != '\t' && c != '\n' && c != '\r') {
                    lastIdentifierLength = 0;
                }
            }
        }
        if (lastIdentifierLength) {
            ASSERT(lastIdentifierLength < BUFFER_SIZE);
            lastIdentifier[lastIdentifierLength] = 0;
            this->writef("%s%s", separator, lastIdentifier);
        }
    }
}

void HCodeGenerator::writeMake() {
    const char* separator;
    if (!this->writeSection(MAKE_SECTION)) {
        this->writef("    static std::unique_ptr<GrFragmentProcessor> Make(");
        separator = "";
        for (const auto& param : fSectionAndParameterHelper.getParameters()) {
            this->writef("%s%s %s", separator, ParameterType(fContext, param->fType,
                                                             param->fModifiers.fLayout).c_str(),
                         String(param->fName).c_str());
            separator = ", ";
        }
        this->writeSection(CONSTRUCTOR_PARAMS_SECTION, separator);
        this->writef(") {\n"
                     "        return std::unique_ptr<GrFragmentProcessor>(new %s(",
                     fFullName.c_str());
        separator = "";
        for (const auto& param : fSectionAndParameterHelper.getParameters()) {
            if (param->fType == *fContext.fFragmentProcessor_Type) {
                this->writef("%sstd::move(%s)", separator, String(param->fName).c_str());
            } else {
                this->writef("%s%s", separator, String(param->fName).c_str());
            }
            separator = ", ";
        }
        this->writeExtraConstructorParams(separator);
        this->writef("));\n"
                     "    }\n");
    }
}

void HCodeGenerator::failOnSection(const char* section, const char* msg) {
    std::vector<const Section*> s = fSectionAndParameterHelper.getSections(section);
    if (s.size()) {
        fErrors.error(s[0]->fOffset, String("@") + section + " " + msg);
    }
}

void HCodeGenerator::writeConstructor() {
    if (this->writeSection(CONSTRUCTOR_SECTION)) {
        const char* msg = "may not be present when constructor is overridden";
        this->failOnSection(CONSTRUCTOR_CODE_SECTION, msg);
        this->failOnSection(CONSTRUCTOR_PARAMS_SECTION, msg);
        this->failOnSection(INITIALIZERS_SECTION, msg);
        this->failOnSection(OPTIMIZATION_FLAGS_SECTION, msg);
        return;
    }
    this->writef("    %s(", fFullName.c_str());
    const char* separator = "";
    for (const auto& param : fSectionAndParameterHelper.getParameters()) {
        this->writef("%s%s %s", separator, ParameterType(fContext, param->fType,
                                                         param->fModifiers.fLayout).c_str(),
                     String(param->fName).c_str());
        separator = ", ";
    }
    this->writeSection(CONSTRUCTOR_PARAMS_SECTION, separator);
    this->writef(")\n"
                 "    : INHERITED(k%s_ClassID", fFullName.c_str());
    if (!this->writeSection(OPTIMIZATION_FLAGS_SECTION, ", (OptimizationFlags) ")) {
        this->writef(", kNone_OptimizationFlags");
    }
    this->writef(")");
    this->writeSection(INITIALIZERS_SECTION, "\n    , ");
    for (const auto& param : fSectionAndParameterHelper.getParameters()) {
        String nameString(param->fName);
        const char* name = nameString.c_str();
        if (param->fType.kind() == Type::kSampler_Kind) {
            this->writef("\n    , %s(std::move(%s)", FieldName(name).c_str(), name);
            for (const Section* s : fSectionAndParameterHelper.getSections(
                                                                          SAMPLER_PARAMS_SECTION)) {
                if (s->fArgument == name) {
                    this->writef(", %s", s->fText.c_str());
                }
            }
            this->writef(")");
        } else if (param->fType == *fContext.fFragmentProcessor_Type) {
            // do nothing
        } else {
            this->writef("\n    , %s(%s)", FieldName(name).c_str(), name);
        }
    }
    for (const Section* s : fSectionAndParameterHelper.getSections(COORD_TRANSFORM_SECTION)) {
        String field = FieldName(s->fArgument.c_str());
        this->writef("\n    , %sCoordTransform(%s, %s.proxy())", field.c_str(), s->fText.c_str(),
                     field.c_str());
    }
    this->writef(" {\n");
    this->writeSection(CONSTRUCTOR_CODE_SECTION);
    for (const auto& param : fSectionAndParameterHelper.getParameters()) {
        if (param->fType.kind() == Type::kSampler_Kind) {
            this->writef("        this->addTextureSampler(&%s);\n",
                         FieldName(String(param->fName).c_str()).c_str());
        } else if (param->fType == *fContext.fFragmentProcessor_Type) {
            this->writef("        this->registerChildProcessor(std::move(%s));",
                         String(param->fName).c_str());
        }
    }
    for (const Section* s : fSectionAndParameterHelper.getSections(COORD_TRANSFORM_SECTION)) {
        String field = FieldName(s->fArgument.c_str());
        this->writef("        this->addCoordTransform(&%sCoordTransform);\n", field.c_str());
    }
    this->writef("    }\n");
}

void HCodeGenerator::writeFields() {
    this->writeSection(FIELDS_SECTION);
    for (const auto& param : fSectionAndParameterHelper.getParameters()) {
        if (param->fType == *fContext.fFragmentProcessor_Type) {
            continue;
        }
        this->writef("    %s %s;\n", FieldType(fContext, param->fType,
                                               param->fModifiers.fLayout).c_str(),
                     FieldName(String(param->fName).c_str()).c_str());
    }
    for (const Section* s : fSectionAndParameterHelper.getSections(COORD_TRANSFORM_SECTION)) {
        this->writef("    GrCoordTransform %sCoordTransform;\n",
                     FieldName(s->fArgument.c_str()).c_str());
    }
}

String HCodeGenerator::GetHeader(const Program& program, ErrorReporter& errors) {
    SymbolTable types(&errors);
    Parser parser(program.fSource->c_str(), program.fSource->length(), types, errors);
    for (;;) {
        Token header = parser.nextRawToken();
        switch (header.fKind) {
            case Token::WHITESPACE:
                break;
            case Token::BLOCK_COMMENT:
                return String(program.fSource->c_str() + header.fOffset, header.fLength);
            default:
                return "";
        }
    }
}

bool HCodeGenerator::generateCode() {
    this->writef("%s\n", GetHeader(fProgram, fErrors).c_str());
    this->writef(kFragmentProcessorHeader, fFullName.c_str());
    this->writef("#ifndef %s_DEFINED\n"
                 "#define %s_DEFINED\n",
                 fFullName.c_str(),
                 fFullName.c_str());
    this->writef("#include \"SkTypes.h\"\n"
                 "#if SK_SUPPORT_GPU\n");
    this->writeSection(HEADER_SECTION);
    this->writef("#include \"GrFragmentProcessor.h\"\n"
                 "#include \"GrCoordTransform.h\"\n");
    this->writef("class %s : public GrFragmentProcessor {\n"
                 "public:\n",
                 fFullName.c_str());
    for (const auto& p : fProgram.fElements) {
        if (ProgramElement::kEnum_Kind == p->fKind && !((Enum&) *p).fBuiltin) {
            this->writef("%s\n", p->description().c_str());
        }
    }
    this->writeSection(CLASS_SECTION);
    for (const auto& param : fSectionAndParameterHelper.getParameters()) {
        if (param->fType.kind() == Type::kSampler_Kind ||
            param->fType.kind() == Type::kOther_Kind) {
            continue;
        }
        String nameString(param->fName);
        const char* name = nameString.c_str();
        this->writef("    %s %s() const { return %s; }\n",
                     FieldType(fContext, param->fType, param->fModifiers.fLayout).c_str(), name,
                     FieldName(name).c_str());
    }
    this->writeMake();
    this->writef("    %s(const %s& src);\n"
                 "    std::unique_ptr<GrFragmentProcessor> clone() const override;\n"
                 "    const char* name() const override { return \"%s\"; }\n"
                 "private:\n",
                 fFullName.c_str(), fFullName.c_str(), fName.c_str());
    this->writeConstructor();
    this->writef("    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;\n"
                 "    void onGetGLSLProcessorKey(const GrShaderCaps&,"
                                                "GrProcessorKeyBuilder*) const override;\n"
                 "    bool onIsEqual(const GrFragmentProcessor&) const override;\n"
                 "    GR_DECLARE_FRAGMENT_PROCESSOR_TEST\n");
    this->writeFields();
    this->writef("    typedef GrFragmentProcessor INHERITED;\n"
                "};\n");
    this->writeSection(HEADER_END_SECTION);
    this->writef("#endif\n"
                 "#endif\n");
    return 0 == fErrors.errorCount();
}

} // namespace
<|endoftext|>


<commit_before>//
// Copyright (c) 2012 Kim Walisch, <kim.walisch@gmail.com>.
// All rights reserved.
//
// This file is part of primesieve.
// Homepage: http://primesieve.googlecode.com
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
//   * Redistributions of source code must retain the above copyright
//     notice, this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above
//     copyright notice, this list of conditions and the following
//     disclaimer in the documentation and/or other materials provided
//     with the distribution.
//   * Neither the name of the author nor the names of its
//     contributors may be used to endorse or promote products derived
//     from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.

#include "SieveOfEratosthenes.h"
#include "PreSieve.h"
#include "EratSmall.h"
#include "EratMedium.h"
#include "EratBig.h"
#include "imath.h"

#include <stdint.h>
#include <stdexcept>
#include <cstdlib>

namespace soe {

const uint_t SieveOfEratosthenes::bitValues_[8] = { 7, 11, 13, 17, 19, 23, 29, 31 };

/// De Bruijn sequence for first set bitValues_
const uint_t SieveOfEratosthenes::bruijnBitValues_[32] =
{
    7,  11, 109,  13, 113,  59,  97,  17,
  119,  89,  79,  61, 101,  71,  19,  37,
  121, 107,  53,  91,  83,  77,  67,  31,
  103,  49,  73,  29,  47,  23,  43,  41
};

/// @param start          Sieve the primes within the interval [start, stop].
/// @param stop           Sieve the primes within the interval [start, stop].
/// @param preSieveLimit  Multiples of small primes <= preSieveLimit are
///                       pre-sieved to speed up the sieve of Eratosthenes,
///                       preSieveLimit >= 13 && <= 23.
/// @param sieveSize      A sieve size in kilobytes, sieveSize >= 1 && <= 4096.
///
SieveOfEratosthenes::SieveOfEratosthenes(uint64_t start,
                                         uint64_t stop,
                                         uint_t preSieveLimit,
                                         uint_t sieveSize) :
  start_(start),
  stop_(stop),
  sqrtStop_(static_cast<uint_t>(isqrt(stop))),
  preSieve_(preSieveLimit),
  isFirstSegment_(true),
  sieve_(NULL),
  sieveSize_(sieveSize * 1024),
  eratSmall_(NULL),
  eratMedium_(NULL),
  eratBig_(NULL)
{
  if (start_ < 7 || start_ > stop_)
    throw std::logic_error("SieveOfEratosthenes: start must be >= 7 && <= stop.");
  // it makes no sense to use very small sieve sizes
  if (sieveSize_ < 1024)
    throw std::invalid_argument("SieveOfEratosthenes: sieveSize must be >= 1 kilobyte.");
  segmentLow_ = start_ - getByteRemainder(start_);
  // '+1' is a correction for primes of type i*30 + 31
  segmentHigh_ = segmentLow_ + sieveSize_ * NUMBERS_PER_BYTE + 1;
  initEratAlgorithms();
  // allocate the sieve of Eratosthenes array
  sieve_ = new uint8_t[sieveSize_ + 8];
}

SieveOfEratosthenes::~SieveOfEratosthenes() {
  delete eratSmall_;
  delete eratMedium_;
  delete eratBig_;
  delete[] sieve_;
}

void SieveOfEratosthenes::initEratAlgorithms() {
  try {
    if (sqrtStop_ > preSieve_.getLimit()) {
      eratSmall_ = new EratSmall(*this);
      if (sqrtStop_ > eratSmall_->getLimit()) {
        eratMedium_ = new EratMedium(*this);
        if (sqrtStop_ > eratMedium_->getLimit())
          eratBig_ = new EratBig(*this);
      }
    }
  } catch (...) {
    delete eratSmall_;
    delete eratMedium_;
    delete eratBig_;
    throw;
  }
}

uint64_t SieveOfEratosthenes::getByteRemainder(uint64_t n) {
  uint_t remainder = n % NUMBERS_PER_BYTE;
  if (remainder <= 1)
    remainder += NUMBERS_PER_BYTE;
  return remainder;
}

/// Pre-sieve multiples of small primes <= preSieve_.getLimit()
/// to speed up the sieve of Eratosthenes.
/// @see PreSieve.cpp
///
void SieveOfEratosthenes::preSieve() {
  preSieve_.doIt(sieve_, sieveSize_, segmentLow_);
  if (isFirstSegment_) {
    isFirstSegment_ = false;
    // correct preSieve_.doIt() for numbers <= 23
    if (start_ <= preSieve_.getLimit())
      sieve_[0] = 0xff;
    // unset bits (numbers) < start_
    uint64_t remainder = getByteRemainder(start_);
    for (int i = 0; i < 8; i++)
      if (bitValues_[i] < remainder)
        sieve_[0] &= ~(1 << i);
  }
}

void SieveOfEratosthenes::crossOffMultiples() {
  if (eratSmall_ != NULL) {
    // process the sieving primes with many multiples per segment
    eratSmall_->crossOff(sieve_, sieveSize_);
    if (eratMedium_ != NULL) {
      // process the sieving primes with a few ...
      eratMedium_->crossOff(sieve_, sieveSize_);
      if (eratBig_ != NULL)
        // process the sieving primes with very few ...
        eratBig_->crossOff(sieve_);
    }
  }
}

/// Sieve the primes within the current segment i.e.
/// [segmentLow_, segmentHigh_].
///
void SieveOfEratosthenes::sieveSegment() {
  preSieve();
  crossOffMultiples();
  segmentProcessed(sieve_, sieveSize_);
  segmentLow_  += sieveSize_ * NUMBERS_PER_BYTE;
  segmentHigh_ += sieveSize_ * NUMBERS_PER_BYTE;
}

/// Sieve the last segments remaining after that sieve(prime) has
/// been called for all primes up to sqrt(stop_).
///
void SieveOfEratosthenes::finish() {
  // sieve all segments left except the last one
  while (segmentHigh_ < stop_)
    sieveSegment();
  // sieve the last segment
  uint64_t remainder = getByteRemainder(stop_);
  sieveSize_ = static_cast<uint_t>((stop_ - remainder) - segmentLow_) / NUMBERS_PER_BYTE + 1;
  preSieve();
  crossOffMultiples();
  // unset bits and bytes (numbers) > stop_
  for (int i = 0; i < 8; i++) {
    if (bitValues_[i] > remainder)
      sieve_[sieveSize_ - 1] &= ~(1 << i);
    sieve_[sieveSize_ + i] = 0;
  }
  segmentProcessed(sieve_, sieveSize_);
}

} // namespace soe
<commit_msg>silenced a warning<commit_after>//
// Copyright (c) 2012 Kim Walisch, <kim.walisch@gmail.com>.
// All rights reserved.
//
// This file is part of primesieve.
// Homepage: http://primesieve.googlecode.com
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
//   * Redistributions of source code must retain the above copyright
//     notice, this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above
//     copyright notice, this list of conditions and the following
//     disclaimer in the documentation and/or other materials provided
//     with the distribution.
//   * Neither the name of the author nor the names of its
//     contributors may be used to endorse or promote products derived
//     from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.

#include "SieveOfEratosthenes.h"
#include "PreSieve.h"
#include "EratSmall.h"
#include "EratMedium.h"
#include "EratBig.h"
#include "imath.h"

#include <stdint.h>
#include <stdexcept>
#include <cstdlib>

namespace soe {

const uint_t SieveOfEratosthenes::bitValues_[8] = { 7, 11, 13, 17, 19, 23, 29, 31 };

/// De Bruijn sequence for first set bitValues_
const uint_t SieveOfEratosthenes::bruijnBitValues_[32] =
{
    7,  11, 109,  13, 113,  59,  97,  17,
  119,  89,  79,  61, 101,  71,  19,  37,
  121, 107,  53,  91,  83,  77,  67,  31,
  103,  49,  73,  29,  47,  23,  43,  41
};

/// @param start          Sieve the primes within the interval [start, stop].
/// @param stop           Sieve the primes within the interval [start, stop].
/// @param preSieveLimit  Multiples of small primes <= preSieveLimit are
///                       pre-sieved to speed up the sieve of Eratosthenes,
///                       preSieveLimit >= 13 && <= 23.
/// @param sieveSize      A sieve size in kilobytes, sieveSize >= 1 && <= 4096.
///
SieveOfEratosthenes::SieveOfEratosthenes(uint64_t start,
                                         uint64_t stop,
                                         uint_t preSieveLimit,
                                         uint_t sieveSize) :
  start_(start),
  stop_(stop),
  sqrtStop_(static_cast<uint_t>(isqrt(stop))),
  preSieve_(preSieveLimit),
  isFirstSegment_(true),
  sieve_(NULL),
  sieveSize_(sieveSize * 1024),
  eratSmall_(NULL),
  eratMedium_(NULL),
  eratBig_(NULL)
{
  if (start_ < 7 || start_ > stop_)
    throw std::logic_error("SieveOfEratosthenes: start must be >= 7 && <= stop.");
  // it makes no sense to use very small sieve sizes
  if (sieveSize_ < 1024)
    throw std::invalid_argument("SieveOfEratosthenes: sieveSize must be >= 1 kilobyte.");
  segmentLow_ = start_ - getByteRemainder(start_);
  // '+1' is a correction for primes of type i*30 + 31
  segmentHigh_ = segmentLow_ + sieveSize_ * NUMBERS_PER_BYTE + 1;
  initEratAlgorithms();
  // allocate the sieve of Eratosthenes array
  sieve_ = new uint8_t[sieveSize_ + 8];
}

SieveOfEratosthenes::~SieveOfEratosthenes() {
  delete eratSmall_;
  delete eratMedium_;
  delete eratBig_;
  delete[] sieve_;
}

void SieveOfEratosthenes::initEratAlgorithms() {
  try {
    if (sqrtStop_ > preSieve_.getLimit()) {
      eratSmall_ = new EratSmall(*this);
      if (sqrtStop_ > eratSmall_->getLimit()) {
        eratMedium_ = new EratMedium(*this);
        if (sqrtStop_ > eratMedium_->getLimit())
          eratBig_ = new EratBig(*this);
      }
    }
  } catch (...) {
    delete eratSmall_;
    delete eratMedium_;
    delete eratBig_;
    throw;
  }
}

uint64_t SieveOfEratosthenes::getByteRemainder(uint64_t n) {
  uint64_t remainder = n % NUMBERS_PER_BYTE;
  if (remainder <= 1)
    remainder += NUMBERS_PER_BYTE;
  return remainder;
}

/// Pre-sieve multiples of small primes <= preSieve_.getLimit()
/// to speed up the sieve of Eratosthenes.
/// @see PreSieve.cpp
///
void SieveOfEratosthenes::preSieve() {
  preSieve_.doIt(sieve_, sieveSize_, segmentLow_);
  if (isFirstSegment_) {
    isFirstSegment_ = false;
    // correct preSieve_.doIt() for numbers <= 23
    if (start_ <= preSieve_.getLimit())
      sieve_[0] = 0xff;
    // unset bits (numbers) < start_
    uint64_t remainder = getByteRemainder(start_);
    for (int i = 0; i < 8; i++)
      if (bitValues_[i] < remainder)
        sieve_[0] &= ~(1 << i);
  }
}

void SieveOfEratosthenes::crossOffMultiples() {
  if (eratSmall_ != NULL) {
    // process the sieving primes with many multiples per segment
    eratSmall_->crossOff(sieve_, sieveSize_);
    if (eratMedium_ != NULL) {
      // process the sieving primes with a few ...
      eratMedium_->crossOff(sieve_, sieveSize_);
      if (eratBig_ != NULL)
        // process the sieving primes with very few ...
        eratBig_->crossOff(sieve_);
    }
  }
}

/// Sieve the primes within the current segment i.e.
/// [segmentLow_, segmentHigh_].
///
void SieveOfEratosthenes::sieveSegment() {
  preSieve();
  crossOffMultiples();
  segmentProcessed(sieve_, sieveSize_);
  segmentLow_  += sieveSize_ * NUMBERS_PER_BYTE;
  segmentHigh_ += sieveSize_ * NUMBERS_PER_BYTE;
}

/// Sieve the last segments remaining after that sieve(prime) has
/// been called for all primes up to sqrt(stop_).
///
void SieveOfEratosthenes::finish() {
  // sieve all segments left except the last one
  while (segmentHigh_ < stop_)
    sieveSegment();
  // sieve the last segment
  uint64_t remainder = getByteRemainder(stop_);
  sieveSize_ = static_cast<uint_t>((stop_ - remainder) - segmentLow_) / NUMBERS_PER_BYTE + 1;
  preSieve();
  crossOffMultiples();
  // unset bits and bytes (numbers) > stop_
  for (int i = 0; i < 8; i++) {
    if (bitValues_[i] > remainder)
      sieve_[sieveSize_ - 1] &= ~(1 << i);
    sieve_[sieveSize_ + i] = 0;
  }
  segmentProcessed(sieve_, sieveSize_);
}

} // namespace soe
<|endoftext|>