<commit_before>/* bzflag
 * Copyright (c) 1993 - 2005 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named COPYING that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

// common goes first
#include "common.h"

// implementation header
#include "LinkManager.h"

// system headers
#include <ctype.h>
#include <stdlib.h>
#include <assert.h>
#include <string>
#include <vector>
#include <iostream>

// common headers
#include "Pack.h"
#include "Teleporter.h"
#include "ObstacleMgr.h"

// simple globbing routines
static int match_object_name (const char *string, const char *objname);
static int match_multi (const char **string, const char **objname);
static const char MATCH_MULTI  = '*'; // matches any number of characters
static const char MATCH_SINGLE = '?'; // matches a single character

LinkManager::LinkManager()
{
  // do nothing
  return;
}

LinkManager::~LinkManager()
{
  clear();
  return;
}

void LinkManager::clear()
{
  linkNames.clear();
  linkNumbers.clear();
  return;
}

void LinkManager::makeLinkName(int number, std::string& name)
{
  name = "/t";
  char buffer[8];
  sprintf(buffer, "%i", (number / 2));
  name += buffer;
  name += ":";
  if ((number % 2) == 0) {
    name += "f";
  } else {
    name += "b";
  }
  return;
}

void LinkManager::addLink(int src, int dst)
{
  LinkNameSet link;
  makeLinkName(src, link.src);
  makeLinkName(dst, link.dst);
  linkNames.push_back(link);
  return;
}

void LinkManager::addLink(const std::string& src, const std::string& dst)
{
  LinkNameSet link;
  if ((src[0] >= '0') && (src[0] <= '9')) {
    int number = atoi(src.c_str());
    makeLinkName(number, link.src);
  } else {
    link.src = src;
  }
  if ((dst[0] >= '0') && (dst[0] <= '9')) {
    int number = atoi(dst.c_str());
    makeLinkName(number, link.dst);
  } else {
    link.dst = dst;
  }
  linkNames.push_back(link);
  return;
}

static bool inIntList(int value, const std::vector<int>& list)
{
  for (unsigned int i = 0; i < list.size(); i++) {
    if (value == list[i]) {
      return true;
    }
  }
  return false;
}

void LinkManager::doLinking()
{
  unsigned int i;

  // initial the destinations
  const ObstacleList& teles = OBSTACLEMGR.getTeles();
  // findTelesByName() can not return higher then this
  linkNumbers.resize(teles.size() * 2);
  for (i = 0; i < linkNumbers.size(); i++) {
    linkNumbers[i].dsts.clear();
  }

  for (i = 0; i < linkNames.size(); i++) {
    LinkNameSet& link = linkNames[i];
    std::vector<int> srcNumbers;
    std::vector<int> dstNumbers;
    findTelesByName(link.src, srcNumbers);
    findTelesByName(link.dst, dstNumbers);

    bool broken = false;
    if (srcNumbers.size() <= 0) {
      broken = true;
      DEBUG1("broken link src: %s\n", link.src.c_str());
    }
    if (dstNumbers.size() <= 0) {
      broken = true;
      DEBUG1("broken link dst: %s\n", link.dst.c_str());
    }
    if (broken) {
      continue;
    }

    for (unsigned s = 0; s < srcNumbers.size(); s++) {
      for (unsigned d = 0; d < dstNumbers.size(); d++) {
	std::vector<int>& dstsList = linkNumbers[srcNumbers[s]].dsts;
	if (!inIntList(dstNumbers[d], dstsList)) { // no duplicates
	  dstsList.push_back(dstNumbers[d]);
	}
      }
    }
  }

  // fill in the blanks (passthru linkage)
  for (i = 0; i < linkNumbers.size(); i++) {
    std::vector<int>& dstsList = linkNumbers[i].dsts;
    if (dstsList.size() <= 0) {
      const int t = (i / 2) * 2; // tele number
      const int f = 1 - (i % 2); // opposite link
      dstsList.push_back(t + f);
    }
  }

  if (debugLevel >= 4) {
    for (i = 0; i < teles.size(); i++) {
      Teleporter* tele = (Teleporter*) teles[i];
      printf ("TELE(%i): %s\n", i, tele->getName().c_str());
    }
    for (i = 0; i < linkNames.size(); i++) {
      LinkNameSet& link = linkNames[i];
      printf ("LINKSRC: %-32sLINKDST: %s\n", link.src.c_str(), link.dst.c_str());
    }
    for (i = 0; i < linkNumbers.size(); i++) {
      printf ("SRC %3i%c:  DSTS", (i / 2), ((i % 2) == 0) ? 'f' : 'b');
      for (unsigned int j = 0; j < linkNumbers[i].dsts.size(); j++) {
	int dst = linkNumbers[i].dsts[j];
	printf (" %i%c", (dst / 2), ((dst % 2) == 0) ? 'f' : 'b');
      }
      printf ("\n");
    }
  }

  return;
}

void LinkManager::findTelesByName(const std::string& name,
				  std::vector<int>& list) const
{
  list.clear();

  std::string glob = name;

  // no chars, no service
  if (glob.size() <= 0) {
    return;
  }

  // a leading ':' might be used to indicate absolute linking if
  // links are ever included in group definintions. strip it here
  // for forwards compatibiliy.
  if (glob[0] == ':') {
    glob.erase(0, 1); // erase 1 char from position 0
  }

  // add all teleporters that have matching names
  const ObstacleList& teles = OBSTACLEMGR.getTeles();
  for (unsigned int i = 0; i < teles.size(); i++) {
    Teleporter* tele = (Teleporter*) teles[i];
    const std::string teleName = tele->getName();

    std::string front = teleName;
    front += ":f";
    if (match_object_name(glob.c_str(), front.c_str())) {
      list.push_back((int)(i * 2) + 0);
    }

    std::string back = teleName;
    back += ":b";
    if (match_object_name(glob.c_str(), back.c_str())) {
      list.push_back((int)(i * 2) + 1);
    }
  }

  return;
}

int LinkManager::getTeleportTarget(int source) const
{
  assert(source < (int)(2 * OBSTACLEMGR.getTeles().size()));

  const std::vector<int>& dstsList = linkNumbers[source].dsts;

  if (dstsList.size() == 1) {
    return dstsList[0];
  }
  else if (dstsList.size() > 1) {
    int target = rand() % int(dstsList.size());
    return dstsList[target];
  }
  else {
    assert(false);
    return 0;
  }
}

int LinkManager::getTeleportTarget(int source, unsigned int seed) const
{
  assert(source < (int)(2 * OBSTACLEMGR.getTeles().size()));

  const std::vector<int>& dstsList = linkNumbers[source].dsts;

  if (dstsList.size() == 1) {
    return dstsList[0];
  }
  else if (dstsList.size() > 1) {
    seed = (seed * 1103515245 + 12345) >> 8; // from POSIX rand() example
    seed = seed % (dstsList.size());
    return dstsList[seed];
  }
  else {
    assert(false);
    return 0;
  }
}

void* LinkManager::pack(void* buf) const
{
  buf = nboPackUInt(buf, (uint32_t) linkNames.size());
  for (unsigned int i = 0; i < linkNames.size(); i++) {
    buf = nboPackStdString(buf, linkNames[i].src);
    buf = nboPackStdString(buf, linkNames[i].dst);
  }
  return buf;
}

void* LinkManager::unpack(void* buf)
{
  clear(); // just in case
  unsigned int i;
  uint32_t count;
  buf = nboUnpackUInt(buf, count);
  for (i = 0; i < count; i++) {
    LinkNameSet link;
    buf = nboUnpackStdString(buf, link.src);
    buf = nboUnpackStdString(buf, link.dst);
    linkNames.push_back(link);
  }
  return buf;
}

void LinkManager::print(std::ostream& out, const std::string& indent) const
{
  for (unsigned int i = 0; i < linkNames.size(); i++) {
    const LinkNameSet& link = linkNames[i];
    out << indent << "link" << std::endl;
    out << indent << "  from " << link.src << std::endl;
    out << indent << "  to   " << link.dst << std::endl;
    out << indent << "end" << std::endl << std::endl;
  }
  return;
}

int LinkManager::packSize() const
{
  int fullSize = sizeof(uint32_t);
  for (unsigned int i = 0; i < linkNames.size(); i++) {
    fullSize += nboStdStringPackSize(linkNames[i].src);
    fullSize += nboStdStringPackSize(linkNames[i].dst);
  }
  return fullSize;
}

/******************************************************************************/

static int match_object_name (const char *string, const char *objname)
{
  if (string == NULL) {
    return 0;
  }
  if (objname == NULL) {
    return 0;
  }

  if ((string[0] == MATCH_MULTI) && (string[1] == '\0')) {
    return 1;
  }

  while (*string != '\0') {
    if (*string == MATCH_MULTI) {
      string++;
      switch (match_multi (&string, &objname)) {
	case +1: {
	  return 1;
	}
	case -1: {
	  return 0;
	}
      }
    }
    else if (*objname == '\0') {
      return 0;
    }
    else if ((*string == MATCH_SINGLE) || (*string == *objname)) {
      string++;
      objname++;
    }
    else {
      return 0;
    }
  }

  if (*objname == '\0') {
    return 1;
  } else {
    return 0;
  }
}

/******************************************************************************/

static int match_multi (const char **string, const char **objname)
{
  const char *str = *string;
  const char *obj = *objname;

  while ((*str != '\0') && (*str == MATCH_MULTI)) {
    str++; // get rid of multiple '*'s
  }

  if (*str == '\0') { // '*' was last, auto-match
    return +1;
  }

  const char *strtop = str;
  const char *objtop = obj;

  while (*str != '\0') {
    if (*str == MATCH_MULTI) {
      *string = str;
      *objname = obj;
      return 0; // matched this segment
    }
    else if (*obj == '\0') {
      return -1; // can't match
    }
    else {
      if ((*str == MATCH_SINGLE) || (*str == *obj)) {
	str++;
	obj++;
	if ((*str == '\0') && (*obj != '\0')) { // advanced check
	  obj++;
	  objtop++;
	  obj = objtop;
	  str = strtop;
	}
      }
      else {
	obj++;
	objtop++;
	obj = objtop;
	str = strtop;
      }
    }
  }

  *string = str;
  *objname = obj;

  return +1; // full match
}

/******************************************************************************/

// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
<commit_msg>use a reference<commit_after>/* bzflag
 * Copyright (c) 1993 - 2005 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named COPYING that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

// common goes first
#include "common.h"

// implementation header
#include "LinkManager.h"

// system headers
#include <ctype.h>
#include <stdlib.h>
#include <assert.h>
#include <string>
#include <vector>
#include <iostream>

// common headers
#include "Pack.h"
#include "Teleporter.h"
#include "ObstacleMgr.h"

// simple globbing routines
static int match_object_name (const char *string, const char *objname);
static int match_multi (const char **string, const char **objname);
static const char MATCH_MULTI  = '*'; // matches any number of characters
static const char MATCH_SINGLE = '?'; // matches a single character

LinkManager::LinkManager()
{
  // do nothing
  return;
}

LinkManager::~LinkManager()
{
  clear();
  return;
}

void LinkManager::clear()
{
  linkNames.clear();
  linkNumbers.clear();
  return;
}

void LinkManager::makeLinkName(int number, std::string& name)
{
  name = "/t";
  char buffer[8];
  sprintf(buffer, "%i", (number / 2));
  name += buffer;
  name += ":";
  if ((number % 2) == 0) {
    name += "f";
  } else {
    name += "b";
  }
  return;
}

void LinkManager::addLink(int src, int dst)
{
  LinkNameSet link;
  makeLinkName(src, link.src);
  makeLinkName(dst, link.dst);
  linkNames.push_back(link);
  return;
}

void LinkManager::addLink(const std::string& src, const std::string& dst)
{
  LinkNameSet link;
  if ((src[0] >= '0') && (src[0] <= '9')) {
    int number = atoi(src.c_str());
    makeLinkName(number, link.src);
  } else {
    link.src = src;
  }
  if ((dst[0] >= '0') && (dst[0] <= '9')) {
    int number = atoi(dst.c_str());
    makeLinkName(number, link.dst);
  } else {
    link.dst = dst;
  }
  linkNames.push_back(link);
  return;
}

static bool inIntList(int value, const std::vector<int>& list)
{
  for (unsigned int i = 0; i < list.size(); i++) {
    if (value == list[i]) {
      return true;
    }
  }
  return false;
}

void LinkManager::doLinking()
{
  unsigned int i;

  // initial the destinations
  const ObstacleList& teles = OBSTACLEMGR.getTeles();
  // findTelesByName() can not return higher then this
  linkNumbers.resize(teles.size() * 2);
  for (i = 0; i < linkNumbers.size(); i++) {
    linkNumbers[i].dsts.clear();
  }

  for (i = 0; i < linkNames.size(); i++) {
    LinkNameSet& link = linkNames[i];
    std::vector<int> srcNumbers;
    std::vector<int> dstNumbers;
    findTelesByName(link.src, srcNumbers);
    findTelesByName(link.dst, dstNumbers);

    bool broken = false;
    if (srcNumbers.size() <= 0) {
      broken = true;
      DEBUG1("broken link src: %s\n", link.src.c_str());
    }
    if (dstNumbers.size() <= 0) {
      broken = true;
      DEBUG1("broken link dst: %s\n", link.dst.c_str());
    }
    if (broken) {
      continue;
    }

    for (unsigned s = 0; s < srcNumbers.size(); s++) {
      for (unsigned d = 0; d < dstNumbers.size(); d++) {
	std::vector<int>& dstsList = linkNumbers[srcNumbers[s]].dsts;
	if (!inIntList(dstNumbers[d], dstsList)) { // no duplicates
	  dstsList.push_back(dstNumbers[d]);
	}
      }
    }
  }

  // fill in the blanks (passthru linkage)
  for (i = 0; i < linkNumbers.size(); i++) {
    std::vector<int>& dstsList = linkNumbers[i].dsts;
    if (dstsList.size() <= 0) {
      const int t = (i / 2) * 2; // tele number
      const int f = 1 - (i % 2); // opposite link
      dstsList.push_back(t + f);
    }
  }

  if (debugLevel >= 4) {
    for (i = 0; i < teles.size(); i++) {
      Teleporter* tele = (Teleporter*) teles[i];
      printf ("TELE(%i): %s\n", i, tele->getName().c_str());
    }
    for (i = 0; i < linkNames.size(); i++) {
      LinkNameSet& link = linkNames[i];
      printf ("LINKSRC: %-32sLINKDST: %s\n", link.src.c_str(), link.dst.c_str());
    }
    for (i = 0; i < linkNumbers.size(); i++) {
      printf ("SRC %3i%c:  DSTS", (i / 2), ((i % 2) == 0) ? 'f' : 'b');
      for (unsigned int j = 0; j < linkNumbers[i].dsts.size(); j++) {
	int dst = linkNumbers[i].dsts[j];
	printf (" %i%c", (dst / 2), ((dst % 2) == 0) ? 'f' : 'b');
      }
      printf ("\n");
    }
  }

  return;
}

void LinkManager::findTelesByName(const std::string& name,
				  std::vector<int>& list) const
{
  list.clear();

  std::string glob = name;

  // no chars, no service
  if (glob.size() <= 0) {
    return;
  }

  // a leading ':' might be used to indicate absolute linking if
  // links are ever included in group definintions. strip it here
  // for forwards compatibiliy.
  if (glob[0] == ':') {
    glob.erase(0, 1); // erase 1 char from position 0
  }

  // add all teleporters that have matching names
  const ObstacleList& teles = OBSTACLEMGR.getTeles();
  for (unsigned int i = 0; i < teles.size(); i++) {
    Teleporter* tele = (Teleporter*) teles[i];
    const std::string& teleName = tele->getName();

    std::string front = teleName;
    front += ":f";
    if (match_object_name(glob.c_str(), front.c_str())) {
      list.push_back((int)(i * 2) + 0);
    }

    std::string back = teleName;
    back += ":b";
    if (match_object_name(glob.c_str(), back.c_str())) {
      list.push_back((int)(i * 2) + 1);
    }
  }

  return;
}

int LinkManager::getTeleportTarget(int source) const
{
  assert(source < (int)(2 * OBSTACLEMGR.getTeles().size()));

  const std::vector<int>& dstsList = linkNumbers[source].dsts;

  if (dstsList.size() == 1) {
    return dstsList[0];
  }
  else if (dstsList.size() > 1) {
    int target = rand() % int(dstsList.size());
    return dstsList[target];
  }
  else {
    assert(false);
    return 0;
  }
}

int LinkManager::getTeleportTarget(int source, unsigned int seed) const
{
  assert(source < (int)(2 * OBSTACLEMGR.getTeles().size()));

  const std::vector<int>& dstsList = linkNumbers[source].dsts;

  if (dstsList.size() == 1) {
    return dstsList[0];
  }
  else if (dstsList.size() > 1) {
    seed = (seed * 1103515245 + 12345) >> 8; // from POSIX rand() example
    seed = seed % (dstsList.size());
    return dstsList[seed];
  }
  else {
    assert(false);
    return 0;
  }
}

void* LinkManager::pack(void* buf) const
{
  buf = nboPackUInt(buf, (uint32_t) linkNames.size());
  for (unsigned int i = 0; i < linkNames.size(); i++) {
    buf = nboPackStdString(buf, linkNames[i].src);
    buf = nboPackStdString(buf, linkNames[i].dst);
  }
  return buf;
}

void* LinkManager::unpack(void* buf)
{
  clear(); // just in case
  unsigned int i;
  uint32_t count;
  buf = nboUnpackUInt(buf, count);
  for (i = 0; i < count; i++) {
    LinkNameSet link;
    buf = nboUnpackStdString(buf, link.src);
    buf = nboUnpackStdString(buf, link.dst);
    linkNames.push_back(link);
  }
  return buf;
}

void LinkManager::print(std::ostream& out, const std::string& indent) const
{
  for (unsigned int i = 0; i < linkNames.size(); i++) {
    const LinkNameSet& link = linkNames[i];
    out << indent << "link" << std::endl;
    out << indent << "  from " << link.src << std::endl;
    out << indent << "  to   " << link.dst << std::endl;
    out << indent << "end" << std::endl << std::endl;
  }
  return;
}

int LinkManager::packSize() const
{
  int fullSize = sizeof(uint32_t);
  for (unsigned int i = 0; i < linkNames.size(); i++) {
    fullSize += nboStdStringPackSize(linkNames[i].src);
    fullSize += nboStdStringPackSize(linkNames[i].dst);
  }
  return fullSize;
}

/******************************************************************************/

static int match_object_name (const char *string, const char *objname)
{
  if (string == NULL) {
    return 0;
  }
  if (objname == NULL) {
    return 0;
  }

  if ((string[0] == MATCH_MULTI) && (string[1] == '\0')) {
    return 1;
  }

  while (*string != '\0') {
    if (*string == MATCH_MULTI) {
      string++;
      switch (match_multi (&string, &objname)) {
	case +1: {
	  return 1;
	}
	case -1: {
	  return 0;
	}
      }
    }
    else if (*objname == '\0') {
      return 0;
    }
    else if ((*string == MATCH_SINGLE) || (*string == *objname)) {
      string++;
      objname++;
    }
    else {
      return 0;
    }
  }

  if (*objname == '\0') {
    return 1;
  } else {
    return 0;
  }
}

/******************************************************************************/

static int match_multi (const char **string, const char **objname)
{
  const char *str = *string;
  const char *obj = *objname;

  while ((*str != '\0') && (*str == MATCH_MULTI)) {
    str++; // get rid of multiple '*'s
  }

  if (*str == '\0') { // '*' was last, auto-match
    return +1;
  }

  const char *strtop = str;
  const char *objtop = obj;

  while (*str != '\0') {
    if (*str == MATCH_MULTI) {
      *string = str;
      *objname = obj;
      return 0; // matched this segment
    }
    else if (*obj == '\0') {
      return -1; // can't match
    }
    else {
      if ((*str == MATCH_SINGLE) || (*str == *obj)) {
	str++;
	obj++;
	if ((*str == '\0') && (*obj != '\0')) { // advanced check
	  obj++;
	  objtop++;
	  obj = objtop;
	  str = strtop;
	}
      }
      else {
	obj++;
	objtop++;
	obj = objtop;
	str = strtop;
      }
    }
  }

  *string = str;
  *objname = obj;

  return +1; // full match
}

/******************************************************************************/

// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
<|endoftext|>


<commit_before>
#include <gtest/gtest.h>
#include <Unittests/unittests_common.hh>

namespace {

class OpenMeshReadWriteSTL : public OpenMeshBase {

    protected:

        // This function is called before each test is run
        virtual void SetUp() {

            // Do some initial stuff with the member data here...
        }

        // This function is called after all tests are through
        virtual void TearDown() {

            // Do some final stuff with the member data here...
        }

    // Member already defined in OpenMeshBase
    //Mesh mesh_;
};

/*
 * ====================================================================
 * Define tests below
 * ====================================================================
 */

/*
 * Just load a simple mesh file in stla format and count whether
 * the right number of entities has been loaded.
 */
TEST_F(OpenMeshReadWriteSTL, LoadSimpleSTLFile) {

    mesh_.clear();

    bool ok = OpenMesh::IO::read_mesh(mesh_, "cube1.stl");

    EXPECT_TRUE(ok);

    EXPECT_EQ(7526u  , mesh_.n_vertices()) << "The number of loaded vertices is not correct!";
    EXPECT_EQ(22572u , mesh_.n_edges()) << "The number of loaded edges is not correct!";
    EXPECT_EQ(15048u , mesh_.n_faces()) << "The number of loaded faces is not correct!";
}

/*
 * Just load a simple mesh file in stla format and count whether
 * the right number of entities has been loaded. Also check facet normals.
 */
TEST_F(OpenMeshReadWriteSTL, LoadSimpleSTLFileWithNormals) {

    mesh_.clear();
    mesh_.request_face_normals();

    OpenMesh::IO::Options opt;
    opt += OpenMesh::IO::Options::FaceNormal;

    bool ok = OpenMesh::IO::read_mesh(mesh_, "cube1.stl", opt);

    EXPECT_TRUE(ok);

    EXPECT_TRUE(opt.face_has_normal());
    EXPECT_FALSE(opt.vertex_has_normal());

    EXPECT_NEAR(-0.038545f, mesh_.normal(mesh_.face_handle(0))[0], 0.0001 ) << "Wrong face normal at face 0 component 0";
    EXPECT_NEAR(-0.004330f, mesh_.normal(mesh_.face_handle(0))[1], 0.0001 ) << "Wrong face normal at face 0 component 1";
    EXPECT_NEAR(0.999247f, mesh_.normal(mesh_.face_handle(0))[2], 0.0001 ) << "Wrong face normal at face 0 component 2";

    EXPECT_EQ(7526u  , mesh_.n_vertices()) << "The number of loaded vertices is not correct!";
    EXPECT_EQ(22572u , mesh_.n_edges()) << "The number of loaded edges is not correct!";
    EXPECT_EQ(15048u , mesh_.n_faces()) << "The number of loaded faces is not correct!";

    mesh_.release_face_normals();
}

/*
 * Just load a simple mesh file in stlb format and count whether
 * the right number of entities has been loaded.
 */
TEST_F(OpenMeshReadWriteSTL, LoadSimpleSTLBinaryFile) {

    mesh_.clear();

    bool ok = OpenMesh::IO::read_mesh(mesh_, "cube1Binary.stl");

    EXPECT_TRUE(ok);

    EXPECT_EQ(7526u  , mesh_.n_vertices()) << "The number of loaded vertices is not correct!";
    EXPECT_EQ(22572u , mesh_.n_edges()) << "The number of loaded edges is not correct!";
    EXPECT_EQ(15048u , mesh_.n_faces()) << "The number of loaded faces is not correct!";
}

/*
 * Just load a simple mesh file in stlb format and count whether
 * the right number of entities has been loaded. Also check facet normals.
 */
TEST_F(OpenMeshReadWriteSTL, LoadSimpleSTLBinaryFileWithNormals) {

    mesh_.clear();
    mesh_.request_face_normals();

    OpenMesh::IO::Options opt;
    opt += OpenMesh::IO::Options::FaceNormal;
    opt += OpenMesh::IO::Options::Binary;

    bool ok = OpenMesh::IO::read_mesh(mesh_, "cube1Binary.stl", opt);

    EXPECT_TRUE(ok);

    EXPECT_TRUE(opt.is_binary());
    EXPECT_TRUE(opt.face_has_normal());
    EXPECT_FALSE(opt.vertex_has_normal());

    EXPECT_NEAR(-0.038545f, mesh_.normal(mesh_.face_handle(0))[0], 0.0001 ) << "Wrong face normal at face 0 component 0";
    EXPECT_NEAR(-0.004330f, mesh_.normal(mesh_.face_handle(0))[1], 0.0001 ) << "Wrong face normal at face 0 component 1";
    EXPECT_NEAR(0.999247f, mesh_.normal(mesh_.face_handle(0))[2], 0.0001 ) << "Wrong face normal at face 0 component 2";

    EXPECT_EQ(7526u  , mesh_.n_vertices()) << "The number of loaded vertices is not correct!";
    EXPECT_EQ(22572u , mesh_.n_edges()) << "The number of loaded edges is not correct!";
    EXPECT_EQ(15048u , mesh_.n_faces()) << "The number of loaded faces is not correct!";

    mesh_.release_face_normals();
}

}
<commit_msg>add write unittest for binary files<commit_after>
#include <gtest/gtest.h>
#include <Unittests/unittests_common.hh>

namespace {

class OpenMeshReadWriteSTL : public OpenMeshBase {

    protected:

        // This function is called before each test is run
        virtual void SetUp() {

            // Do some initial stuff with the member data here...
        }

        // This function is called after all tests are through
        virtual void TearDown() {

            // Do some final stuff with the member data here...
        }

    // Member already defined in OpenMeshBase
    //Mesh mesh_;
};

/*
 * ====================================================================
 * Define tests below
 * ====================================================================
 */

/*
 * Just load a simple mesh file in stla format and count whether
 * the right number of entities has been loaded.
 */
TEST_F(OpenMeshReadWriteSTL, LoadSimpleSTLFile) {

    mesh_.clear();

    bool ok = OpenMesh::IO::read_mesh(mesh_, "cube1.stl");

    EXPECT_TRUE(ok);

    EXPECT_EQ(7526u  , mesh_.n_vertices()) << "The number of loaded vertices is not correct!";
    EXPECT_EQ(22572u , mesh_.n_edges()) << "The number of loaded edges is not correct!";
    EXPECT_EQ(15048u , mesh_.n_faces()) << "The number of loaded faces is not correct!";
}

/*
 * Just load a simple mesh file in stla format and count whether
 * the right number of entities has been loaded. Also check facet normals.
 */
TEST_F(OpenMeshReadWriteSTL, LoadSimpleSTLFileWithNormals) {

    mesh_.clear();
    mesh_.request_face_normals();

    OpenMesh::IO::Options opt;
    opt += OpenMesh::IO::Options::FaceNormal;

    bool ok = OpenMesh::IO::read_mesh(mesh_, "cube1.stl", opt);

    EXPECT_TRUE(ok);

    EXPECT_TRUE(opt.face_has_normal());
    EXPECT_FALSE(opt.vertex_has_normal());

    EXPECT_NEAR(-0.038545f, mesh_.normal(mesh_.face_handle(0))[0], 0.0001 ) << "Wrong face normal at face 0 component 0";
    EXPECT_NEAR(-0.004330f, mesh_.normal(mesh_.face_handle(0))[1], 0.0001 ) << "Wrong face normal at face 0 component 1";
    EXPECT_NEAR(0.999247f, mesh_.normal(mesh_.face_handle(0))[2], 0.0001 ) << "Wrong face normal at face 0 component 2";

    EXPECT_EQ(7526u  , mesh_.n_vertices()) << "The number of loaded vertices is not correct!";
    EXPECT_EQ(22572u , mesh_.n_edges()) << "The number of loaded edges is not correct!";
    EXPECT_EQ(15048u , mesh_.n_faces()) << "The number of loaded faces is not correct!";

    mesh_.release_face_normals();
}

/*
 * Just load a simple mesh file in stlb format and count whether
 * the right number of entities has been loaded.
 */
TEST_F(OpenMeshReadWriteSTL, LoadSimpleSTLBinaryFile) {

    mesh_.clear();

    bool ok = OpenMesh::IO::read_mesh(mesh_, "cube1Binary.stl");

    EXPECT_TRUE(ok);

    EXPECT_EQ(7526u  , mesh_.n_vertices()) << "The number of loaded vertices is not correct!";
    EXPECT_EQ(22572u , mesh_.n_edges()) << "The number of loaded edges is not correct!";
    EXPECT_EQ(15048u , mesh_.n_faces()) << "The number of loaded faces is not correct!";
}

/*
 * Just load a simple mesh file in stlb format and count whether
 * the right number of entities has been loaded. Also check facet normals.
 */
TEST_F(OpenMeshReadWriteSTL, LoadSimpleSTLBinaryFileWithNormals) {

    mesh_.clear();
    mesh_.request_face_normals();

    OpenMesh::IO::Options opt;
    opt += OpenMesh::IO::Options::FaceNormal;
    opt += OpenMesh::IO::Options::Binary;

    bool ok = OpenMesh::IO::read_mesh(mesh_, "cube1Binary.stl", opt);

    EXPECT_TRUE(ok);

    EXPECT_TRUE(opt.is_binary());
    EXPECT_TRUE(opt.face_has_normal());
    EXPECT_FALSE(opt.vertex_has_normal());

    EXPECT_NEAR(-0.038545f, mesh_.normal(mesh_.face_handle(0))[0], 0.0001 ) << "Wrong face normal at face 0 component 0";
    EXPECT_NEAR(-0.004330f, mesh_.normal(mesh_.face_handle(0))[1], 0.0001 ) << "Wrong face normal at face 0 component 1";
    EXPECT_NEAR(0.999247f, mesh_.normal(mesh_.face_handle(0))[2], 0.0001 ) << "Wrong face normal at face 0 component 2";

    EXPECT_EQ(7526u  , mesh_.n_vertices()) << "The number of loaded vertices is not correct!";
    EXPECT_EQ(22572u , mesh_.n_edges()) << "The number of loaded edges is not correct!";
    EXPECT_EQ(15048u , mesh_.n_faces()) << "The number of loaded faces is not correct!";

    mesh_.release_face_normals();
}

/*
 * Read and Write stl binary file
 */
TEST_F(OpenMeshReadWriteSTL, ReadWriteSimpleSTLBinaryFile) {

    mesh_.clear();

    OpenMesh::IO::Options opt;
    opt += OpenMesh::IO::Options::Binary;

    bool ok = OpenMesh::IO::read_mesh(mesh_, "cube1Binary.stl");

    EXPECT_TRUE(ok);

    const char* filename = "cube1Binary_openmeshWriteTestFile.stl";

    ok = OpenMesh::IO::write_mesh(mesh_, filename, opt);

    EXPECT_TRUE(ok);

    ok = OpenMesh::IO::read_mesh(mesh_, filename, opt);

    EXPECT_TRUE(ok);

    EXPECT_EQ(7526u  , mesh_.n_vertices()) << "The number of loaded vertices is not correct!";
    EXPECT_EQ(22572u , mesh_.n_edges()) << "The number of loaded edges is not correct!";
    EXPECT_EQ(15048u , mesh_.n_faces()) << "The number of loaded faces is not correct!";

    remove(filename);
}

/*
 * Just load a simple mesh file in stlb format and count whether
 * the right number of entities has been loaded. Also check facet normals.
 */
TEST_F(OpenMeshReadWriteSTL, ReadWriteSimpleSTLBinaryFileWithNormals) {

    mesh_.clear();
    mesh_.request_face_normals();

    OpenMesh::IO::Options opt;
    opt += OpenMesh::IO::Options::FaceNormal;
    opt += OpenMesh::IO::Options::Binary;

    bool ok = OpenMesh::IO::read_mesh(mesh_, "cube1Binary.stl", opt);

    EXPECT_TRUE(ok);

    const char* filename = "cube1BinaryNormal_openmeshWriteTestFile.stl";

    ok = OpenMesh::IO::write_mesh(mesh_, filename, opt);

    EXPECT_TRUE(ok);

    ok = OpenMesh::IO::read_mesh(mesh_, filename, opt);

    EXPECT_TRUE(ok);

    EXPECT_TRUE(opt.is_binary());
    EXPECT_TRUE(opt.face_has_normal());
    EXPECT_FALSE(opt.vertex_has_normal());

    EXPECT_NEAR(-0.038545f, mesh_.normal(mesh_.face_handle(0))[0], 0.0001 ) << "Wrong face normal at face 0 component 0";
    EXPECT_NEAR(-0.004330f, mesh_.normal(mesh_.face_handle(0))[1], 0.0001 ) << "Wrong face normal at face 0 component 1";
    EXPECT_NEAR(0.999247f, mesh_.normal(mesh_.face_handle(0))[2], 0.0001 ) << "Wrong face normal at face 0 component 2";

    EXPECT_EQ(7526u  , mesh_.n_vertices()) << "The number of loaded vertices is not correct!";
    EXPECT_EQ(22572u , mesh_.n_edges()) << "The number of loaded edges is not correct!";
    EXPECT_EQ(15048u , mesh_.n_faces()) << "The number of loaded faces is not correct!";

    mesh_.release_face_normals();
    remove(filename);
}

}
<|endoftext|>


<commit_before>/*
   Copyright (c) 2015, The Dokdo Project. All rights reserved.

   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 Linux Foundation 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 "AS IS" AND ANY EXPRESS OR IMPLIED
   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
   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.

   File Name : init_sec.c
   Create Date : 2015.11.03
   Author : Sunghun Ra
 */

#include <stdlib.h>
#include <stdio.h>

#include "vendor_init.h"
#include "property_service.h"
#include "log.h"
#include "util.h"

void vendor_load_properties()
{
    char platform[PROP_VALUE_MAX];
    char bootloader[PROP_VALUE_MAX];
    char device[PROP_VALUE_MAX];
    char devicename[PROP_VALUE_MAX];

    property_get("ro.bootloader", bootloader);

    if (strstr(bootloader, "G920S")) {
        /* zeroflteskt */
        property_set("ro.build.fingerprint", "samsung/zeroflteskt/zeroflteskt:5.0.2/LRX22G/G920SKSU1AOF5:user/release-keys");
        property_set("ro.build.description", "zeroflteskt-user 5.0.2 LRX22G G920SKSU1AOF5 release-keys");
        property_set("ro.product.model", "SM-G920S");
        property_set("ro.product.device", "zeroflteskt");
    } else if (strstr(bootloader, "G920K")) {
        /* zerofltektt */
        property_set("ro.build.fingerprint", "samsung/zerofltelgt/zerofltelgt:5.0.2/LRX22G/G920KKSU1AOF5:user/release-keys");
        property_set("ro.build.description", "zerofltektt-user 5.0.2 LRX22G G920KKSU1AOF5 release-keys");
        property_set("ro.product.model", "SM-G920K");
        property_set("ro.product.device", "zerofltektt");
    } else if (strstr(bootloader, "G920L")) {
        /* zerofltelgt */
        property_set("ro.build.fingerprint", "samsung/zerofltelgt/zerofltelgt:5.0.2/LRX22G/G920LKSU1AOF5:user/release-keys");
        property_set("ro.build.description", "zerofltelgt-user 5.0.2 LRX22G G920LKSU1AOF5 release-keys");
        property_set("ro.product.model", "SM-G920L");
        property_set("ro.product.device", "zerofltelgt");
    } else {
        /* zerofltexx */
        property_set("ro.build.fingerprint", "samsung/zerofltexx/zerofltexx:5.0.2/LRX22G/G920FKSU1AOF5:user/release-keys");
        property_set("ro.build.description", "zerofltexx-user 5.0.2 LRX22G G920FKSU1AOF5 release-keys");
        property_set("ro.product.model", "SM-G920F");
        property_set("ro.product.device", "zerofltexx");
    }

    property_get("ro.product.device", device);
    strlcpy(devicename, device, sizeof(devicename));
    ERROR("Found bootloader id %s setting build properties for %s device\n", bootloader, devicename);
}
<commit_msg>init: update fingerprint<commit_after>/*
   Copyright (c) 2015, The Dokdo Project. All rights reserved.

   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 Linux Foundation 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 "AS IS" AND ANY EXPRESS OR IMPLIED
   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
   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.

   File Name : init_sec.c
   Create Date : 2015.11.03
   Author : Sunghun Ra
 */

#include <stdlib.h>
#include <stdio.h>

#include "vendor_init.h"
#include "property_service.h"
#include "log.h"
#include "util.h"

void vendor_load_properties()
{
    char platform[PROP_VALUE_MAX];
    char bootloader[PROP_VALUE_MAX];
    char device[PROP_VALUE_MAX];
    char devicename[PROP_VALUE_MAX];

    property_get("ro.bootloader", bootloader);

    if (strstr(bootloader, "G920S")) {
        /* zeroflteskt */
        property_set("ro.build.fingerprint", "samsung/zeroflteskt/zeroflteskt:5.1.1/LMY47X/G920SXXS3COK5:user/release-keys");
        property_set("ro.build.description", "zeroflteskt-user 5.1.1 LMY47X G920SXXS3COK5 release-keys");
        property_set("ro.product.model", "SM-G920S");
        property_set("ro.product.device", "zeroflteskt");
    } else if (strstr(bootloader, "G920K")) {
        /* zerofltektt */
        property_set("ro.build.fingerprint", "samsung/zerofltelgt/zerofltelgt:5.1.1/LMY47X/G920KXXS3COK5:user/release-keys");
        property_set("ro.build.description", "zerofltektt-user 5.1.1 LMY47X G920KXXS3COK5 release-keys");
        property_set("ro.product.model", "SM-G920K");
        property_set("ro.product.device", "zerofltektt");
    } else if (strstr(bootloader, "G920L")) {
        /* zerofltelgt */
        property_set("ro.build.fingerprint", "samsung/zerofltelgt/zerofltelgt:5.1.1/LMY47X/G920LXXS3COK5:user/release-keys");
        property_set("ro.build.description", "zerofltelgt-user 5.1.1 LMY47X G920LXXS3COK5 release-keys");
        property_set("ro.product.model", "SM-G920L");
        property_set("ro.product.device", "zerofltelgt");
    } else {
        /* zerofltexx */
        property_set("ro.build.fingerprint", "samsung/zerofltexx/zerofltexx:5.1.1/LMY47X/G920FXXS3COK5:user/release-keys");
        property_set("ro.build.description", "zerofltexx-user 5.1.1 LMY47X G920FXXS3COK5 release-keys");
        property_set("ro.product.model", "SM-G920F");
        property_set("ro.product.device", "zerofltexx");
    }

    property_get("ro.product.device", device);
    strlcpy(devicename, device, sizeof(devicename));
    ERROR("Found bootloader id %s setting build properties for %s device\n", bootloader, devicename);
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * 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 "src/profiling/memory/bookkeeping.h"

#include "test/gtest_and_gmock.h"

namespace perfetto {
namespace profiling {
namespace {

using ::testing::AnyOf;
using ::testing::Eq;

std::vector<FrameData> stack() {
  std::vector<FrameData> res;

  unwindstack::FrameData data{};
  data.function_name = "fun1";
  data.map_name = "map1";
  res.emplace_back(std::move(data), "dummy_buildid");
  data = {};
  data.function_name = "fun2";
  data.map_name = "map2";
  res.emplace_back(std::move(data), "dummy_buildid");
  return res;
}

std::vector<FrameData> stack2() {
  std::vector<FrameData> res;
  unwindstack::FrameData data{};
  data.function_name = "fun1";
  data.map_name = "map1";
  res.emplace_back(std::move(data), "dummy_buildid");
  data = {};
  data.function_name = "fun3";
  data.map_name = "map3";
  res.emplace_back(std::move(data), "dummy_buildid");
  return res;
}

TEST(BookkeepingTest, Basic) {
  uint64_t sequence_number = 1;
  GlobalCallstackTrie c;
  HeapTracker hd(&c, false);

  hd.RecordMalloc(stack(), 1, 5, 5, sequence_number, 100 * sequence_number);
  sequence_number++;
  hd.RecordMalloc(stack2(), 2, 2, 2, sequence_number, 100 * sequence_number);
  sequence_number++;
  ASSERT_EQ(hd.GetSizeForTesting(stack()), 5u);
  ASSERT_EQ(hd.GetSizeForTesting(stack2()), 2u);
  ASSERT_EQ(hd.GetTimestampForTesting(), 100 * (sequence_number - 1));
  hd.RecordFree(2, sequence_number, 100 * sequence_number);
  sequence_number++;
  ASSERT_EQ(hd.GetSizeForTesting(stack()), 5u);
  ASSERT_EQ(hd.GetSizeForTesting(stack2()), 0u);
  ASSERT_EQ(hd.GetTimestampForTesting(), 100 * (sequence_number - 1));
  hd.RecordFree(1, sequence_number, 100 * sequence_number);
  sequence_number++;
  ASSERT_EQ(hd.GetSizeForTesting(stack()), 0u);
  ASSERT_EQ(hd.GetSizeForTesting(stack2()), 0u);
  ASSERT_EQ(hd.GetTimestampForTesting(), 100 * (sequence_number - 1));
}

TEST(BookkeepingTest, Max) {
  uint64_t sequence_number = 1;
  GlobalCallstackTrie c;
  HeapTracker hd(&c, true);

  hd.RecordMalloc(stack(), 1, 5, 5, sequence_number, 100 * sequence_number);
  sequence_number++;
  hd.RecordMalloc(stack2(), 2, 2, 2, sequence_number, 100 * sequence_number);
  sequence_number++;
  hd.RecordFree(2, sequence_number, 100 * sequence_number);
  sequence_number++;
  hd.RecordFree(1, sequence_number, 100 * sequence_number);
  sequence_number++;
  ASSERT_EQ(hd.max_timestamp(), 200u);
  ASSERT_EQ(hd.GetMaxForTesting(stack()), 5u);
  ASSERT_EQ(hd.GetMaxForTesting(stack2()), 2u);
}

TEST(BookkeepingTest, TwoHeapTrackers) {
  uint64_t sequence_number = 1;
  GlobalCallstackTrie c;
  HeapTracker hd(&c, false);
  {
    HeapTracker hd2(&c, false);

    hd.RecordMalloc(stack(), 1, 5, 5, sequence_number, 100 * sequence_number);
    hd2.RecordMalloc(stack(), 2, 2, 2, sequence_number, 100 * sequence_number);
    sequence_number++;
    ASSERT_EQ(hd2.GetSizeForTesting(stack()), 2u);
    ASSERT_EQ(hd.GetSizeForTesting(stack()), 5u);
    ASSERT_EQ(hd.GetTimestampForTesting(), 100 * (sequence_number - 1));
  }
  ASSERT_EQ(hd.GetSizeForTesting(stack()), 5u);
}

TEST(BookkeepingTest, ReplaceAlloc) {
  uint64_t sequence_number = 1;
  GlobalCallstackTrie c;
  HeapTracker hd(&c, false);

  hd.RecordMalloc(stack(), 1, 5, 5, sequence_number, 100 * sequence_number);
  sequence_number++;
  hd.RecordMalloc(stack2(), 1, 2, 2, sequence_number, 100 * sequence_number);
  sequence_number++;
  EXPECT_EQ(hd.GetSizeForTesting(stack()), 0u);
  EXPECT_EQ(hd.GetSizeForTesting(stack2()), 2u);
  ASSERT_EQ(hd.GetTimestampForTesting(), 100 * (sequence_number - 1));
}

TEST(BookkeepingTest, OutOfOrder) {
  GlobalCallstackTrie c;
  HeapTracker hd(&c, false);

  hd.RecordMalloc(stack(), 1, 5, 5, 2, 2);
  hd.RecordMalloc(stack2(), 1, 2, 2, 1, 1);
  EXPECT_EQ(hd.GetSizeForTesting(stack()), 5u);
  EXPECT_EQ(hd.GetSizeForTesting(stack2()), 0u);
}

TEST(BookkeepingTest, ManyAllocations) {
  GlobalCallstackTrie c;
  HeapTracker hd(&c, false);

  std::vector<std::pair<uint64_t, uint64_t>> batch_frees;

  for (uint64_t sequence_number = 1; sequence_number < 1000;) {
    if (batch_frees.size() > 10) {
      for (const auto& p : batch_frees)
        hd.RecordFree(p.first, p.second, 100 * p.second);
      batch_frees.clear();
    }

    uint64_t addr = sequence_number;
    hd.RecordMalloc(stack(), addr, 5, 5, sequence_number, sequence_number);
    sequence_number++;
    batch_frees.emplace_back(addr, sequence_number++);
    ASSERT_THAT(hd.GetSizeForTesting(stack()), AnyOf(Eq(0u), Eq(5u)));
  }
}

TEST(BookkeepingTest, ArbitraryOrder) {
  std::vector<FrameData> s = stack();
  std::vector<FrameData> s2 = stack2();

  enum OperationType { kAlloc, kFree, kDump };

  struct Operation {
    uint64_t sequence_number;
    OperationType type;
    uint64_t address;
    uint64_t bytes;                       // 0 for free
    const std::vector<FrameData>* stack;  // nullptr for free

    // For std::next_permutation.
    bool operator<(const Operation& other) const {
      return sequence_number < other.sequence_number;
    }
  } operations[] = {
      {1, kAlloc, 1, 5, &s},      //
      {2, kAlloc, 1, 10, &s2},    //
      {3, kFree, 1, 0, nullptr},  //
      {4, kFree, 2, 0, nullptr},  //
      {5, kFree, 3, 0, nullptr},  //
      {6, kAlloc, 3, 2, &s},      //
      {7, kAlloc, 4, 3, &s2},     //
      {0, kDump, 0, 0, nullptr},  //
  };

  uint64_t s_size = 2;
  uint64_t s2_size = 3;

  do {
    GlobalCallstackTrie c;
    HeapTracker hd(&c, false);

    for (auto it = std::begin(operations); it != std::end(operations); ++it) {
      const Operation& operation = *it;

      if (operation.type == OperationType::kFree) {
        hd.RecordFree(operation.address, operation.sequence_number,
                      100 * operation.sequence_number);
      } else if (operation.type == OperationType::kAlloc) {
        hd.RecordMalloc(*operation.stack, operation.address, operation.bytes,
                        operation.bytes, operation.sequence_number,
                        100 * operation.sequence_number);
      } else {
        hd.GetCallstackAllocations(
            [](const HeapTracker::CallstackAllocations&) {});
      }
    }
    ASSERT_EQ(hd.GetSizeForTesting(s), s_size);
    ASSERT_EQ(hd.GetSizeForTesting(s2), s2_size);
  } while (std::next_permutation(std::begin(operations), std::end(operations)));
}

}  // namespace
}  // namespace profiling
}  // namespace perfetto
<commit_msg>Add bookkeeping test for empty callstack. am: debe648a37 am: 44f01b5bf7 am: 8c3dbd9ea2<commit_after>/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * 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 "src/profiling/memory/bookkeeping.h"

#include "test/gtest_and_gmock.h"

namespace perfetto {
namespace profiling {
namespace {

using ::testing::AnyOf;
using ::testing::Eq;

std::vector<FrameData> stack() {
  std::vector<FrameData> res;

  unwindstack::FrameData data{};
  data.function_name = "fun1";
  data.map_name = "map1";
  res.emplace_back(std::move(data), "dummy_buildid");
  data = {};
  data.function_name = "fun2";
  data.map_name = "map2";
  res.emplace_back(std::move(data), "dummy_buildid");
  return res;
}

std::vector<FrameData> stack2() {
  std::vector<FrameData> res;
  unwindstack::FrameData data{};
  data.function_name = "fun1";
  data.map_name = "map1";
  res.emplace_back(std::move(data), "dummy_buildid");
  data = {};
  data.function_name = "fun3";
  data.map_name = "map3";
  res.emplace_back(std::move(data), "dummy_buildid");
  return res;
}

TEST(BookkeepingTest, EmptyStack) {
  uint64_t sequence_number = 1;
  GlobalCallstackTrie c;
  HeapTracker hd(&c, false);

  hd.RecordMalloc({}, 0x1, 5, 5, sequence_number, 100 * sequence_number);
  sequence_number++;
  hd.RecordFree(0x1, sequence_number, 100 * sequence_number);
  hd.GetCallstackAllocations([](const HeapTracker::CallstackAllocations&) {});
}

TEST(BookkeepingTest, Basic) {
  uint64_t sequence_number = 1;
  GlobalCallstackTrie c;
  HeapTracker hd(&c, false);

  hd.RecordMalloc(stack(), 0x1, 5, 5, sequence_number, 100 * sequence_number);
  sequence_number++;
  hd.RecordMalloc(stack2(), 0x2, 2, 2, sequence_number, 100 * sequence_number);
  sequence_number++;
  ASSERT_EQ(hd.GetSizeForTesting(stack()), 5u);
  ASSERT_EQ(hd.GetSizeForTesting(stack2()), 2u);
  ASSERT_EQ(hd.GetTimestampForTesting(), 100 * (sequence_number - 1));
  hd.RecordFree(0x2, sequence_number, 100 * sequence_number);
  sequence_number++;
  ASSERT_EQ(hd.GetSizeForTesting(stack()), 5u);
  ASSERT_EQ(hd.GetSizeForTesting(stack2()), 0u);
  ASSERT_EQ(hd.GetTimestampForTesting(), 100 * (sequence_number - 1));
  hd.RecordFree(0x1, sequence_number, 100 * sequence_number);
  sequence_number++;
  ASSERT_EQ(hd.GetSizeForTesting(stack()), 0u);
  ASSERT_EQ(hd.GetSizeForTesting(stack2()), 0u);
  ASSERT_EQ(hd.GetTimestampForTesting(), 100 * (sequence_number - 1));
}

TEST(BookkeepingTest, Max) {
  uint64_t sequence_number = 1;
  GlobalCallstackTrie c;
  HeapTracker hd(&c, true);

  hd.RecordMalloc(stack(), 0x1, 5, 5, sequence_number, 100 * sequence_number);
  sequence_number++;
  hd.RecordMalloc(stack2(), 0x2, 2, 2, sequence_number, 100 * sequence_number);
  sequence_number++;
  hd.RecordFree(0x2, sequence_number, 100 * sequence_number);
  sequence_number++;
  hd.RecordFree(0x1, sequence_number, 100 * sequence_number);
  sequence_number++;
  ASSERT_EQ(hd.max_timestamp(), 200u);
  ASSERT_EQ(hd.GetMaxForTesting(stack()), 5u);
  ASSERT_EQ(hd.GetMaxForTesting(stack2()), 2u);
}

TEST(BookkeepingTest, TwoHeapTrackers) {
  uint64_t sequence_number = 1;
  GlobalCallstackTrie c;
  HeapTracker hd(&c, false);
  {
    HeapTracker hd2(&c, false);

    hd.RecordMalloc(stack(), 0x1, 5, 5, sequence_number, 100 * sequence_number);
    hd2.RecordMalloc(stack(), 0x2, 2, 2, sequence_number,
                     100 * sequence_number);
    sequence_number++;
    ASSERT_EQ(hd2.GetSizeForTesting(stack()), 2u);
    ASSERT_EQ(hd.GetSizeForTesting(stack()), 5u);
    ASSERT_EQ(hd.GetTimestampForTesting(), 100 * (sequence_number - 1));
  }
  ASSERT_EQ(hd.GetSizeForTesting(stack()), 5u);
}

TEST(BookkeepingTest, ReplaceAlloc) {
  uint64_t sequence_number = 1;
  GlobalCallstackTrie c;
  HeapTracker hd(&c, false);

  hd.RecordMalloc(stack(), 0x1, 5, 5, sequence_number, 100 * sequence_number);
  sequence_number++;
  hd.RecordMalloc(stack2(), 0x1, 2, 2, sequence_number, 100 * sequence_number);
  sequence_number++;
  EXPECT_EQ(hd.GetSizeForTesting(stack()), 0u);
  EXPECT_EQ(hd.GetSizeForTesting(stack2()), 2u);
  ASSERT_EQ(hd.GetTimestampForTesting(), 100 * (sequence_number - 1));
}

TEST(BookkeepingTest, OutOfOrder) {
  GlobalCallstackTrie c;
  HeapTracker hd(&c, false);

  hd.RecordMalloc(stack(), 0x1, 5, 5, 2, 2);
  hd.RecordMalloc(stack2(), 0x1, 2, 2, 1, 1);
  EXPECT_EQ(hd.GetSizeForTesting(stack()), 5u);
  EXPECT_EQ(hd.GetSizeForTesting(stack2()), 0u);
}

TEST(BookkeepingTest, ManyAllocations) {
  GlobalCallstackTrie c;
  HeapTracker hd(&c, false);

  std::vector<std::pair<uint64_t, uint64_t>> batch_frees;

  for (uint64_t sequence_number = 1; sequence_number < 1000;) {
    if (batch_frees.size() > 10) {
      for (const auto& p : batch_frees)
        hd.RecordFree(p.first, p.second, 100 * p.second);
      batch_frees.clear();
    }

    uint64_t addr = sequence_number;
    hd.RecordMalloc(stack(), addr, 5, 5, sequence_number, sequence_number);
    sequence_number++;
    batch_frees.emplace_back(addr, sequence_number++);
    ASSERT_THAT(hd.GetSizeForTesting(stack()), AnyOf(Eq(0u), Eq(5u)));
  }
}

TEST(BookkeepingTest, ArbitraryOrder) {
  std::vector<FrameData> s = stack();
  std::vector<FrameData> s2 = stack2();

  enum OperationType { kAlloc, kFree, kDump };

  struct Operation {
    uint64_t sequence_number;
    OperationType type;
    uint64_t address;
    uint64_t bytes;                       // 0 for free
    const std::vector<FrameData>* stack;  // nullptr for free

    // For std::next_permutation.
    bool operator<(const Operation& other) const {
      return sequence_number < other.sequence_number;
    }
  } operations[] = {
      {1, kAlloc, 0x1, 5, &s},       //
      {2, kAlloc, 0x1, 10, &s2},     //
      {3, kFree, 0x01, 0, nullptr},  //
      {4, kFree, 0x02, 0, nullptr},  //
      {5, kFree, 0x03, 0, nullptr},  //
      {6, kAlloc, 0x3, 2, &s},       //
      {7, kAlloc, 0x4, 3, &s2},      //
      {0, kDump, 0x00, 0, nullptr},  //
  };

  uint64_t s_size = 2;
  uint64_t s2_size = 3;

  do {
    GlobalCallstackTrie c;
    HeapTracker hd(&c, false);

    for (auto it = std::begin(operations); it != std::end(operations); ++it) {
      const Operation& operation = *it;

      if (operation.type == OperationType::kFree) {
        hd.RecordFree(operation.address, operation.sequence_number,
                      100 * operation.sequence_number);
      } else if (operation.type == OperationType::kAlloc) {
        hd.RecordMalloc(*operation.stack, operation.address, operation.bytes,
                        operation.bytes, operation.sequence_number,
                        100 * operation.sequence_number);
      } else {
        hd.GetCallstackAllocations(
            [](const HeapTracker::CallstackAllocations&) {});
      }
    }
    ASSERT_EQ(hd.GetSizeForTesting(s), s_size);
    ASSERT_EQ(hd.GetSizeForTesting(s2), s2_size);
  } while (std::next_permutation(std::begin(operations), std::end(operations)));
}

}  // namespace
}  // namespace profiling
}  // namespace perfetto
<|endoftext|>


<commit_before>// Copyright (c) 2022 The Orbit 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 <absl/base/casts.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>

#include <atomic>

#include "OrbitBase/ExecutablePath.h"
#include "OrbitBase/GetLastError.h"
#include "OrbitBase/ThreadUtils.h"
#include "Test/Path.h"
#include "TestUtils/TestUtils.h"
#include "WindowsUtils/BusyLoopLauncher.h"
#include "WindowsUtils/BusyLoopUtils.h"
#include "WindowsUtils/DllInjection.h"
#include "WindowsUtils/OpenProcess.h"
#include "WindowsUtils/ProcessList.h"

namespace orbit_windows_utils {

namespace {

using orbit_test_utils::HasError;
using orbit_test_utils::HasNoError;

[[nodiscard]] std::filesystem::path GetTestExecutablePath() {
  static auto path = orbit_base::GetExecutableDir() / "FakeCliProgram.exe";
  return path;
}

[[nodiscard]] std::filesystem::path GetTestDllPath() {
  return orbit_test::GetTestdataDir() / "libtest.dll";
}

__declspec(noinline) void IncrementCounter(uint32_t* counter, std::atomic<bool>* exit_requested) {
  ++(*counter);

  // Wait for exit request.
  while (!(*exit_requested)) {
    std::this_thread::sleep_for(std::chrono::milliseconds(10));
  }
}

}  // namespace

TEST(BusyLoop, BusyLoopLauncher) {
  // Start process that would normally exit instantly and install busy loop at entry point.
  BusyLoopLauncher busy_loop_launcher;
  ErrorMessageOr<BusyLoopInfo> result = busy_loop_launcher.StartWithBusyLoopAtEntryPoint(
      GetTestExecutablePath(), /*working_directory=*/"", /*arguments=*/"");
  ASSERT_TRUE(result.has_value());

  // Validate returned busy loop info.
  const BusyLoopInfo& busy_loop_info = result.value();
  ASSERT_TRUE(busy_loop_info.original_bytes.size() > 0);
  ASSERT_NE(busy_loop_info.process_id, 0);
  ASSERT_NE(busy_loop_info.address, 0);

  // At this point, the process should be busy looping at entry point. Make sure it's still alive.
  std::unique_ptr<ProcessList> process_list = ProcessList::Create();
  ASSERT_TRUE(process_list->GetProcessByPid(busy_loop_info.process_id).has_value());

  // Inject dll while process is spinning at entry point.
  ErrorMessageOr<void> injection_result = InjectDll(busy_loop_info.process_id, GetTestDllPath());
  ASSERT_THAT(injection_result, HasNoError());

  // Suspend main thread and replace busy loop by original bytes.
  ASSERT_THAT(busy_loop_launcher.SuspendMainThreadAndRemoveBusyLoop(), HasNoError());

  // Call function in injected dll.
  ErrorMessageOr<void> remote_thread_result =
      CreateRemoteThread(busy_loop_info.process_id, "libtest.dll", "PrintHelloWorld", {});
  EXPECT_THAT(remote_thread_result, HasNoError());

  // Resume main thread.
  ASSERT_THAT(busy_loop_launcher.ResumeMainThread(), HasNoError());

  // Wait for created process to exit.
  busy_loop_launcher.WaitForProcessToExit();
}

TEST(BusyLoop, BusyLoopAtFunction) {
  std::atomic<bool> exit_requested = false;
  uint32_t counter = 0;

  // Install busy loop at start of "IncrementCounter" function.
  auto busy_loop_info_or = InstallBusyLoopAtAddress(GetCurrentProcess(), &IncrementCounter);
  ASSERT_TRUE(busy_loop_info_or.has_value());
  const BusyLoopInfo& busy_loop_info = busy_loop_info_or.value();

  // Launch a thread that calls "IncrementCounter" which should spin at function entry.
  std::thread t(&IncrementCounter, &counter, &exit_requested);

  // Verify that our counter has not been incremented.
  constexpr size_t kNumChecks = 10;
  for (int i = 0; i < kNumChecks; ++i) {
    ASSERT_EQ(counter, 0);
    std::this_thread::sleep_for(std::chrono::milliseconds(10));
  }

  // Suspend thread and replace busy loop by original code.
  HANDLE thread_handle = t.native_handle();
  ASSERT_TRUE(orbit_windows_utils::SuspendThread(thread_handle).has_value());
  ASSERT_TRUE(RemoveBusyLoop(busy_loop_info_or.value()).has_value());

  // Make sure the instruction pointer is back to the original address.
  ASSERT_TRUE(SetThreadInstructionPointer(thread_handle, busy_loop_info.address).has_value());

  // Verify that our counter has still not been incremented.
  for (int i = 0; i < kNumChecks; ++i) {
    ASSERT_EQ(counter, 0);
    std::this_thread::sleep_for(std::chrono::milliseconds(10));
  }

  // Resume thread.
  ASSERT_TRUE(orbit_windows_utils::ResumeThread(thread_handle).has_value());

  // Wait for thread to exit.
  exit_requested = true;
  t.join();

  // Make sure our counter was incremented.
  ASSERT_EQ(counter, 1);
}

}  // namespace orbit_windows_utils<commit_msg>Tentative fix for BusyLoopLauncherTest<commit_after>// Copyright (c) 2022 The Orbit 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 <absl/base/casts.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>

#include <atomic>

#include "OrbitBase/ExecutablePath.h"
#include "OrbitBase/GetLastError.h"
#include "OrbitBase/ThreadUtils.h"
#include "Test/Path.h"
#include "TestUtils/TestUtils.h"
#include "WindowsUtils/BusyLoopLauncher.h"
#include "WindowsUtils/BusyLoopUtils.h"
#include "WindowsUtils/DllInjection.h"
#include "WindowsUtils/OpenProcess.h"
#include "WindowsUtils/ProcessList.h"

namespace orbit_windows_utils {

namespace {

using orbit_test_utils::HasError;
using orbit_test_utils::HasNoError;

[[nodiscard]] std::filesystem::path GetTestExecutablePath() {
  static auto path = orbit_base::GetExecutableDir() / "FakeCliProgram.exe";
  return path;
}

[[nodiscard]] std::filesystem::path GetTestDllPath() {
  return orbit_test::GetTestdataDir() / "libtest.dll";
}

__declspec(noinline) void IncrementCounter(uint32_t* counter, std::atomic<bool>* exit_requested) {
  ++(*counter);

  // Wait for exit request.
  while (!(*exit_requested)) {
    std::this_thread::sleep_for(std::chrono::milliseconds(10));
  }
}

void MaybeTerminateProcess(uint32_t process_id) {
  ErrorMessageOr<SafeHandle> process_handle =
      OpenProcess(PROCESS_ALL_ACCESS, /*inherit_handle=*/false, process_id);

  if (process_handle.has_value()) {
    ::TerminateProcess(*process_handle.value(), /*exit_code*/ 0);
  }
}

}  // namespace

TEST(BusyLoop, BusyLoopLauncher) {
  // Start process that would normally exit instantly and install busy loop at entry point.
  BusyLoopLauncher busy_loop_launcher;
  ErrorMessageOr<BusyLoopInfo> result = busy_loop_launcher.StartWithBusyLoopAtEntryPoint(
      GetTestExecutablePath(), /*working_directory=*/"", /*arguments=*/"");
  ASSERT_TRUE(result.has_value());

  // Validate returned busy loop info.
  const BusyLoopInfo& busy_loop_info = result.value();
  ASSERT_TRUE(busy_loop_info.original_bytes.size() > 0);
  ASSERT_NE(busy_loop_info.process_id, 0);
  ASSERT_NE(busy_loop_info.address, 0);

  // At this point, the process should be busy looping at entry point. Make sure it's still alive.
  std::unique_ptr<ProcessList> process_list = ProcessList::Create();
  ASSERT_TRUE(process_list->GetProcessByPid(busy_loop_info.process_id).has_value());

  // Inject dll while process is spinning at entry point.
  ErrorMessageOr<void> injection_result = InjectDll(busy_loop_info.process_id, GetTestDllPath());
  ASSERT_THAT(injection_result, HasNoError());

  // Suspend main thread and replace busy loop by original bytes.
  ASSERT_THAT(busy_loop_launcher.SuspendMainThreadAndRemoveBusyLoop(), HasNoError());

  // Call function in injected dll.
  ErrorMessageOr<void> remote_thread_result =
      CreateRemoteThread(busy_loop_info.process_id, "libtest.dll", "PrintHelloWorld", {});
  EXPECT_THAT(remote_thread_result, HasNoError());

  // Resume main thread.
  ASSERT_THAT(busy_loop_launcher.ResumeMainThread(), HasNoError());

  // Kill process if not dead already.
  MaybeTerminateProcess(busy_loop_info.process_id);

  // Wait for created process to exit.
  busy_loop_launcher.WaitForProcessToExit();
}

TEST(BusyLoop, BusyLoopAtFunction) {
  std::atomic<bool> exit_requested = false;
  uint32_t counter = 0;

  // Install busy loop at start of "IncrementCounter" function.
  auto busy_loop_info_or = InstallBusyLoopAtAddress(GetCurrentProcess(), &IncrementCounter);
  ASSERT_TRUE(busy_loop_info_or.has_value());
  const BusyLoopInfo& busy_loop_info = busy_loop_info_or.value();

  // Launch a thread that calls "IncrementCounter" which should spin at function entry.
  std::thread t(&IncrementCounter, &counter, &exit_requested);

  // Verify that our counter has not been incremented.
  constexpr size_t kNumChecks = 10;
  for (int i = 0; i < kNumChecks; ++i) {
    ASSERT_EQ(counter, 0);
    std::this_thread::sleep_for(std::chrono::milliseconds(10));
  }

  // Suspend thread and replace busy loop by original code.
  HANDLE thread_handle = t.native_handle();
  ASSERT_TRUE(orbit_windows_utils::SuspendThread(thread_handle).has_value());
  ASSERT_TRUE(RemoveBusyLoop(busy_loop_info_or.value()).has_value());

  // Make sure the instruction pointer is back to the original address.
  ASSERT_TRUE(SetThreadInstructionPointer(thread_handle, busy_loop_info.address).has_value());

  // Verify that our counter has still not been incremented.
  for (int i = 0; i < kNumChecks; ++i) {
    ASSERT_EQ(counter, 0);
    std::this_thread::sleep_for(std::chrono::milliseconds(10));
  }

  // Resume thread.
  ASSERT_TRUE(orbit_windows_utils::ResumeThread(thread_handle).has_value());

  // Wait for thread to exit.
  exit_requested = true;
  t.join();

  // Make sure our counter was incremented.
  ASSERT_EQ(counter, 1);
}

}  // namespace orbit_windows_utils<|endoftext|>


<commit_before>#include "simdjson.h"
#include "isadetection.h"
#include "simdprune_tables.h"

#include <initializer_list>

// Static array of known implementations. We're hoping these get baked into the executable
// without requiring a static initializer.

#if SIMDJSON_IMPLEMENTATION_HASWELL
#include "haswell/implementation.h"
namespace simdjson { namespace internal { const haswell::implementation haswell_singleton{}; } }
#endif // SIMDJSON_IMPLEMENTATION_HASWELL

#if SIMDJSON_IMPLEMENTATION_WESTMERE
#include "westmere/implementation.h"
namespace simdjson { namespace internal { const westmere::implementation westmere_singleton{}; } }
#endif // SIMDJSON_IMPLEMENTATION_WESTMERE

#if SIMDJSON_IMPLEMENTATION_ARM64
#include "arm64/implementation.h"
namespace simdjson { namespace internal { const arm64::implementation arm64_singleton{}; } }
#endif // SIMDJSON_IMPLEMENTATION_ARM64

#if SIMDJSON_IMPLEMENTATION_FALLBACK
#include "fallback/implementation.h"
namespace simdjson { namespace internal { const fallback::implementation fallback_singleton{}; } }
#endif // SIMDJSON_IMPLEMENTATION_FALLBACK

namespace simdjson {
namespace internal {

/**
 * @private Detects best supported implementation on first use, and sets it
 */
class detect_best_supported_implementation_on_first_use final : public implementation {
public:
  const std::string &name() const noexcept final { return set_best()->name(); }
  const std::string &description() const noexcept final { return set_best()->description(); }
  uint32_t required_instruction_sets() const noexcept final { return set_best()->required_instruction_sets(); }
  WARN_UNUSED error_code create_dom_parser_implementation(
    size_t capacity,
    size_t max_length,
    std::unique_ptr<internal::dom_parser_implementation>& dst
  ) const noexcept final {
    return set_best()->create_dom_parser_implementation(capacity, max_length, dst);
  }
  WARN_UNUSED error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final {
    return set_best()->minify(buf, len, dst, dst_len);
  }
  WARN_UNUSED bool validate_utf8(const char * buf, size_t len) const noexcept final override {
    return set_best()->validate_utf8(buf, len);
  }
  really_inline detect_best_supported_implementation_on_first_use() noexcept : implementation("best_supported_detector", "Detects the best supported implementation and sets it", 0) {}
private:
  const implementation *set_best() const noexcept;
};

const detect_best_supported_implementation_on_first_use detect_best_supported_implementation_on_first_use_singleton;

internal::atomic_ptr<const implementation> active_implementation{&internal::detect_best_supported_implementation_on_first_use_singleton};

const std::initializer_list<const implementation *> available_implementation_pointers {
#if SIMDJSON_IMPLEMENTATION_HASWELL
  &haswell_singleton,
#endif
#if SIMDJSON_IMPLEMENTATION_WESTMERE
  &westmere_singleton,
#endif
#if SIMDJSON_IMPLEMENTATION_ARM64
  &arm64_singleton,
#endif
#if SIMDJSON_IMPLEMENTATION_FALLBACK
  &fallback_singleton,
#endif
}; // available_implementation_pointers

// So we can return UNSUPPORTED_ARCHITECTURE from the parser when there is no support
class unsupported_implementation final : public implementation {
public:
  WARN_UNUSED error_code create_dom_parser_implementation(
    size_t,
    size_t,
    std::unique_ptr<internal::dom_parser_implementation>&
  ) const noexcept final {
    return UNSUPPORTED_ARCHITECTURE;
  }
  WARN_UNUSED error_code minify(const uint8_t *, size_t, uint8_t *, size_t &) const noexcept final override {
    return UNSUPPORTED_ARCHITECTURE;
  }
  WARN_UNUSED bool validate_utf8(const char *, size_t) const noexcept final override {
    return false; // just refuse the validate
  }
  unsupported_implementation() : implementation("unsupported", "Unsupported CPU (no detected SIMD instructions)", 0) {}
};

const unsupported_implementation unsupported_singleton{};

size_t available_implementation_list::size() const noexcept {
  return internal::available_implementation_pointers.size();
}
const implementation * const *available_implementation_list::begin() const noexcept {
  return internal::available_implementation_pointers.begin();
}
const implementation * const *available_implementation_list::end() const noexcept {
  return internal::available_implementation_pointers.end();
}
const implementation *available_implementation_list::detect_best_supported() const noexcept {
  // They are prelisted in priority order, so we just go down the list
  uint32_t supported_instruction_sets = detect_supported_architectures();
  for (const implementation *impl : internal::available_implementation_pointers) {
    uint32_t required_instruction_sets = impl->required_instruction_sets();
    if ((supported_instruction_sets & required_instruction_sets) == required_instruction_sets) { return impl; }
  }
  return &unsupported_singleton; // this should never happen?
}

const implementation *detect_best_supported_implementation_on_first_use::set_best() const noexcept {
  SIMDJSON_PUSH_DISABLE_WARNINGS
  SIMDJSON_DISABLE_DEPRECATED_WARNING // Disable CRT_SECURE warning on MSVC: manually verified this is safe
  char *force_implementation_name = getenv("SIMDJSON_FORCE_IMPLEMENTATION");
  SIMDJSON_POP_DISABLE_WARNINGS

  if (force_implementation_name) {
    auto force_implementation = available_implementations[force_implementation_name];
    if (force_implementation) {
      return active_implementation = force_implementation;
    } else {
      // Note: abort() and stderr usage within the library is forbidden.
      return active_implementation = &unsupported_singleton;
    }
  }
  return active_implementation = available_implementations.detect_best_supported();
}

} // namespace internal

SIMDJSON_DLLIMPORTEXPORT const internal::available_implementation_list available_implementations{};
SIMDJSON_DLLIMPORTEXPORT internal::atomic_ptr<const implementation> active_implementation{&internal::detect_best_supported_implementation_on_first_use_singleton};

WARN_UNUSED error_code minify(const char *buf, size_t len, char *dst, size_t &dst_len) noexcept {
  return active_implementation->minify((const uint8_t *)buf, len, (uint8_t *)dst, dst_len);
}
WARN_UNUSED bool validate_utf8(const char *buf, size_t len) noexcept {
  return active_implementation->validate_utf8(buf, len);
}

} // namespace simdjson
<commit_msg>Explaining decision.<commit_after>#include "simdjson.h"
#include "isadetection.h"
#include "simdprune_tables.h"

#include <initializer_list>

// Static array of known implementations. We're hoping these get baked into the executable
// without requiring a static initializer.

#if SIMDJSON_IMPLEMENTATION_HASWELL
#include "haswell/implementation.h"
namespace simdjson { namespace internal { const haswell::implementation haswell_singleton{}; } }
#endif // SIMDJSON_IMPLEMENTATION_HASWELL

#if SIMDJSON_IMPLEMENTATION_WESTMERE
#include "westmere/implementation.h"
namespace simdjson { namespace internal { const westmere::implementation westmere_singleton{}; } }
#endif // SIMDJSON_IMPLEMENTATION_WESTMERE

#if SIMDJSON_IMPLEMENTATION_ARM64
#include "arm64/implementation.h"
namespace simdjson { namespace internal { const arm64::implementation arm64_singleton{}; } }
#endif // SIMDJSON_IMPLEMENTATION_ARM64

#if SIMDJSON_IMPLEMENTATION_FALLBACK
#include "fallback/implementation.h"
namespace simdjson { namespace internal { const fallback::implementation fallback_singleton{}; } }
#endif // SIMDJSON_IMPLEMENTATION_FALLBACK

namespace simdjson {
namespace internal {

/**
 * @private Detects best supported implementation on first use, and sets it
 */
class detect_best_supported_implementation_on_first_use final : public implementation {
public:
  const std::string &name() const noexcept final { return set_best()->name(); }
  const std::string &description() const noexcept final { return set_best()->description(); }
  uint32_t required_instruction_sets() const noexcept final { return set_best()->required_instruction_sets(); }
  WARN_UNUSED error_code create_dom_parser_implementation(
    size_t capacity,
    size_t max_length,
    std::unique_ptr<internal::dom_parser_implementation>& dst
  ) const noexcept final {
    return set_best()->create_dom_parser_implementation(capacity, max_length, dst);
  }
  WARN_UNUSED error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final {
    return set_best()->minify(buf, len, dst, dst_len);
  }
  WARN_UNUSED bool validate_utf8(const char * buf, size_t len) const noexcept final override {
    return set_best()->validate_utf8(buf, len);
  }
  really_inline detect_best_supported_implementation_on_first_use() noexcept : implementation("best_supported_detector", "Detects the best supported implementation and sets it", 0) {}
private:
  const implementation *set_best() const noexcept;
};

const detect_best_supported_implementation_on_first_use detect_best_supported_implementation_on_first_use_singleton;

internal::atomic_ptr<const implementation> active_implementation{&internal::detect_best_supported_implementation_on_first_use_singleton};

const std::initializer_list<const implementation *> available_implementation_pointers {
#if SIMDJSON_IMPLEMENTATION_HASWELL
  &haswell_singleton,
#endif
#if SIMDJSON_IMPLEMENTATION_WESTMERE
  &westmere_singleton,
#endif
#if SIMDJSON_IMPLEMENTATION_ARM64
  &arm64_singleton,
#endif
#if SIMDJSON_IMPLEMENTATION_FALLBACK
  &fallback_singleton,
#endif
}; // available_implementation_pointers

// So we can return UNSUPPORTED_ARCHITECTURE from the parser when there is no support
class unsupported_implementation final : public implementation {
public:
  WARN_UNUSED error_code create_dom_parser_implementation(
    size_t,
    size_t,
    std::unique_ptr<internal::dom_parser_implementation>&
  ) const noexcept final {
    return UNSUPPORTED_ARCHITECTURE;
  }
  WARN_UNUSED error_code minify(const uint8_t *, size_t, uint8_t *, size_t &) const noexcept final override {
    return UNSUPPORTED_ARCHITECTURE;
  }
  WARN_UNUSED bool validate_utf8(const char *, size_t) const noexcept final override {
    return false; // Just refuse to validate. Given that we have a fallback implementation
    // it seems unlikely that unsupported_implementation will ever be used. If it is used,
    // then it will flag all strings as invalid. The alternative is to return an error_code
    // from which the user has to figure out whether the string is valid UTF-8... which seems
    // like a lot of work just to handle the very unlikely case that we have an unsupported
    // implementation. And, when it does happen (that we have an unsupported implementation),
    // what are the chances that the programmer has a fallback? Given that *we* provide the
    // fallback, it implies that the programmer would need a fallback for our fallback.
  }
  unsupported_implementation() : implementation("unsupported", "Unsupported CPU (no detected SIMD instructions)", 0) {}
};

const unsupported_implementation unsupported_singleton{};

size_t available_implementation_list::size() const noexcept {
  return internal::available_implementation_pointers.size();
}
const implementation * const *available_implementation_list::begin() const noexcept {
  return internal::available_implementation_pointers.begin();
}
const implementation * const *available_implementation_list::end() const noexcept {
  return internal::available_implementation_pointers.end();
}
const implementation *available_implementation_list::detect_best_supported() const noexcept {
  // They are prelisted in priority order, so we just go down the list
  uint32_t supported_instruction_sets = detect_supported_architectures();
  for (const implementation *impl : internal::available_implementation_pointers) {
    uint32_t required_instruction_sets = impl->required_instruction_sets();
    if ((supported_instruction_sets & required_instruction_sets) == required_instruction_sets) { return impl; }
  }
  return &unsupported_singleton; // this should never happen?
}

const implementation *detect_best_supported_implementation_on_first_use::set_best() const noexcept {
  SIMDJSON_PUSH_DISABLE_WARNINGS
  SIMDJSON_DISABLE_DEPRECATED_WARNING // Disable CRT_SECURE warning on MSVC: manually verified this is safe
  char *force_implementation_name = getenv("SIMDJSON_FORCE_IMPLEMENTATION");
  SIMDJSON_POP_DISABLE_WARNINGS

  if (force_implementation_name) {
    auto force_implementation = available_implementations[force_implementation_name];
    if (force_implementation) {
      return active_implementation = force_implementation;
    } else {
      // Note: abort() and stderr usage within the library is forbidden.
      return active_implementation = &unsupported_singleton;
    }
  }
  return active_implementation = available_implementations.detect_best_supported();
}

} // namespace internal

SIMDJSON_DLLIMPORTEXPORT const internal::available_implementation_list available_implementations{};
SIMDJSON_DLLIMPORTEXPORT internal::atomic_ptr<const implementation> active_implementation{&internal::detect_best_supported_implementation_on_first_use_singleton};

WARN_UNUSED error_code minify(const char *buf, size_t len, char *dst, size_t &dst_len) noexcept {
  return active_implementation->minify((const uint8_t *)buf, len, (uint8_t *)dst, dst_len);
}
WARN_UNUSED bool validate_utf8(const char *buf, size_t len) noexcept {
  return active_implementation->validate_utf8(buf, len);
}

} // namespace simdjson
<|endoftext|>


<commit_before>/*
 * Copyright © 2010 Intel Corporation
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

#include "ir.h"
#include "glsl_parser_extras.h"
#include "glsl_symbol_table.h"
#include "builtin_variables.h"

#ifndef Elements
#define Elements(x) (sizeof(x)/sizeof(*(x)))
#endif

static void generate_ARB_draw_buffers_fs_variables(exec_list *,
    struct _mesa_glsl_parse_state *, bool);

static ir_variable *
add_variable(const char *name, enum ir_variable_mode mode, int slot,
	     const glsl_type *type, exec_list *instructions,
		     glsl_symbol_table *symtab)
{
   ir_variable *var = new(symtab) ir_variable(type, name);

   var->mode = mode;
   switch (var->mode) {
   case ir_var_auto:
      var->read_only = true;
      break;
   case ir_var_in:
      var->shader_in = true;
      var->read_only = true;
      break;
   case ir_var_inout:
      var->shader_in = true;
      var->shader_out = true;
      break;
   case ir_var_out:
      var->shader_out = true;
      break;
   case ir_var_uniform:
      var->shader_in = true;
      var->read_only = true;
      break;
   default:
      assert(0);
      break;
   }

   var->location = slot;

   /* Once the variable is created an initialized, add it to the symbol table
    * and add the declaration to the IR stream.
    */
   instructions->push_tail(var);

   symtab->add_variable(var->name, var);
   return var;
}

static void
add_builtin_variable(const builtin_variable *proto, exec_list *instructions,
		     glsl_symbol_table *symtab)
{
   /* Create a new variable declaration from the description supplied by
    * the caller.
    */
   const glsl_type *const type = symtab->get_type(proto->type);

   assert(type != NULL);

   add_variable(proto->name, proto->mode, proto->slot, type, instructions,
		symtab);
}

static void
generate_110_uniforms(exec_list *instructions,
		      glsl_symbol_table *symtab)
{
   for (unsigned i = 0
	   ; i < Elements(builtin_110_deprecated_uniforms)
	   ; i++) {
      add_builtin_variable(& builtin_110_deprecated_uniforms[i],
			   instructions, symtab);
   }

   /* FINISHME: The size of this array is implementation dependent based on the
    * FINISHME: value of GL_MAX_TEXTURE_COORDS.  Every platform that supports
    * FINISHME: GLSL sets GL_MAX_TEXTURE_COORDS to at least 4, so hard-code 4
    * FINISHME: for now.
    */
   const glsl_type *const mat4_array_type =
      glsl_type::get_array_instance(symtab, glsl_type::mat4_type, 4);

   add_variable("gl_TextureMatrix", ir_var_uniform, -1, mat4_array_type,
		instructions, symtab);

   /* FINISHME: Add support for gl_DepthRangeParameters */
   /* FINISHME: Add support for gl_ClipPlane[] */
   /* FINISHME: Add support for gl_PointParameters */

   /* FINISHME: Add support for gl_MaterialParameters
    * FINISHME: (glFrontMaterial, glBackMaterial)
    */

   /* FINISHME: The size of this array is implementation dependent based on the
    * FINISHME: value of GL_MAX_TEXTURE_LIGHTS.  GL_MAX_TEXTURE_LIGHTS must be
    * FINISHME: at least 8, so hard-code 8 for now.
    */
   const glsl_type *const light_source_array_type =
      glsl_type::get_array_instance(symtab,
				    symtab->get_type("gl_LightSourceParameters"), 8);

   add_variable("gl_LightSource", ir_var_uniform, -1, light_source_array_type,
		instructions, symtab);

   /* FINISHME: Add support for gl_LightModel */
   /* FINISHME: Add support for gl_FrontLightProduct[], gl_BackLightProduct[] */
   /* FINISHME: Add support for gl_TextureEnvColor[] */
   /* FINISHME: Add support for gl_ObjectPlane*[], gl_EyePlane*[] */
   /* FINISHME: Add support for gl_Fog */
}

static void
generate_110_vs_variables(exec_list *instructions,
			  glsl_symbol_table *symtab)
{
   for (unsigned i = 0; i < Elements(builtin_core_vs_variables); i++) {
      add_builtin_variable(& builtin_core_vs_variables[i],
			   instructions, symtab);
   }

   for (unsigned i = 0
	   ; i < Elements(builtin_110_deprecated_vs_variables)
	   ; i++) {
      add_builtin_variable(& builtin_110_deprecated_vs_variables[i],
			   instructions, symtab);
   }
   generate_110_uniforms(instructions, symtab);

   /* FINISHME: The size of this array is implementation dependent based on the
    * FINISHME: value of GL_MAX_TEXTURE_COORDS.  Every platform that supports
    * FINISHME: GLSL sets GL_MAX_TEXTURE_COORDS to at least 4, so hard-code 4
    * FINISHME: for now.
    */
   const glsl_type *const vec4_array_type =
      glsl_type::get_array_instance(symtab, glsl_type::vec4_type, 4);

   add_variable("gl_TexCoord", ir_var_out, VERT_RESULT_TEX0, vec4_array_type,
		instructions, symtab);
}

static void
generate_120_vs_variables(exec_list *instructions,
			  glsl_symbol_table *symtab)
{
   /* GLSL version 1.20 did not add any built-in variables in the vertex
    * shader.
    */
   generate_110_vs_variables(instructions, symtab);
}

static void
generate_130_vs_variables(exec_list *instructions,
			  glsl_symbol_table *symtab)
{
   void *ctx = symtab;
   generate_120_vs_variables(instructions, symtab);

   for (unsigned i = 0; i < Elements(builtin_130_vs_variables); i++) {
      add_builtin_variable(& builtin_130_vs_variables[i],
			   instructions, symtab);
   }

   /* FINISHME: The size of this array is implementation dependent based on
    * FINISHME: the value of GL_MAX_CLIP_DISTANCES.
    */
   const glsl_type *const clip_distance_array_type =
      glsl_type::get_array_instance(ctx, glsl_type::float_type, 8);

   /* FINISHME: gl_ClipDistance needs a real location assigned. */
   add_variable("gl_ClipDistance", ir_var_out, -1, clip_distance_array_type,
		instructions, symtab);

}

static void
initialize_vs_variables(exec_list *instructions,
			struct _mesa_glsl_parse_state *state)
{

   switch (state->language_version) {
   case 110:
      generate_110_vs_variables(instructions, state->symbols);
      break;
   case 120:
      generate_120_vs_variables(instructions, state->symbols);
      break;
   case 130:
      generate_130_vs_variables(instructions, state->symbols);
      break;
   }
}

static void
generate_110_fs_variables(exec_list *instructions,
			  struct _mesa_glsl_parse_state *state)
{
   for (unsigned i = 0; i < Elements(builtin_core_fs_variables); i++) {
      add_builtin_variable(& builtin_core_fs_variables[i],
			   instructions, state->symbols);
   }

   for (unsigned i = 0
	   ; i < Elements(builtin_110_deprecated_fs_variables)
	   ; i++) {
      add_builtin_variable(& builtin_110_deprecated_fs_variables[i],
			   instructions, state->symbols);
   }
   generate_110_uniforms(instructions, state->symbols);

   /* FINISHME: The size of this array is implementation dependent based on the
    * FINISHME: value of GL_MAX_TEXTURE_COORDS.  Every platform that supports
    * FINISHME: GLSL sets GL_MAX_TEXTURE_COORDS to at least 4, so hard-code 4
    * FINISHME: for now.
    */
   const glsl_type *const vec4_array_type =
      glsl_type::get_array_instance(state->symbols, glsl_type::vec4_type, 4);

   add_variable("gl_TexCoord", ir_var_in, FRAG_ATTRIB_TEX0, vec4_array_type,
		instructions, state->symbols);

   generate_ARB_draw_buffers_fs_variables(instructions, state, false);
}

static void
generate_ARB_draw_buffers_fs_variables(exec_list *instructions,
				       struct _mesa_glsl_parse_state *state,
				       bool warn)
{
   assert(state->Const.MaxDrawBuffers >= 1);

   ir_variable *const mdb =
      add_variable("gl_MaxDrawBuffers", ir_var_auto, -1,
		   glsl_type::int_type, instructions, state->symbols);

   if (warn)
      mdb->warn_extension = "GL_ARB_draw_buffers";

   mdb->constant_value = new(mdb)
      ir_constant(int(state->Const.MaxDrawBuffers));

   const glsl_type *const vec4_array_type =
      glsl_type::get_array_instance(state->symbols, glsl_type::vec4_type,
				    state->Const.MaxDrawBuffers);

   ir_variable *const fd =
      add_variable("gl_FragData", ir_var_out, FRAG_RESULT_DATA0,
		   vec4_array_type, instructions, state->symbols);

   if (warn)
      fd->warn_extension = "GL_ARB_draw_buffers";
}

static void
generate_120_fs_variables(exec_list *instructions,
			  struct _mesa_glsl_parse_state *state)
{
   generate_110_fs_variables(instructions, state);
}

static void
generate_130_fs_variables(exec_list *instructions,
			  struct _mesa_glsl_parse_state *state)
{
   void *ctx = state->symbols;
   generate_120_fs_variables(instructions, state);

   /* FINISHME: The size of this array is implementation dependent based on
    * FINISHME: the value of GL_MAX_CLIP_DISTANCES.
    */
   const glsl_type *const clip_distance_array_type =
      glsl_type::get_array_instance(ctx, glsl_type::float_type, 8);

   /* FINISHME: gl_ClipDistance needs a real location assigned. */
   add_variable("gl_ClipDistance", ir_var_in, -1, clip_distance_array_type,
		instructions, state->symbols);
}

static void
initialize_fs_variables(exec_list *instructions,
			struct _mesa_glsl_parse_state *state)
{

   switch (state->language_version) {
   case 110:
      generate_110_fs_variables(instructions, state);
      break;
   case 120:
      generate_120_fs_variables(instructions, state);
      break;
   case 130:
      generate_130_fs_variables(instructions, state);
      break;
   }
}

void
_mesa_glsl_initialize_variables(exec_list *instructions,
				struct _mesa_glsl_parse_state *state)
{
   switch (state->target) {
   case vertex_shader:
      initialize_vs_variables(instructions, state);
      break;
   case geometry_shader:
      break;
   case fragment_shader:
      initialize_fs_variables(instructions, state);
      break;
   case ir_shader:
      fprintf(stderr, "ir reader has no builtin variables");
      exit(1);
      break;
   }
}
<commit_msg>glsl2: Make gl_MaxDrawBuffers available in the vertex shader<commit_after>/*
 * Copyright © 2010 Intel Corporation
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

#include "ir.h"
#include "glsl_parser_extras.h"
#include "glsl_symbol_table.h"
#include "builtin_variables.h"

#ifndef Elements
#define Elements(x) (sizeof(x)/sizeof(*(x)))
#endif

static void generate_ARB_draw_buffers_variables(exec_list *,
						struct _mesa_glsl_parse_state *,
						bool, _mesa_glsl_parser_targets);

static ir_variable *
add_variable(const char *name, enum ir_variable_mode mode, int slot,
	     const glsl_type *type, exec_list *instructions,
		     glsl_symbol_table *symtab)
{
   ir_variable *var = new(symtab) ir_variable(type, name);

   var->mode = mode;
   switch (var->mode) {
   case ir_var_auto:
      var->read_only = true;
      break;
   case ir_var_in:
      var->shader_in = true;
      var->read_only = true;
      break;
   case ir_var_inout:
      var->shader_in = true;
      var->shader_out = true;
      break;
   case ir_var_out:
      var->shader_out = true;
      break;
   case ir_var_uniform:
      var->shader_in = true;
      var->read_only = true;
      break;
   default:
      assert(0);
      break;
   }

   var->location = slot;

   /* Once the variable is created an initialized, add it to the symbol table
    * and add the declaration to the IR stream.
    */
   instructions->push_tail(var);

   symtab->add_variable(var->name, var);
   return var;
}

static void
add_builtin_variable(const builtin_variable *proto, exec_list *instructions,
		     glsl_symbol_table *symtab)
{
   /* Create a new variable declaration from the description supplied by
    * the caller.
    */
   const glsl_type *const type = symtab->get_type(proto->type);

   assert(type != NULL);

   add_variable(proto->name, proto->mode, proto->slot, type, instructions,
		symtab);
}

static void
generate_110_uniforms(exec_list *instructions,
		      glsl_symbol_table *symtab)
{
   for (unsigned i = 0
	   ; i < Elements(builtin_110_deprecated_uniforms)
	   ; i++) {
      add_builtin_variable(& builtin_110_deprecated_uniforms[i],
			   instructions, symtab);
   }

   /* FINISHME: The size of this array is implementation dependent based on the
    * FINISHME: value of GL_MAX_TEXTURE_COORDS.  Every platform that supports
    * FINISHME: GLSL sets GL_MAX_TEXTURE_COORDS to at least 4, so hard-code 4
    * FINISHME: for now.
    */
   const glsl_type *const mat4_array_type =
      glsl_type::get_array_instance(symtab, glsl_type::mat4_type, 4);

   add_variable("gl_TextureMatrix", ir_var_uniform, -1, mat4_array_type,
		instructions, symtab);

   /* FINISHME: Add support for gl_DepthRangeParameters */
   /* FINISHME: Add support for gl_ClipPlane[] */
   /* FINISHME: Add support for gl_PointParameters */

   /* FINISHME: Add support for gl_MaterialParameters
    * FINISHME: (glFrontMaterial, glBackMaterial)
    */

   /* FINISHME: The size of this array is implementation dependent based on the
    * FINISHME: value of GL_MAX_TEXTURE_LIGHTS.  GL_MAX_TEXTURE_LIGHTS must be
    * FINISHME: at least 8, so hard-code 8 for now.
    */
   const glsl_type *const light_source_array_type =
      glsl_type::get_array_instance(symtab,
				    symtab->get_type("gl_LightSourceParameters"), 8);

   add_variable("gl_LightSource", ir_var_uniform, -1, light_source_array_type,
		instructions, symtab);

   /* FINISHME: Add support for gl_LightModel */
   /* FINISHME: Add support for gl_FrontLightProduct[], gl_BackLightProduct[] */
   /* FINISHME: Add support for gl_TextureEnvColor[] */
   /* FINISHME: Add support for gl_ObjectPlane*[], gl_EyePlane*[] */
   /* FINISHME: Add support for gl_Fog */
}

static void
generate_110_vs_variables(exec_list *instructions,
			  struct _mesa_glsl_parse_state *state)
{
   for (unsigned i = 0; i < Elements(builtin_core_vs_variables); i++) {
      add_builtin_variable(& builtin_core_vs_variables[i],
			   instructions, state->symbols);
   }

   for (unsigned i = 0
	   ; i < Elements(builtin_110_deprecated_vs_variables)
	   ; i++) {
      add_builtin_variable(& builtin_110_deprecated_vs_variables[i],
			   instructions, state->symbols);
   }
   generate_110_uniforms(instructions, state->symbols);

   /* FINISHME: The size of this array is implementation dependent based on the
    * FINISHME: value of GL_MAX_TEXTURE_COORDS.  Every platform that supports
    * FINISHME: GLSL sets GL_MAX_TEXTURE_COORDS to at least 4, so hard-code 4
    * FINISHME: for now.
    */
   const glsl_type *const vec4_array_type =
      glsl_type::get_array_instance(state->symbols, glsl_type::vec4_type, 4);

   add_variable("gl_TexCoord", ir_var_out, VERT_RESULT_TEX0, vec4_array_type,
		instructions, state->symbols);

   generate_ARB_draw_buffers_variables(instructions, state, false,
				       vertex_shader);
}

static void
generate_120_vs_variables(exec_list *instructions,
			  struct _mesa_glsl_parse_state *state)
{
   /* GLSL version 1.20 did not add any built-in variables in the vertex
    * shader.
    */
   generate_110_vs_variables(instructions, state);
}

static void
generate_130_vs_variables(exec_list *instructions,
			  struct _mesa_glsl_parse_state *state)
{
   void *ctx = state->symbols;
   generate_120_vs_variables(instructions, state);

   for (unsigned i = 0; i < Elements(builtin_130_vs_variables); i++) {
      add_builtin_variable(& builtin_130_vs_variables[i],
			   instructions, state->symbols);
   }

   /* FINISHME: The size of this array is implementation dependent based on
    * FINISHME: the value of GL_MAX_CLIP_DISTANCES.
    */
   const glsl_type *const clip_distance_array_type =
      glsl_type::get_array_instance(ctx, glsl_type::float_type, 8);

   /* FINISHME: gl_ClipDistance needs a real location assigned. */
   add_variable("gl_ClipDistance", ir_var_out, -1, clip_distance_array_type,
		instructions, state->symbols);

}

static void
initialize_vs_variables(exec_list *instructions,
			struct _mesa_glsl_parse_state *state)
{

   switch (state->language_version) {
   case 110:
      generate_110_vs_variables(instructions, state);
      break;
   case 120:
      generate_120_vs_variables(instructions, state);
      break;
   case 130:
      generate_130_vs_variables(instructions, state);
      break;
   }
}

static void
generate_110_fs_variables(exec_list *instructions,
			  struct _mesa_glsl_parse_state *state)
{
   for (unsigned i = 0; i < Elements(builtin_core_fs_variables); i++) {
      add_builtin_variable(& builtin_core_fs_variables[i],
			   instructions, state->symbols);
   }

   for (unsigned i = 0
	   ; i < Elements(builtin_110_deprecated_fs_variables)
	   ; i++) {
      add_builtin_variable(& builtin_110_deprecated_fs_variables[i],
			   instructions, state->symbols);
   }
   generate_110_uniforms(instructions, state->symbols);

   /* FINISHME: The size of this array is implementation dependent based on the
    * FINISHME: value of GL_MAX_TEXTURE_COORDS.  Every platform that supports
    * FINISHME: GLSL sets GL_MAX_TEXTURE_COORDS to at least 4, so hard-code 4
    * FINISHME: for now.
    */
   const glsl_type *const vec4_array_type =
      glsl_type::get_array_instance(state->symbols, glsl_type::vec4_type, 4);

   add_variable("gl_TexCoord", ir_var_in, FRAG_ATTRIB_TEX0, vec4_array_type,
		instructions, state->symbols);

   generate_ARB_draw_buffers_variables(instructions, state, false,
				       fragment_shader);
}

static void
generate_ARB_draw_buffers_variables(exec_list *instructions,
				    struct _mesa_glsl_parse_state *state,
				    bool warn, _mesa_glsl_parser_targets target)
{
   /* gl_MaxDrawBuffers is available in all shader stages.
    */
   ir_variable *const mdb =
      add_variable("gl_MaxDrawBuffers", ir_var_auto, -1,
		   glsl_type::int_type, instructions, state->symbols);

   if (warn)
      mdb->warn_extension = "GL_ARB_draw_buffers";

   mdb->constant_value = new(mdb)
      ir_constant(int(state->Const.MaxDrawBuffers));

   /* gl_FragData is only available in the fragment shader.
    */
   if (target == fragment_shader) {
      const glsl_type *const vec4_array_type =
	 glsl_type::get_array_instance(state->symbols, glsl_type::vec4_type,
				       state->Const.MaxDrawBuffers);

      ir_variable *const fd =
	 add_variable("gl_FragData", ir_var_out, FRAG_RESULT_DATA0,
		      vec4_array_type, instructions, state->symbols);

      if (warn)
	 fd->warn_extension = "GL_ARB_draw_buffers";
   }
}

static void
generate_120_fs_variables(exec_list *instructions,
			  struct _mesa_glsl_parse_state *state)
{
   generate_110_fs_variables(instructions, state);
}

static void
generate_130_fs_variables(exec_list *instructions,
			  struct _mesa_glsl_parse_state *state)
{
   void *ctx = state->symbols;
   generate_120_fs_variables(instructions, state);

   /* FINISHME: The size of this array is implementation dependent based on
    * FINISHME: the value of GL_MAX_CLIP_DISTANCES.
    */
   const glsl_type *const clip_distance_array_type =
      glsl_type::get_array_instance(ctx, glsl_type::float_type, 8);

   /* FINISHME: gl_ClipDistance needs a real location assigned. */
   add_variable("gl_ClipDistance", ir_var_in, -1, clip_distance_array_type,
		instructions, state->symbols);
}

static void
initialize_fs_variables(exec_list *instructions,
			struct _mesa_glsl_parse_state *state)
{

   switch (state->language_version) {
   case 110:
      generate_110_fs_variables(instructions, state);
      break;
   case 120:
      generate_120_fs_variables(instructions, state);
      break;
   case 130:
      generate_130_fs_variables(instructions, state);
      break;
   }
}

void
_mesa_glsl_initialize_variables(exec_list *instructions,
				struct _mesa_glsl_parse_state *state)
{
   switch (state->target) {
   case vertex_shader:
      initialize_vs_variables(instructions, state);
      break;
   case geometry_shader:
      break;
   case fragment_shader:
      initialize_fs_variables(instructions, state);
      break;
   case ir_shader:
      fprintf(stderr, "ir reader has no builtin variables");
      exit(1);
      break;
   }
}
<|endoftext|>