<commit_before>// Sh: A GPU metaprogramming language.
//
// Copyright 2003-2005 Serious Hack Inc.
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must
// not claim that you wrote the original software. If you use this
// software in a product, an acknowledgment in the product documentation
// would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must
// not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source
// distribution.
//////////////////////////////////////////////////////////////////////////////
#include "ShImage.hpp"
#include <string>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <png.h>
#include <sstream>
#include "ShException.hpp"
#include "ShError.hpp"
#include "ShDebug.hpp"

namespace SH {

ShImage::ShImage()
  : m_width(0), m_height(0), m_elements(0), m_memory(0)
{
}

ShImage::ShImage(int width, int height, int elements)
  : m_width(width), m_height(height), m_elements(elements),
    m_memory(new ShHostMemory(sizeof(float) * m_width * m_height * m_elements))
{
}

ShImage::ShImage(const ShImage& other)
  : m_width(other.m_width), m_height(other.m_height), m_elements(other.m_elements),
    m_memory(other.m_memory ? new ShHostMemory(sizeof(float) * m_width * m_height * m_elements) : 0)
{
  if (m_memory) {
    std::memcpy(m_memory->hostStorage()->data(),
                other.m_memory->hostStorage()->data(),
                m_width * m_height * m_elements * sizeof(float));
  }
}

ShImage::~ShImage()
{
}

ShImage& ShImage::operator=(const ShImage& other)
{
  m_width = other.m_width;
  m_height = other.m_height;
  m_elements = other.m_elements;
  m_memory = (other.m_memory ? new ShHostMemory(sizeof(float) * m_width * m_height * m_elements) : 0);
  std::memcpy(m_memory->hostStorage()->data(),
              other.m_memory->hostStorage()->data(),
              m_width * m_height * m_elements * sizeof(float));

  return *this;
}

int ShImage::width() const
{
  return m_width;
}

int ShImage::height() const
{
  return m_height;
}

int ShImage::elements() const
{
  return m_elements;
}

float ShImage::operator()(int x, int y, int i) const
{
  SH_DEBUG_ASSERT(m_memory);
  return data()[m_elements * (m_width * y + x) + i];
}

float& ShImage::operator()(int x, int y, int i)
{
  return data()[m_elements * (m_width * y + x) + i];
}

void ShImage::savePng(const std::string& filename, int inverse_alpha)
{
  FILE* fout = std::fopen(filename.c_str(), "wb");
  png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
  png_infop info_ptr = png_create_info_struct(png_ptr);
  setjmp(png_ptr->jmpbuf);

  /* Setup PNG I/O */
  png_init_io(png_ptr, fout);

  /* Optionally setup a callback to indicate when a row has been
   * written. */

  /* Setup filtering. Use Paeth filtering */
  png_set_filter(png_ptr, 0, PNG_FILTER_PAETH);

  /* Setup compression level. */
  png_set_compression_level(png_ptr, Z_BEST_COMPRESSION);

  /* Setup PNG header information and write it to the file */

  int color_type;
  switch (m_elements) {
  case 1:
    color_type = PNG_COLOR_TYPE_GRAY;
    break;
  case 2:
    color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
    break;
  case 3:
    color_type = PNG_COLOR_TYPE_RGB;
    break;
  case 4:
    color_type = PNG_COLOR_TYPE_RGBA;
    break;
  default:
    throw ShImageException("Invalid element size");
  }

  png_set_IHDR(png_ptr, info_ptr,
               m_width, m_height,
               8,
               color_type,
               PNG_INTERLACE_NONE,
               PNG_COMPRESSION_TYPE_DEFAULT,
               PNG_FILTER_TYPE_DEFAULT);
  png_write_info(png_ptr, info_ptr);

  // Actual writing
  png_byte* tempLine = (png_byte*)malloc(m_width * sizeof(png_byte) * m_elements);

  for(int i=0;i<m_height;i+=1){
    for(int j=0;j<m_width;j+=1){
      for(int k = 0;k<m_elements;k+=1)
        tempLine[m_elements*j+k] = static_cast<png_byte>((*this)(j, i, k)*255.0);

      // inverse alpha
      if(inverse_alpha && m_elements == 4)
        tempLine[m_elements*j+3] = 255 - tempLine[m_elements*j+3];
    }
    png_write_row(png_ptr, tempLine);
  }

  // closing and freeing the structs
  png_write_end(png_ptr, info_ptr);
  png_destroy_write_struct(&png_ptr, &info_ptr);
  free(tempLine);
  fclose(fout);
}

void ShImage::loadPng(const std::string& filename)
{
  // check that the file is a png file
  png_byte buf[8];

  FILE* in = std::fopen(filename.c_str(), "rb");

  if (!in) shError( ShImageException("Unable to open " + filename) );

  for (int i = 0; i < 8; i++) {
    if (!(buf[i] = fgetc(in))) shError( ShImageException("Not a PNG file") );
  }
  if (png_sig_cmp(buf, 0, 8)) shError( ShImageException("Not a PNG file") );

  png_structp png_ptr =
    png_create_read_struct(PNG_LIBPNG_VER_STRING,
			   0, 0, 0); // FIXME: use error handlers
  if (!png_ptr) shError( ShImageException("Error initialising libpng (png_ptr)") );

  png_infop info_ptr = png_create_info_struct(png_ptr);
  if (!info_ptr) {
    png_destroy_read_struct(&png_ptr, 0, 0);
    shError( ShImageException("Error initialising libpng (info_ptr)") );
  }

  if (setjmp(png_jmpbuf(png_ptr))) {
    png_destroy_read_struct(&png_ptr, 0, 0);
    shError( ShImageException("Error initialising libpng (setjmp/lngjmp)") );
  }

  //  png_set_read_fn(png_ptr, reinterpret_cast<void*>(&in), my_istream_read_data);
  png_init_io(png_ptr, in);

  png_set_sig_bytes(png_ptr, 8);

  png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, 0);

  int bit_depth = png_get_bit_depth(png_ptr, info_ptr);
  if (bit_depth % 8) {
    png_destroy_read_struct(&png_ptr, 0, 0);
    std::ostringstream os;
    os << "Invalid bit elements " << bit_depth;
    shError( ShImageException(os.str()) );
  }

  int colour_type = png_get_color_type(png_ptr, info_ptr);

  if (colour_type == PNG_COLOR_TYPE_PALETTE) {
    png_set_palette_to_rgb(png_ptr);
    colour_type = PNG_COLOR_TYPE_RGB;
  }

  if (colour_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
    png_set_gray_1_2_4_to_8(png_ptr);
  }

  if (colour_type != PNG_COLOR_TYPE_RGB
      && colour_type != PNG_COLOR_TYPE_GRAY
      && colour_type != PNG_COLOR_TYPE_RGBA) {
    png_destroy_read_struct(&png_ptr, 0, 0);
    shError( ShImageException("Invalid colour type") );
  }

  m_memory = 0;

  m_width = png_get_image_width(png_ptr, info_ptr);
  m_height = png_get_image_height(png_ptr, info_ptr);
  switch (colour_type) {
  case PNG_COLOR_TYPE_RGB:
    m_elements = 3;
    break;
  case PNG_COLOR_TYPE_RGBA:
    m_elements = 4;
    break;
  default:
    m_elements = 1;
    break;
  }

  png_bytep* row_pointers = png_get_rows(png_ptr, info_ptr);

  m_memory = new ShHostMemory(sizeof(float) * m_width * m_height * m_elements);

  for (int y = 0; y < m_height; y++) {
    for (int x = 0; x < m_width; x++) {
      for (int i = 0; i < m_elements; i++) {
	png_byte *row = row_pointers[y];
	int index = m_elements * (y * m_width + x) + i;

  long element = 0;
  for (int j = bit_depth/8 - 1; j >= 0; j--) {
    element <<= 8;
    element += row[(x * m_elements + i) * bit_depth/8 + j];
  }

	data()[index] = element / static_cast<float>((1 << bit_depth) - 1);
      }
    }
  }

  png_destroy_read_struct(&png_ptr, &info_ptr, 0);
}

void ShImage::savePng16(const std::string& filename, int inverse_alpha)
{
  FILE* fout = std::fopen(filename.c_str(), "w");
  png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
  png_infop info_ptr = png_create_info_struct(png_ptr);
  setjmp(png_ptr->jmpbuf);

  /* Setup PNG I/O */
  png_init_io(png_ptr, fout);

  /* Optionally setup a callback to indicate when a row has been
   * written. */

  /* Setup filtering. Use Paeth filtering */
  png_set_filter(png_ptr, 0, PNG_FILTER_PAETH);

  /* Setup compression level. */
  png_set_compression_level(png_ptr, Z_BEST_COMPRESSION);

  /* Setup PNG header information and write it to the file */

  int color_type;
  switch (m_elements) {
  case 1:
    color_type = PNG_COLOR_TYPE_GRAY;
    break;
  case 2:
    color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
    break;
  case 3:
    color_type = PNG_COLOR_TYPE_RGB;
    break;
  case 4:
    color_type = PNG_COLOR_TYPE_RGBA;
    break;
  default:
    throw ShImageException("Invalid element size");
  }

  png_set_IHDR(png_ptr, info_ptr,
               m_width, m_height,
               16,
               color_type,
               PNG_INTERLACE_NONE,
               PNG_COMPRESSION_TYPE_DEFAULT,
               PNG_FILTER_TYPE_DEFAULT);
  png_write_info(png_ptr, info_ptr);

  // Actual writing
  png_uint_16* tempLine = (png_uint_16*)malloc(m_width * sizeof(png_uint_16) * m_elements);

  for(int i=0;i<m_height;i+=1){
    for(int j=0;j<m_width;j+=1){
      for(int k = 0;k<m_elements;k+=1)
        tempLine[m_elements*j+k] = static_cast<png_uint_16>((*this)(j, i, k)*65535.0);

      // inverse alpha
      if(inverse_alpha && m_elements == 4)
        tempLine[m_elements*j+3] = 65535 - tempLine[m_elements*j+3];
    }
    png_write_row(png_ptr, (png_byte*)tempLine);
  }

  // closing and freeing the structs
  png_write_end(png_ptr, info_ptr);
  png_destroy_write_struct(&png_ptr, &info_ptr);
  free(tempLine);
  fclose(fout);
}

ShImage ShImage::getNormalImage()
{
  int w = width();
  int h = height();
  ShImage output_image(w,h,3);
  for (int j = 0; j < h; j++) {
    int jp1 = j + 1;
    if (jp1 >= h) jp1 = 0;
    int jm1 = (j - 1);
    if (jm1 < 0) jm1 = h - 1;
    for (int i = 0; i < w; i++) {
      int ip1 = i + 1;
      if (ip1 >= w) ip1 = 0;
      int im1 = (i - 1);
      if (im1 < 0) im1 = w - 1;
      float x, y, z;
      x = ((*this)(ip1,j,0) - (*this)(im1,j,0))/2.0f;
      output_image(i,j,0) = x/2.0f + 0.5f;
      y = ((*this)(i,jp1,0) - (*this)(i,jm1,0))/2.0f;
      output_image(i,j,1) = y/2.0f + 0.5f;
      z = x*x + y*y;
      z = (z > 1.0f) ? z = 0.0f : sqrt(1 - z);
      output_image(i,j,2) = z;
    }
  }
  return output_image;
}

const float* ShImage::data() const
{
  if (!m_memory) return 0;
  return reinterpret_cast<const float*>(m_memory->hostStorage()->data());
}

float* ShImage::data()
{
  if (!m_memory) return 0;
  return reinterpret_cast<float*>(m_memory->hostStorage()->data());
}

void ShImage::dirty()
{
  if (!m_memory) return;
  m_memory->hostStorage()->dirty();
}

ShMemoryPtr ShImage::memory()
{
  return m_memory;
}

ShPointer<const ShMemory> ShImage::memory() const
{
  return m_memory;
}

}
<commit_msg>Fix compilation error<commit_after>// Sh: A GPU metaprogramming language.
//
// Copyright 2003-2005 Serious Hack Inc.
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must
// not claim that you wrote the original software. If you use this
// software in a product, an acknowledgment in the product documentation
// would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must
// not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source
// distribution.
//////////////////////////////////////////////////////////////////////////////
#include "ShImage.hpp"
#include <string>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <png.h>
#include <sstream>
#include "ShException.hpp"
#include "ShError.hpp"
#include "ShDebug.hpp"

namespace SH {

ShImage::ShImage()
  : m_width(0), m_height(0), m_elements(0), m_memory(0)
{
}

ShImage::ShImage(int width, int height, int elements)
  : m_width(width), m_height(height), m_elements(elements),
    m_memory(new ShHostMemory(sizeof(float) * m_width * m_height * m_elements))
{
}

ShImage::ShImage(const ShImage& other)
  : m_width(other.m_width), m_height(other.m_height), m_elements(other.m_elements),
    m_memory(other.m_memory ? new ShHostMemory(sizeof(float) * m_width * m_height * m_elements) : 0)
{
  if (m_memory) {
    std::memcpy(m_memory->hostStorage()->data(),
                other.m_memory->hostStorage()->data(),
                m_width * m_height * m_elements * sizeof(float));
  }
}

ShImage::~ShImage()
{
}

ShImage& ShImage::operator=(const ShImage& other)
{
  m_width = other.m_width;
  m_height = other.m_height;
  m_elements = other.m_elements;
  m_memory = (other.m_memory ? new ShHostMemory(sizeof(float) * m_width * m_height * m_elements) : 0);
  std::memcpy(m_memory->hostStorage()->data(),
              other.m_memory->hostStorage()->data(),
              m_width * m_height * m_elements * sizeof(float));

  return *this;
}

int ShImage::width() const
{
  return m_width;
}

int ShImage::height() const
{
  return m_height;
}

int ShImage::elements() const
{
  return m_elements;
}

float ShImage::operator()(int x, int y, int i) const
{
  SH_DEBUG_ASSERT(m_memory);
  return data()[m_elements * (m_width * y + x) + i];
}

float& ShImage::operator()(int x, int y, int i)
{
  return data()[m_elements * (m_width * y + x) + i];
}

void ShImage::savePng(const std::string& filename, int inverse_alpha)
{
  FILE* fout = std::fopen(filename.c_str(), "wb");
  png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
  png_infop info_ptr = png_create_info_struct(png_ptr);
  setjmp(png_ptr->jmpbuf);

  /* Setup PNG I/O */
  png_init_io(png_ptr, fout);

  /* Optionally setup a callback to indicate when a row has been
   * written. */

  /* Setup filtering. Use Paeth filtering */
  png_set_filter(png_ptr, 0, PNG_FILTER_PAETH);

  /* Setup compression level. */
  png_set_compression_level(png_ptr, Z_BEST_COMPRESSION);

  /* Setup PNG header information and write it to the file */

  int color_type;
  switch (m_elements) {
  case 1:
    color_type = PNG_COLOR_TYPE_GRAY;
    break;
  case 2:
    color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
    break;
  case 3:
    color_type = PNG_COLOR_TYPE_RGB;
    break;
  case 4:
    color_type = PNG_COLOR_TYPE_RGBA;
    break;
  default:
    throw ShImageException("Invalid element size");
  }

  png_set_IHDR(png_ptr, info_ptr,
               m_width, m_height,
               8,
               color_type,
               PNG_INTERLACE_NONE,
               PNG_COMPRESSION_TYPE_DEFAULT,
               PNG_FILTER_TYPE_DEFAULT);
  png_write_info(png_ptr, info_ptr);

  // Actual writing
  png_byte* tempLine = (png_byte*)malloc(m_width * sizeof(png_byte) * m_elements);

  for(int i=0;i<m_height;i+=1){
    for(int j=0;j<m_width;j+=1){
      for(int k = 0;k<m_elements;k+=1)
        tempLine[m_elements*j+k] = static_cast<png_byte>((*this)(j, i, k)*255.0);

      // inverse alpha
      if(inverse_alpha && m_elements == 4)
        tempLine[m_elements*j+3] = 255 - tempLine[m_elements*j+3];
    }
    png_write_row(png_ptr, tempLine);
  }

  // closing and freeing the structs
  png_write_end(png_ptr, info_ptr);
  png_destroy_write_struct(&png_ptr, &info_ptr);
  free(tempLine);
  fclose(fout);
}

void ShImage::loadPng(const std::string& filename)
{
  // check that the file is a png file
  png_byte buf[8];

  FILE* in = std::fopen(filename.c_str(), "rb");

  if (!in) shError( ShImageException("Unable to open " + filename) );

  for (int i = 0; i < 8; i++) {
    if (!(buf[i] = fgetc(in))) shError( ShImageException("Not a PNG file") );
  }
  if (png_sig_cmp(buf, 0, 8)) shError( ShImageException("Not a PNG file") );

  png_structp png_ptr =
    png_create_read_struct(PNG_LIBPNG_VER_STRING,
			   0, 0, 0); // FIXME: use error handlers
  if (!png_ptr) shError( ShImageException("Error initialising libpng (png_ptr)") );

  png_infop info_ptr = png_create_info_struct(png_ptr);
  if (!info_ptr) {
    png_destroy_read_struct(&png_ptr, 0, 0);
    shError( ShImageException("Error initialising libpng (info_ptr)") );
  }

  if (setjmp(png_jmpbuf(png_ptr))) {
    png_destroy_read_struct(&png_ptr, 0, 0);
    shError( ShImageException("Error initialising libpng (setjmp/lngjmp)") );
  }

  //  png_set_read_fn(png_ptr, reinterpret_cast<void*>(&in), my_istream_read_data);
  png_init_io(png_ptr, in);

  png_set_sig_bytes(png_ptr, 8);

  png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, 0);

  int bit_depth = png_get_bit_depth(png_ptr, info_ptr);
  if (bit_depth % 8) {
    png_destroy_read_struct(&png_ptr, 0, 0);
    std::ostringstream os;
    os << "Invalid bit elements " << bit_depth;
    shError( ShImageException(os.str()) );
  }

  int colour_type = png_get_color_type(png_ptr, info_ptr);

  if (colour_type == PNG_COLOR_TYPE_PALETTE) {
    png_set_palette_to_rgb(png_ptr);
    colour_type = PNG_COLOR_TYPE_RGB;
  }

  if (colour_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
    png_set_gray_1_2_4_to_8(png_ptr);
  }

  if (colour_type != PNG_COLOR_TYPE_RGB
      && colour_type != PNG_COLOR_TYPE_GRAY
      && colour_type != PNG_COLOR_TYPE_RGBA) {
    png_destroy_read_struct(&png_ptr, 0, 0);
    shError( ShImageException("Invalid colour type") );
  }

  m_memory = 0;

  m_width = png_get_image_width(png_ptr, info_ptr);
  m_height = png_get_image_height(png_ptr, info_ptr);
  switch (colour_type) {
  case PNG_COLOR_TYPE_RGB:
    m_elements = 3;
    break;
  case PNG_COLOR_TYPE_RGBA:
    m_elements = 4;
    break;
  default:
    m_elements = 1;
    break;
  }

  png_bytep* row_pointers = png_get_rows(png_ptr, info_ptr);

  m_memory = new ShHostMemory(sizeof(float) * m_width * m_height * m_elements);

  for (int y = 0; y < m_height; y++) {
    for (int x = 0; x < m_width; x++) {
      for (int i = 0; i < m_elements; i++) {
	png_byte *row = row_pointers[y];
	int index = m_elements * (y * m_width + x) + i;

  long element = 0;
  for (int j = bit_depth/8 - 1; j >= 0; j--) {
    element <<= 8;
    element += row[(x * m_elements + i) * bit_depth/8 + j];
  }

	data()[index] = element / static_cast<float>((1 << bit_depth) - 1);
      }
    }
  }

  png_destroy_read_struct(&png_ptr, &info_ptr, 0);
}

void ShImage::savePng16(const std::string& filename, int inverse_alpha)
{
  FILE* fout = std::fopen(filename.c_str(), "w");
  png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
  png_infop info_ptr = png_create_info_struct(png_ptr);
  setjmp(png_ptr->jmpbuf);

  /* Setup PNG I/O */
  png_init_io(png_ptr, fout);

  /* Optionally setup a callback to indicate when a row has been
   * written. */

  /* Setup filtering. Use Paeth filtering */
  png_set_filter(png_ptr, 0, PNG_FILTER_PAETH);

  /* Setup compression level. */
  png_set_compression_level(png_ptr, Z_BEST_COMPRESSION);

  /* Setup PNG header information and write it to the file */

  int color_type;
  switch (m_elements) {
  case 1:
    color_type = PNG_COLOR_TYPE_GRAY;
    break;
  case 2:
    color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
    break;
  case 3:
    color_type = PNG_COLOR_TYPE_RGB;
    break;
  case 4:
    color_type = PNG_COLOR_TYPE_RGBA;
    break;
  default:
    throw ShImageException("Invalid element size");
  }

  png_set_IHDR(png_ptr, info_ptr,
               m_width, m_height,
               16,
               color_type,
               PNG_INTERLACE_NONE,
               PNG_COMPRESSION_TYPE_DEFAULT,
               PNG_FILTER_TYPE_DEFAULT);
  png_write_info(png_ptr, info_ptr);

  // Actual writing
  png_uint_16* tempLine = (png_uint_16*)malloc(m_width * sizeof(png_uint_16) * m_elements);

  for(int i=0;i<m_height;i+=1){
    for(int j=0;j<m_width;j+=1){
      for(int k = 0;k<m_elements;k+=1)
        tempLine[m_elements*j+k] = static_cast<png_uint_16>((*this)(j, i, k)*65535.0);

      // inverse alpha
      if(inverse_alpha && m_elements == 4)
        tempLine[m_elements*j+3] = 65535 - tempLine[m_elements*j+3];
    }
    png_write_row(png_ptr, (png_byte*)tempLine);
  }

  // closing and freeing the structs
  png_write_end(png_ptr, info_ptr);
  png_destroy_write_struct(&png_ptr, &info_ptr);
  free(tempLine);
  fclose(fout);
}

ShImage ShImage::getNormalImage()
{
  int w = width();
  int h = height();
  ShImage output_image(w,h,3);
  for (int j = 0; j < h; j++) {
    int jp1 = j + 1;
    if (jp1 >= h) jp1 = 0;
    int jm1 = (j - 1);
    if (jm1 < 0) jm1 = h - 1;
    for (int i = 0; i < w; i++) {
      int ip1 = i + 1;
      if (ip1 >= w) ip1 = 0;
      int im1 = (i - 1);
      if (im1 < 0) im1 = w - 1;
      float x, y, z;
      x = ((*this)(ip1,j,0) - (*this)(im1,j,0))/2.0f;
      output_image(i,j,0) = x/2.0f + 0.5f;
      y = ((*this)(i,jp1,0) - (*this)(i,jm1,0))/2.0f;
      output_image(i,j,1) = y/2.0f + 0.5f;
      z = x*x + y*y;
      z = (z > 1.0f) ? z = 0.0f : std::sqrt(1 - z);
      output_image(i,j,2) = z;
    }
  }
  return output_image;
}

const float* ShImage::data() const
{
  if (!m_memory) return 0;
  return reinterpret_cast<const float*>(m_memory->hostStorage()->data());
}

float* ShImage::data()
{
  if (!m_memory) return 0;
  return reinterpret_cast<float*>(m_memory->hostStorage()->data());
}

void ShImage::dirty()
{
  if (!m_memory) return;
  m_memory->hostStorage()->dirty();
}

ShMemoryPtr ShImage::memory()
{
  return m_memory;
}

ShPointer<const ShMemory> ShImage::memory() const
{
  return m_memory;
}

}
<|endoftext|>


<commit_before>/*
    Copyright (C) 1998 by Jorrit Tyberghein

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

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

    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the Free
    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include <math.h>
#include <stdarg.h>

#include "sysdef.h"
#include "txtmgr.h"
#include "csutil/util.h"
#include "csutil/inifile.h"
#include "qint.h"
#include "iimage.h"
#include "isystem.h"
#include "lightdef.h"

/// This is not too good from theoretical point of view,
/// however I do not see other efficient solutions. An alternative could
/// be to store such a pointer into each csTextureMM, but this is definitely
/// a waste of memory.
static csTextureManager *texman = NULL;

//---------------------------------------------------------- csTextureMM -----//

IMPLEMENT_IBASE (csTextureMM)
  IMPLEMENTS_INTERFACE (iTextureHandle)
IMPLEMENT_IBASE_END

csTextureMM::csTextureMM (iImage* Image, int Flags)
{
  CONSTRUCT_IBASE (NULL);

  (image = Image)->IncRef ();
  flags = Flags;

  tex [0] = tex [1] = tex [2] = tex [3] = NULL;

  transp = false;
  transp_color.red = transp_color.green = transp_color.blue = 0;
  cachedata = NULL;
  gamma_aplied = false;
}

csTextureMM::~csTextureMM ()
{
  for (int i = 0; i < 4; i++)
    CHKB (delete tex [i]);
  free_image ();
}

void csTextureMM::free_image ()
{
  if (!image) return;
  image->DecRef ();
  image = NULL;
}

void csTextureMM::create_mipmaps (bool verynice, bool blend_mipmap0)
{
  if (!image) return;

  // Delete existing mipmaps, if any
  for (int i = 0; i < 4; i++)
    CHKB (delete tex [i]);

  int step = verynice ? 0 : 1;

  RGBPixel *tc = transp ? &transp_color : (RGBPixel *)NULL;

  if (flags & CS_TEXTURE_3D)
  {
    iImage *i0 = blend_mipmap0 ? image->MipMap (0, tc) : (image->IncRef (), image);
#if 1
    // Create each new level by creating a level 2 mipmap from previous level
    iImage *i1 = i0->MipMap (step, tc);
    iImage *i2 = i1->MipMap (1, tc);
    iImage *i3 = i2->MipMap (1, tc);
#else
    // Create each mipmap level from original texture
    iImage *i1 = image->MipMap (step++, tc);
    iImage *i2 = image->MipMap (step++, tc);
    iImage *i3 = image->MipMap (step++, tc);
#endif

    tex [0] = new_texture (i0);
    tex [1] = new_texture (i1);
    tex [2] = new_texture (i2);
    tex [3] = new_texture (i3);
  }
  else
  {
    // 2D textures uses just the top-level mipmap
    image->IncRef ();
    tex [0] = new_texture (image);
  }

  compute_mean_color ();
}

void csTextureMM::SetTransparent (bool Enable)
{
  transp = Enable;
}

// This function must be called BEFORE calling TextureManager::Update().
void csTextureMM::SetTransparent (UByte red, UByte green, UByte blue)
{
  transp_color.red = red;
  transp_color.green = green;
  transp_color.blue = blue;
  transp = true;
}

/// Get the transparent color
void csTextureMM::GetTransparent (UByte &r, UByte &g, UByte &b)
{
  r = transp_color.red;
  g = transp_color.green;
  b = transp_color.blue;
}

bool csTextureMM::GetTransparent ()
{
  return transp;
}

void csTextureMM::GetMeanColor (UByte &r, UByte &g, UByte &b)
{
  r = mean_color.red;
  g = mean_color.green;
  b = mean_color.blue;
}

csTexture* csTextureMM::get_texture (int lev)
{
  return (lev >= 0) && (lev < 4) ? tex [lev] : NULL;
}

bool csTextureMM::GetMipMapDimensions (int mipmap, int& w, int& h)
{
  csTexture *txt = get_texture (mipmap);
  if (txt)
  {
    w = txt->get_width ();
    h = txt->get_height ();
    return true;
  }
  return false;
}

void *csTextureMM::GetMipMapData (int mipmap)
{
  csTexture *txt = get_texture (mipmap);
  return txt ? txt->get_bitmap () : NULL;
}

void csTextureMM::adjust_size_po2 ()
{
  int newwidth  = image->GetWidth();
  int newheight = image->GetHeight();

  if (!IsPowerOf2(newwidth))
    newwidth = FindNearestPowerOf2 (image->GetWidth ()) / 2;

  if (!IsPowerOf2 (newheight))
    newheight = FindNearestPowerOf2 (image->GetHeight ()) / 2;

  if (newwidth != image->GetWidth () || newheight != image->GetHeight ())
    image->Rescale (newwidth, newheight);
}

void csTextureMM::apply_gamma ()
{
  if (gamma_aplied || !image)
    return;
  gamma_aplied = true;

  if (texman->Gamma == 1.0)
    return;

  RGBPixel *src = NULL;
  int pixels = 0;
  switch (image->GetFormat () & CS_IMGFMT_MASK)
  {
    case CS_IMGFMT_TRUECOLOR:
      src = (RGBPixel *)image->GetImageData ();
      pixels = image->GetWidth () * image->GetHeight ();
      break;
    case CS_IMGFMT_PALETTED8:
      src = image->GetPalette ();
      pixels = 256;
      break;
  }
  if (!src) return;
  while (pixels--)
  {
    src->red   = texman->GammaTable [src->red];
    src->green = texman->GammaTable [src->green];
    src->blue  = texman->GammaTable [src->blue];
    src++;
  }
}

//------------------------------------------------------------ csTexture -----//

void csTexture::compute_masks ()
{
  shf_w = csLog2 (w);
  and_w = (1 << shf_w) - 1;
  shf_h = csLog2 (h);
  and_h = (1 << shf_h) - 1;
}

//----------------------------------------------------- csTextureManager -----//

IMPLEMENT_IBASE (csTextureManager)
  IMPLEMENTS_INTERFACE (iTextureManager)
IMPLEMENT_IBASE_END

csTextureManager::csTextureManager (iSystem* iSys, iGraphics2D *iG2D)
  : textures (16, 16)
{
  texman = this;

  System = iSys;
  verbose = false;

  Gamma = 1.0;
  mipmap_mode = MIPMAP_NICE;
  do_blend_mipmap0 = false;
  pfmt = *iG2D->GetPixelFormat ();
}

csTextureManager::~csTextureManager()
{
  Clear ();
}

void csTextureManager::read_config (csIniFile *config)
{
  Gamma = config->GetFloat ("TextureManager", "GAMMA", 1.0);
  do_blend_mipmap0 = config->GetYesNo ("TextureManager", "BLEND_MIPMAP", false);
  const char *p = config->GetStr ("TextureManager", "MIPMAP_MODE", "nice");
  if (!strcmp (p, "nice"))
  {
    mipmap_mode = MIPMAP_NICE;
    if (verbose)
      System->Printf (MSG_INITIALIZATION, "Mipmap calculation 'nice'.\n");
  }
  else if (!strcmp (p, "verynice"))
  {
    mipmap_mode = MIPMAP_VERYNICE;
    if (verbose)
      System->Printf (MSG_INITIALIZATION, "Mipmap calculation 'verynice'\n");
  }
  else
    System->Printf (MSG_FATAL_ERROR, "Bad value '%s' for MIPMAP_MODE!\n(Use 'verynice' or 'nice')\n", p);
  compute_gamma_table ();
}

void csTextureManager::FreeImages ()
{
  for (int i = 0 ; i < textures.Length () ; i++)
    textures.Get (i)->free_image ();
}

int csTextureManager::GetTextureFormat ()
{
  return CS_IMGFMT_TRUECOLOR;
}

void csTextureManager::compute_gamma_table ()
{
  for (int i = 0; i < 256; i++)
    GammaTable [i] = QRound (255 * pow (i / 255.0, Gamma));
}

int csTextureManager::FindRGB (int r, int g, int b)
{
  if (r > 255) r = 255; else if (r < 0) r = 0;
  if (g > 255) g = 255; else if (g < 0) g = 0;
  if (b > 255) b = 255; else if (b < 0) b = 0;
  return
    ((r >> (8-pfmt.RedBits))   << pfmt.RedShift) |
    ((g >> (8-pfmt.GreenBits)) << pfmt.GreenShift) |
    ((b >> (8-pfmt.BlueBits))  << pfmt.BlueShift);
}

void csTextureManager::ReserveColor (int /*r*/, int /*g*/, int /*b*/)
{
}

void csTextureManager::SetPalette ()
{
}

void csTextureManager::ResetPalette ()
{
}
<commit_msg>Adjusted create_mipmap to reflect the new flag CS_TEXTURE_MMLEVEL_MASK.<commit_after>/*
    Copyright (C) 1998 by Jorrit Tyberghein

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

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

    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the Free
    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include <math.h>
#include <stdarg.h>

#include "sysdef.h"
#include "txtmgr.h"
#include "csutil/util.h"
#include "csutil/inifile.h"
#include "qint.h"
#include "iimage.h"
#include "isystem.h"
#include "lightdef.h"

/// This is not too good from theoretical point of view,
/// however I do not see other efficient solutions. An alternative could
/// be to store such a pointer into each csTextureMM, but this is definitely
/// a waste of memory.
static csTextureManager *texman = NULL;

//---------------------------------------------------------- csTextureMM -----//

IMPLEMENT_IBASE (csTextureMM)
  IMPLEMENTS_INTERFACE (iTextureHandle)
IMPLEMENT_IBASE_END

csTextureMM::csTextureMM (iImage* Image, int Flags)
{
  CONSTRUCT_IBASE (NULL);

  (image = Image)->IncRef ();
  flags = Flags;

  tex [0] = tex [1] = tex [2] = tex [3] = NULL;

  transp = false;
  transp_color.red = transp_color.green = transp_color.blue = 0;
  cachedata = NULL;
  gamma_aplied = false;
}

csTextureMM::~csTextureMM ()
{
  for (int i = 0; i < 4; i++)
    CHKB (delete tex [i]);
  free_image ();
}

void csTextureMM::free_image ()
{
  if (!image) return;
  image->DecRef ();
  image = NULL;
}

void csTextureMM::create_mipmaps (bool verynice, bool blend_mipmap0)
{
  if (!image) return;

  // Delete existing mipmaps, if any
  for (int i = 0; i < 4; i++)
    CHKB (delete tex [i]);

  int step = verynice ? 0 : 1;

  RGBPixel *tc = transp ? &transp_color : (RGBPixel *)NULL;

  if (flags & CS_TEXTURE_3D)
  {
    int n=1;
    int maxMM = CS_GET_TEXTURE_MMLEVEL(flags);
    maxMM = maxMM == 0 ? 4 : MIN( maxMM, 4 );
    iImage *i0 = blend_mipmap0 ? image->MipMap (0, tc) : (image->IncRef (), image);
#if 1
    // Create each new level by creating a level 2 mipmap from previous level
    iImage *i1 = ++n <= maxMM ? i0->MipMap (step, tc) : NULL;
    iImage *i2 = ++n <= maxMM ? i1->MipMap (1, tc) : NULL;
    iImage *i3 = ++n <= maxMM ? i2->MipMap (1, tc) : NULL;
#else
    // Create each mipmap level from original texture
    iImage *i1 = ++n <= maxMM ? image->MipMap (step++, tc) : NULL;
    iImage *i2 = ++n <= maxMM ? image->MipMap (step++, tc) : NULL;
    iImage *i3 = ++n <= maxMM ? image->MipMap (step++, tc) : NULL;
#endif

    tex [0] = new_texture (i0);
    tex [1] = i1 ? new_texture (i1) : NULL;
    tex [2] = i2 ? new_texture (i2) : NULL;
    tex [3] = i3 ? new_texture (i3) : NULL;
  }
  else
  {
    // 2D textures uses just the top-level mipmap
    image->IncRef ();
    tex [0] = new_texture (image);
  }

  compute_mean_color ();
}

void csTextureMM::SetTransparent (bool Enable)
{
  transp = Enable;
}

// This function must be called BEFORE calling TextureManager::Update().
void csTextureMM::SetTransparent (UByte red, UByte green, UByte blue)
{
  transp_color.red = red;
  transp_color.green = green;
  transp_color.blue = blue;
  transp = true;
}

/// Get the transparent color
void csTextureMM::GetTransparent (UByte &r, UByte &g, UByte &b)
{
  r = transp_color.red;
  g = transp_color.green;
  b = transp_color.blue;
}

bool csTextureMM::GetTransparent ()
{
  return transp;
}

void csTextureMM::GetMeanColor (UByte &r, UByte &g, UByte &b)
{
  r = mean_color.red;
  g = mean_color.green;
  b = mean_color.blue;
}

csTexture* csTextureMM::get_texture (int lev)
{
  return (lev >= 0) && (lev < 4) ? tex [lev] : NULL;
}

bool csTextureMM::GetMipMapDimensions (int mipmap, int& w, int& h)
{
  csTexture *txt = get_texture (mipmap);
  if (txt)
  {
    w = txt->get_width ();
    h = txt->get_height ();
    return true;
  }
  return false;
}

void *csTextureMM::GetMipMapData (int mipmap)
{
  csTexture *txt = get_texture (mipmap);
  return txt ? txt->get_bitmap () : NULL;
}

void csTextureMM::adjust_size_po2 ()
{
  int newwidth  = image->GetWidth();
  int newheight = image->GetHeight();

  if (!IsPowerOf2(newwidth))
    newwidth = FindNearestPowerOf2 (image->GetWidth ()) / 2;

  if (!IsPowerOf2 (newheight))
    newheight = FindNearestPowerOf2 (image->GetHeight ()) / 2;

  if (newwidth != image->GetWidth () || newheight != image->GetHeight ())
    image->Rescale (newwidth, newheight);
}

void csTextureMM::apply_gamma ()
{
  if (gamma_aplied || !image)
    return;
  gamma_aplied = true;

  if (texman->Gamma == 1.0)
    return;

  RGBPixel *src = NULL;
  int pixels = 0;
  switch (image->GetFormat () & CS_IMGFMT_MASK)
  {
    case CS_IMGFMT_TRUECOLOR:
      src = (RGBPixel *)image->GetImageData ();
      pixels = image->GetWidth () * image->GetHeight ();
      break;
    case CS_IMGFMT_PALETTED8:
      src = image->GetPalette ();
      pixels = 256;
      break;
  }
  if (!src) return;
  while (pixels--)
  {
    src->red   = texman->GammaTable [src->red];
    src->green = texman->GammaTable [src->green];
    src->blue  = texman->GammaTable [src->blue];
    src++;
  }
}

//------------------------------------------------------------ csTexture -----//

void csTexture::compute_masks ()
{
  shf_w = csLog2 (w);
  and_w = (1 << shf_w) - 1;
  shf_h = csLog2 (h);
  and_h = (1 << shf_h) - 1;
}

//----------------------------------------------------- csTextureManager -----//

IMPLEMENT_IBASE (csTextureManager)
  IMPLEMENTS_INTERFACE (iTextureManager)
IMPLEMENT_IBASE_END

csTextureManager::csTextureManager (iSystem* iSys, iGraphics2D *iG2D)
  : textures (16, 16)
{
  texman = this;

  System = iSys;
  verbose = false;

  Gamma = 1.0;
  mipmap_mode = MIPMAP_NICE;
  do_blend_mipmap0 = false;
  pfmt = *iG2D->GetPixelFormat ();
}

csTextureManager::~csTextureManager()
{
  Clear ();
}

void csTextureManager::read_config (csIniFile *config)
{
  Gamma = config->GetFloat ("TextureManager", "GAMMA", 1.0);
  do_blend_mipmap0 = config->GetYesNo ("TextureManager", "BLEND_MIPMAP", false);
  const char *p = config->GetStr ("TextureManager", "MIPMAP_MODE", "nice");
  if (!strcmp (p, "nice"))
  {
    mipmap_mode = MIPMAP_NICE;
    if (verbose)
      System->Printf (MSG_INITIALIZATION, "Mipmap calculation 'nice'.\n");
  }
  else if (!strcmp (p, "verynice"))
  {
    mipmap_mode = MIPMAP_VERYNICE;
    if (verbose)
      System->Printf (MSG_INITIALIZATION, "Mipmap calculation 'verynice'\n");
  }
  else
    System->Printf (MSG_FATAL_ERROR, "Bad value '%s' for MIPMAP_MODE!\n(Use 'verynice' or 'nice')\n", p);
  compute_gamma_table ();
}

void csTextureManager::FreeImages ()
{
  for (int i = 0 ; i < textures.Length () ; i++)
    textures.Get (i)->free_image ();
}

int csTextureManager::GetTextureFormat ()
{
  return CS_IMGFMT_TRUECOLOR;
}

void csTextureManager::compute_gamma_table ()
{
  for (int i = 0; i < 256; i++)
    GammaTable [i] = QRound (255 * pow (i / 255.0, Gamma));
}

int csTextureManager::FindRGB (int r, int g, int b)
{
  if (r > 255) r = 255; else if (r < 0) r = 0;
  if (g > 255) g = 255; else if (g < 0) g = 0;
  if (b > 255) b = 255; else if (b < 0) b = 0;
  return
    ((r >> (8-pfmt.RedBits))   << pfmt.RedShift) |
    ((g >> (8-pfmt.GreenBits)) << pfmt.GreenShift) |
    ((b >> (8-pfmt.BlueBits))  << pfmt.BlueShift);
}

void csTextureManager::ReserveColor (int /*r*/, int /*g*/, int /*b*/)
{
}

void csTextureManager::SetPalette ()
{
}

void csTextureManager::ResetPalette ()
{
}
<|endoftext|>


<commit_before>#include <cassert>
#include <cstring>
#include <fstream>
#include <string>
#include <memory>

#include <GraphMol/FileParsers/MolSupplier.h>

#include <sqlite3ext.h>
extern const sqlite3_api_routines *sqlite3_api;

#include "utils.hpp"
#include "sdf_io.hpp"
#include "mol.hpp"

// static const int SDF_INDEX_COLUMN = 0;
// static const int SDF_MOLECULE_COLUMN = 1;
static const int SDF_FILEPATH_COLUMN = 2;

static int sdfReaderConnect(sqlite3 *db, void */*pAux*/,
                      int /*argc*/, const char * const */*argv*/,
                      sqlite3_vtab **ppVTab,
                      char **pzErr)
{
  int rc = sqlite3_declare_vtab(db, "CREATE TABLE x("
    "idx INTEGER, molecule MOL, "
    "file_path HIDDEN"
    ")");

  if (rc == SQLITE_OK) {
    sqlite3_vtab *vtab = *ppVTab = (sqlite3_vtab *) sqlite3_malloc(sizeof(sqlite3_vtab));
    if (!vtab) {
      rc = SQLITE_NOMEM;
    }
    else {
      memset(vtab, 0, sizeof(sqlite3_vtab));
    }
  }

  if (rc != SQLITE_OK) {
    *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db));
  }

  return rc;
}

int sdfReaderBestIndex(sqlite3_vtab */*pVTab*/, sqlite3_index_info *pIndexInfo)
{
  // At this time the only arg passed to the SDF parser is the input file path
  // this function is consequently very simple. it might become more complex
  // when/if support for more args/options is implemented.
  int file_path_index = -1;
  for (int index = 0; index < pIndexInfo->nConstraint; ++index) {
    if (pIndexInfo->aConstraint[index].usable == 0) {
      continue;
    }
    if (pIndexInfo->aConstraint[index].iColumn == SDF_FILEPATH_COLUMN &&
        pIndexInfo->aConstraint[index].op == SQLITE_INDEX_CONSTRAINT_EQ) {
      file_path_index = index;
    }
  }
  if (file_path_index < 0) {
    // The SDF file path is not available, or it's not usable,
    // This plan is unusable for this table
    return SQLITE_CONSTRAINT;
  }
  pIndexInfo->idxNum = 1; // Not really meaningful a this time
  pIndexInfo->aConstraintUsage[file_path_index].argvIndex = 1; // will be argv[0] for xFilter
  pIndexInfo->aConstraintUsage[file_path_index].omit = 1; // no need for SQLite to verify
  pIndexInfo->estimatedCost = 1000000;
  return SQLITE_OK;
}

static int sdfReaderDisconnect(sqlite3_vtab *pVTab)
{
  sqlite3_free(pVTab);
  return SQLITE_OK;
}

struct SdfReaderCursor : public sqlite3_vtab_cursor {
  std::unique_ptr<RDKit::ForwardSDMolSupplier> supplier;
  sqlite3_int64 rowid;
  std::unique_ptr<RDKit::ROMol> mol;
};

static int sdfReaderOpen(sqlite3_vtab */*pVTab*/, sqlite3_vtab_cursor **ppCursor)
{
  int rc = SQLITE_OK;
  SdfReaderCursor *pCsr = new SdfReaderCursor; //FIXME
  *ppCursor = (sqlite3_vtab_cursor *)pCsr;
  return rc;
}

static int sdfReaderClose(sqlite3_vtab_cursor *pCursor)
{
  SdfReaderCursor *p = (SdfReaderCursor *)pCursor;
  delete p;
  return SQLITE_OK;
}

static int sdfReaderFilter(sqlite3_vtab_cursor *pCursor, int /*idxNum*/, const char */*idxStr*/,
                     int argc, sqlite3_value **argv)
{
  SdfReaderCursor *p = (SdfReaderCursor *)pCursor;

  if (argc != 1) {
    // at present we always expect the input file path as the only arg
    return SQLITE_ERROR;
  }

  sqlite3_value *arg = argv[0];
  if (sqlite3_value_type(arg) != SQLITE_TEXT) {
    return SQLITE_MISMATCH;
  }

  const char * file_path = (const char *) sqlite3_value_text(arg);
  std::unique_ptr<std::ifstream> pins(new std::ifstream(file_path));
  if (!pins->is_open()) {
    // Maybe log something
    return SQLITE_ERROR;
  }

  p->supplier.reset(new RDKit::ForwardSDMolSupplier(pins.release(), true));
  if (!p->supplier->atEnd()) {
    p->rowid = 1;
    p->mol.reset(p->supplier->next());
  }

  return SQLITE_OK;
}

static int sdfReaderNext(sqlite3_vtab_cursor *pCursor)
{
  SdfReaderCursor * p = (SdfReaderCursor *)pCursor;
  if (!p->supplier->atEnd()) {
    p->rowid += 1;
    p->mol.reset(p->supplier->next());
  }
  return SQLITE_OK;
}

static int sdfReaderEof(sqlite3_vtab_cursor *pCursor)
{
  SdfReaderCursor * p = (SdfReaderCursor *)pCursor;
  return p->supplier->atEnd() ? 1 : 0;
}

static int sdfReaderColumn(sqlite3_vtab_cursor *pCursor, sqlite3_context *ctx, int N)
{
  SdfReaderCursor * p = (SdfReaderCursor *)pCursor;

  switch (N) {
    case 0:
      // row id
      sqlite3_result_int(ctx, p->rowid);
      break;
    case 1:
      // the molecule
      if (p->mol) {
        int rc = SQLITE_OK;
        Blob blob = mol_to_blob(*p->mol, &rc);
        if (rc == SQLITE_OK) {
          sqlite3_result_blob(ctx, blob.data(), blob.size(), SQLITE_TRANSIENT);
        }
        else {
          sqlite3_result_error_code(ctx, rc);
        }
      }
      else {
        sqlite3_result_null(ctx);
      }
      break;
    default:
      assert(!"unexpected column number");
      sqlite3_result_null(ctx);
  }
  return SQLITE_OK;
}

static int sdfReaderRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid)
{
  SdfReaderCursor * p = (SdfReaderCursor *)pCursor;
  *pRowid = p->rowid;
  return SQLITE_OK;
}

/*
** The SDF reader module, collecting the methods that operate on the PeriodicTable vtab
*/
static sqlite3_module sdfReaderModule = {
  0,                           /* iVersion */
  0,                           /* xCreate - create a table */ /* null because eponymous-only */
  sdfReaderConnect,            /* xConnect - connect to an existing table */
  sdfReaderBestIndex,          /* xBestIndex - Determine search strategy */
  sdfReaderDisconnect,         /* xDisconnect - Disconnect from a table */
  0,                           /* xDestroy - Drop a table */
  sdfReaderOpen,               /* xOpen - open a cursor */
  sdfReaderClose,              /* xClose - close a cursor */
  sdfReaderFilter,             /* xFilter - configure scan constraints */
  sdfReaderNext,               /* xNext - advance a cursor */
  sdfReaderEof,                /* xEof */
  sdfReaderColumn,             /* xColumn - read data */
  sdfReaderRowid,              /* xRowid - read data */
  0,                           /* xUpdate - write data */
  0,                           /* xBegin - begin transaction */
  0,                           /* xSync - sync transaction */
  0,                           /* xCommit - commit transaction */
  0,                           /* xRollback - rollback transaction */
  0,                           /* xFindFunction - function overloading */
  0,                           /* xRename - rename the table */
  0,                           /* xSavepoint */
  0,                           /* xRelease */
  0,                           /* xRollbackTo */
  0                            /* xShadowName */
};

int chemicalite_init_sdf_io(sqlite3 *db)
{
  int rc = SQLITE_OK;

  if (rc == SQLITE_OK) {
    rc = sqlite3_create_module_v2(db, "sdf_reader", &sdfReaderModule,
				  0,  /* Client data for xCreate/xConnect */
				  0   /* Module destructor function */
				  );
  }

  return rc;
}
<commit_msg>remove the explicit rowid/index column<commit_after>#include <cassert>
#include <cstring>
#include <fstream>
#include <string>
#include <memory>

#include <GraphMol/FileParsers/MolSupplier.h>

#include <sqlite3ext.h>
extern const sqlite3_api_routines *sqlite3_api;

#include "utils.hpp"
#include "sdf_io.hpp"
#include "mol.hpp"

// static const int SDF_MOLECULE_COLUMN = 0;
static const int SDF_FILEPATH_COLUMN = 1;

static int sdfReaderConnect(sqlite3 *db, void */*pAux*/,
                      int /*argc*/, const char * const */*argv*/,
                      sqlite3_vtab **ppVTab,
                      char **pzErr)
{
  int rc = sqlite3_declare_vtab(db, "CREATE TABLE x("
    "molecule MOL, "
    "file_path HIDDEN"
    ")");

  if (rc == SQLITE_OK) {
    sqlite3_vtab *vtab = *ppVTab = (sqlite3_vtab *) sqlite3_malloc(sizeof(sqlite3_vtab));
    if (!vtab) {
      rc = SQLITE_NOMEM;
    }
    else {
      memset(vtab, 0, sizeof(sqlite3_vtab));
    }
  }

  if (rc != SQLITE_OK) {
    *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db));
  }

  return rc;
}

int sdfReaderBestIndex(sqlite3_vtab */*pVTab*/, sqlite3_index_info *pIndexInfo)
{
  // At this time the only arg passed to the SDF parser is the input file path
  // this function is consequently very simple. it might become more complex
  // when/if support for more args/options is implemented.
  int file_path_index = -1;
  for (int index = 0; index < pIndexInfo->nConstraint; ++index) {
    if (pIndexInfo->aConstraint[index].usable == 0) {
      continue;
    }
    if (pIndexInfo->aConstraint[index].iColumn == SDF_FILEPATH_COLUMN &&
        pIndexInfo->aConstraint[index].op == SQLITE_INDEX_CONSTRAINT_EQ) {
      file_path_index = index;
    }
  }
  if (file_path_index < 0) {
    // The SDF file path is not available, or it's not usable,
    // This plan is unusable for this table
    return SQLITE_CONSTRAINT;
  }
  pIndexInfo->idxNum = 1; // Not really meaningful a this time
  pIndexInfo->aConstraintUsage[file_path_index].argvIndex = 1; // will be argv[0] for xFilter
  pIndexInfo->aConstraintUsage[file_path_index].omit = 1; // no need for SQLite to verify
  pIndexInfo->estimatedCost = 1000000;
  return SQLITE_OK;
}

static int sdfReaderDisconnect(sqlite3_vtab *pVTab)
{
  sqlite3_free(pVTab);
  return SQLITE_OK;
}

struct SdfReaderCursor : public sqlite3_vtab_cursor {
  std::unique_ptr<RDKit::ForwardSDMolSupplier> supplier;
  sqlite3_int64 rowid;
  std::unique_ptr<RDKit::ROMol> mol;
};

static int sdfReaderOpen(sqlite3_vtab */*pVTab*/, sqlite3_vtab_cursor **ppCursor)
{
  int rc = SQLITE_OK;
  SdfReaderCursor *pCsr = new SdfReaderCursor; //FIXME
  *ppCursor = (sqlite3_vtab_cursor *)pCsr;
  return rc;
}

static int sdfReaderClose(sqlite3_vtab_cursor *pCursor)
{
  SdfReaderCursor *p = (SdfReaderCursor *)pCursor;
  delete p;
  return SQLITE_OK;
}

static int sdfReaderFilter(sqlite3_vtab_cursor *pCursor, int /*idxNum*/, const char */*idxStr*/,
                     int argc, sqlite3_value **argv)
{
  SdfReaderCursor *p = (SdfReaderCursor *)pCursor;

  if (argc != 1) {
    // at present we always expect the input file path as the only arg
    return SQLITE_ERROR;
  }

  sqlite3_value *arg = argv[0];
  if (sqlite3_value_type(arg) != SQLITE_TEXT) {
    return SQLITE_MISMATCH;
  }

  const char * file_path = (const char *) sqlite3_value_text(arg);
  std::unique_ptr<std::ifstream> pins(new std::ifstream(file_path));
  if (!pins->is_open()) {
    // Maybe log something
    return SQLITE_ERROR;
  }

  p->supplier.reset(new RDKit::ForwardSDMolSupplier(pins.release(), true));
  if (!p->supplier->atEnd()) {
    p->rowid = 1;
    p->mol.reset(p->supplier->next());
  }

  return SQLITE_OK;
}

static int sdfReaderNext(sqlite3_vtab_cursor *pCursor)
{
  SdfReaderCursor * p = (SdfReaderCursor *)pCursor;
  if (!p->supplier->atEnd()) {
    p->rowid += 1;
    p->mol.reset(p->supplier->next());
  }
  return SQLITE_OK;
}

static int sdfReaderEof(sqlite3_vtab_cursor *pCursor)
{
  SdfReaderCursor * p = (SdfReaderCursor *)pCursor;
  return p->supplier->atEnd() ? 1 : 0;
}

static int sdfReaderColumn(sqlite3_vtab_cursor *pCursor, sqlite3_context *ctx, int N)
{
  SdfReaderCursor * p = (SdfReaderCursor *)pCursor;

  switch (N) {
    case 0:
      // the molecule
      if (p->mol) {
        int rc = SQLITE_OK;
        Blob blob = mol_to_blob(*p->mol, &rc);
        if (rc == SQLITE_OK) {
          sqlite3_result_blob(ctx, blob.data(), blob.size(), SQLITE_TRANSIENT);
        }
        else {
          sqlite3_result_error_code(ctx, rc);
        }
      }
      else {
        sqlite3_result_null(ctx);
      }
      break;
    default:
      assert(!"unexpected column number");
      sqlite3_result_null(ctx);
  }
  return SQLITE_OK;
}

static int sdfReaderRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid)
{
  SdfReaderCursor * p = (SdfReaderCursor *)pCursor;
  *pRowid = p->rowid;
  return SQLITE_OK;
}

/*
** The SDF reader module, collecting the methods that operate on the PeriodicTable vtab
*/
static sqlite3_module sdfReaderModule = {
  0,                           /* iVersion */
  0,                           /* xCreate - create a table */ /* null because eponymous-only */
  sdfReaderConnect,            /* xConnect - connect to an existing table */
  sdfReaderBestIndex,          /* xBestIndex - Determine search strategy */
  sdfReaderDisconnect,         /* xDisconnect - Disconnect from a table */
  0,                           /* xDestroy - Drop a table */
  sdfReaderOpen,               /* xOpen - open a cursor */
  sdfReaderClose,              /* xClose - close a cursor */
  sdfReaderFilter,             /* xFilter - configure scan constraints */
  sdfReaderNext,               /* xNext - advance a cursor */
  sdfReaderEof,                /* xEof */
  sdfReaderColumn,             /* xColumn - read data */
  sdfReaderRowid,              /* xRowid - read data */
  0,                           /* xUpdate - write data */
  0,                           /* xBegin - begin transaction */
  0,                           /* xSync - sync transaction */
  0,                           /* xCommit - commit transaction */
  0,                           /* xRollback - rollback transaction */
  0,                           /* xFindFunction - function overloading */
  0,                           /* xRename - rename the table */
  0,                           /* xSavepoint */
  0,                           /* xRelease */
  0,                           /* xRollbackTo */
  0                            /* xShadowName */
};

int chemicalite_init_sdf_io(sqlite3 *db)
{
  int rc = SQLITE_OK;

  if (rc == SQLITE_OK) {
    rc = sqlite3_create_module_v2(db, "sdf_reader", &sdfReaderModule,
				  0,  /* Client data for xCreate/xConnect */
				  0   /* Module destructor function */
				  );
  }

  return rc;
}
<|endoftext|>


<commit_before>//---------------------------------------------------------------------------
/// \file   es/storage.hpp
/// \brief  The entity/component data store
//
// Copyright 2013, nocte@hippie.nu            Released under the MIT License.
//---------------------------------------------------------------------------
#pragma once

#include <algorithm>
#include <bitset>
#include <cassert>
#include <cstdint>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <vector>
#include <unordered_map>

#include "component.hpp"
#include "entity.hpp"
#include "traits.hpp"

namespace es {

/** A storage ties entities and components together.
 * Storage associates two other bits of data with every entity:
 * - A 64-bit mask that keeps track of which components are defined
 * - A vector of bytes, holding the actual data
 *
 * The vector of bytes tries to pack the component data as tightly as
 * possible.  It is really fast for plain old datatypes, but it also
 * handles nontrivial types safely.  It packs a virtual table and a
 * pointer to some heap space in the vector, and calls the constructor
 * and destructor as needed.
 */
class storage
{
    // It is assumed the first few components will be accessed the
    // most often.  We keep a cache of the first 12.
    static constexpr uint32_t cache_size = 12;
    static constexpr uint32_t cache_mask = (1 << cache_size) - 1;

    /** This data gets associated with every entity. */
    struct elem
    {
        /** Bitmask to keep track of which components are held in \a data. */
        std::bitset<64>     components;
        /** Component data for this entity. */
        std::vector<char>   data;
    };

    typedef component::placeholder placeholder;

    /** Data types that do not have a flat memory layout are kept in the
     ** elem::data buffer in a placeholder object. */
    template <typename t>
    class holder : public placeholder
    {
    public:
        holder () { }
        holder (t init) : held_(std::move(init)) { }

        const t& held() const { return held_; }
        t&       held()       { return held_; }

        placeholder* clone() const { return new holder<t>(held_); }

    private:
        t held_;
    };

    typedef std::unordered_map<uint32_t, elem> stor_impl;

public:
    typedef uint8_t     component_id;

    typedef stor_impl::iterator         iterator;
    typedef stor_impl::const_iterator   const_iterator;

public:
    /** Variable references are used by systems to access an entity's data.
     *  It keeps track of the elem::data buffer and the offset inside that
     *  buffer. */
    template <typename type>
    class var_ref
    {
        friend class storage;

        type& get()
        {
            if (is_flat<type>::value)
                return *reinterpret_cast<type*>(&*e_.data.begin() + offset_);

            auto ptr (reinterpret_cast<holder<type>*>(&*e_.data.begin() + offset_));
            return ptr->held();
        }

    public:
        operator const type& () const
        {
            if (is_flat<type>::value)
                return *reinterpret_cast<const type*>(&*e_.data.begin() + offset_);

            auto ptr (reinterpret_cast<const holder<type>*>(&*e_.data.begin() + offset_));
            return ptr->held();
        }

        var_ref& operator= (type assign)
        {
            if (is_flat<type>::value)
            {
                new (&*e_.data.begin() + offset_) type(std::move(assign));
            }
            else
            {
                auto ptr (reinterpret_cast<holder<type>*>(&*e_.data.begin() + offset_));
                new (ptr) holder<type>(std::move(assign));
            }
            return *this;
        }

        template <typename s>
        var_ref& operator+= (s val)
            { get() += val; return *this; }

        template <typename s>
        var_ref& operator-= (s val)
            { get() -= val; return *this; }

        template <typename s>
        var_ref& operator*= (s val)
            { get() *= val; return *this; }

        template <typename s>
        var_ref& operator/= (s val)
            { get() /= val; return *this; }

    protected:
        var_ref (size_t offset, elem& e)
            : offset_(offset), e_(e)
            { }

    private:
        size_t offset_;
        elem&  e_;
    };

public:
    storage()
        : next_id_(0)
        {
            component_offsets_.push_back(0);
        }

    template <typename type>
    component_id register_component (std::string name)
        {
            size_t size;

            if (is_flat<type>::value)
            {
                size = sizeof(type);
                components_.emplace_back(name, size, nullptr);
            }
            else
            {
                flat_mask_.set(components_.size());
                size = sizeof(holder<type>);
                components_.emplace_back(name, size,
                                         std::unique_ptr<placeholder>(new holder<type>()));
            }

            if (components_.size() < cache_size)
            {
                size_t i (component_offsets_.size());
                component_offsets_.resize(i * 2);
                for (size_t j (i); j != i * 2; ++j)
                    component_offsets_[j] = component_offsets_[j-i] + size;
            }

            return components_.size() - 1;
        }

    component_id find_component (const std::string& name) const
        {
            auto found (std::find(components_.begin(), components_.end(), name));
            if (found == components_.end())
                throw std::logic_error("component does not exist");

            return std::distance(components_.begin(), found);
        }

    const component& operator[] (component_id id) const
        { return components_[id]; }

    const std::vector<component>& components() const
        { return components_; }

public:
    entity new_entity()
        {
            entities_[next_id_++];
            return next_id_ - 1;
        }

    /** Get an entity with a given ID, or create it if it didn't exist yet. */
    iterator make (uint32_t id)
        {
            if (next_id_ <= id)
                next_id_ = id + 1;

            return entities_.insert(entities_.end(), std::make_pair(id, elem()));
        }

    /** Create a whole bunch of empty entities in one go.
     * @param count     The number of entities to create
     * @return The range of entities created */
    std::pair<entity, entity> new_entities (size_t count)
        {
            auto range_begin (next_id_);
            for (; count > 0; --count)
                entities_[next_id_++];

            return std::make_pair(range_begin, next_id_);
        }

    entity clone_entity (iterator f)
        {
            elem& e (f->second);
            entities_[next_id_++] = e;

            // Quick check if we need to make deep copies
            if ((e.components & flat_mask_).any())
            {
                size_t off (0);
                for (int c_id (0); c_id < 64 && off < e.data.size(); ++c_id)
                {
                    if (e.components[c_id])
                    {
                        if (!components_[c_id].is_flat())
                        {
                            auto ptr (reinterpret_cast<placeholder*>(&*e.data.begin() + off));
                            ptr = ptr->clone();
                        }
                        off += components_[c_id].size();
                    }
                }
            }
            return next_id_ - 1;
        }

    iterator find (entity en)
        {
            auto found (entities_.find(en));
            if (found == entities_.end())
                throw std::logic_error("unknown entity");

            return found;
        }

    const_iterator find (entity en) const
        {
            auto found (entities_.find(en));
            if (found == entities_.end())
                throw std::logic_error("unknown entity");

            return found;
        }

    void delete_entity (entity en)
        { delete_entity(find(en)); }

    void delete_entity (iterator f)
        {
            elem& e (f->second);

            // Quick check if we'll have to call any destructors.
            if ((e.components & flat_mask_).any())
            {
                size_t off (0);
                for (int search (0); search < 64 && off < e.data.size(); ++search)
                {
                    if (e.components[search])
                    {
                        if (!components_[search].is_flat())
                        {
                            auto ptr (reinterpret_cast<placeholder*>(&*e.data.begin() + off));
                            ptr->~placeholder();
                        }
                        off += components_[search].size();
                    }
                }
            }

            entities_.erase(f);
        }

    void remove_component_from_entity (iterator en, component_id c)
        {
            auto& e (en->second);
            if (!e.components[c])
                return;

            size_t off (offset(e, c));
            auto& comp_info (components_[c]);
            if (!comp_info.is_flat())
            {
                auto ptr (reinterpret_cast<placeholder*>(&*e.data.begin() + off));
                ptr->~placeholder();
            }
            auto o (e.data.begin() + off);
            e.data.erase(o, o + comp_info.size());
            e.components.reset(c);
        }

    /** Call a function for every entity that has a given component.
     *  The callee can then query and change the value of the component through
     *  a var_ref object, or remove the entity.
     * @param c     The component to look for.
     * @param func  The function to call.  This function will be passed an
     *              iterator to the current entity, and a var_ref corresponding
     *              to the component value in this entity. */
    template <typename t>
    void for_each (component_id c, std::function<void(iterator, var_ref<t>)> func)
        {
            std::bitset<64> mask;
            mask.set(c);
            for (auto i (entities_.begin()); i != entities_.end(); )
            {
                auto next (std::next(i));
                elem& e (i->second);
                if ((e.components & mask) == mask)
                    func(i, var_ref<t>(offset(e, c), e));

                i = next;
            }
        }

    template <typename t1, typename t2>
    void for_each (component_id c1, component_id c2,
                   std::function<void(iterator, var_ref<t1>, var_ref<t2>)> func)
        {
            std::bitset<64> mask;
            mask.set(c1);
            mask.set(c2);
            for (auto i (entities_.begin()); i != entities_.end(); )
            {
                auto next (std::next(i));
                elem& e (i->second);
                if ((e.components & mask) == mask)
                {
                    func(i, var_ref<t1>(offset(e, c1), e),
                            var_ref<t2>(offset(e, c2), e));
                }
                i = next;
            }
        }

    template <typename t1, typename t2, typename t3>
    void for_each (component_id c1, component_id c2, component_id c3,
                   std::function<void(iterator, var_ref<t1>, var_ref<t2>, var_ref<t3>)> func)
        {
            std::bitset<64> mask;
            mask.set(c1);
            mask.set(c2);
            mask.set(c3);
            for (auto i (entities_.begin()); i != entities_.end(); )
            {
                auto next (std::next(i));
                elem& e (i->second);
                if ((e.components & mask) == mask)
                {
                    func(i, var_ref<t1>(offset(e, c1), e),
                            var_ref<t2>(offset(e, c2), e),
                            var_ref<t3>(offset(e, c3), e));
                }
                i = next;
            }
        }

    bool exists (entity en) const
        { return entities_.count(en); }

    template <typename type>
    void set (entity en, component_id c_id, type val)
        { return set<type>(find(en), c_id, std::move(val)); }

    template <typename type>
    void set (iterator en, component_id c_id, type val)
        {
            const component& c (components_[c_id]);
            elem&  e   (en->second);
            size_t off (offset(e, c_id));

            if (!e.components[c_id])
            {
                if (e.data.size() < off)
                    e.data.resize(off + c.size());
                else
                    e.data.insert(e.data.begin() + off, c.size(), 0);

                e.components.set(c_id);
            }

            if (is_flat<type>::value)
            {
                assert(e.data.size() >= off + sizeof(type));
                new (&*e.data.begin() + off) type(std::move(val));
            }
            else
            {
                assert(e.data.size() >= off + sizeof(holder<type>));
                auto ptr (reinterpret_cast<holder<type>*>(&*e.data.begin() + off));
                new (ptr) holder<type>(std::move(val));
            }
        }

    template <typename type>
    const type& get (entity en, component_id c_id) const
        { return get<type>(find(en), c_id); }

    template <typename type>
    const type& get (const_iterator en, component_id c_id) const
        {
            auto& e (en->second);
            if (!e.components[c_id])
                throw std::logic_error("entity does not have component");

            return get<type>(e, c_id);
        }

private:
    template <typename type>
    const type& get (const elem& e, component_id c_id) const
        {
            size_t off (offset(e, c_id));
            if (is_flat<type>::value)
                return *reinterpret_cast<const type*>(&*e.data.begin() + off);

            auto ptr (reinterpret_cast<const holder<type>*>(&*e.data.begin() + off));

            return ptr->held();
        }

    size_t offset (const elem& e, component_id c) const
        {
            assert(c < components_.size());
            assert((c & cache_mask) < component_offsets_.size());
            size_t result (component_offsets_[c & cache_mask]);

            for (component_id search (cache_size); search < c; ++search)
            {
                if (e.components[search])
                    result += components_[search].size();
            }
            return result;
        }

private:
    /** Keeps track of entity IDs to give out. */
    uint32_t                            next_id_;
    /** The list of registered components. */
    std::vector<component>              components_;
    /** Mapping entity IDs to their data. */
    std::unordered_map<uint32_t, elem>  entities_;
    /** A lookup table for the data offsets of components.
     *  The index is the bitmask as used in elem::components. */
    std::vector<size_t>                 component_offsets_;
    /** A bitmask to quickly determine whether a certain combination of
     ** components has a flat memory layout or not. */
    std::bitset<64>                     flat_mask_;
};

} // namespace es
<commit_msg>Make sure entities exist before deleting them<commit_after>//---------------------------------------------------------------------------
/// \file   es/storage.hpp
/// \brief  The entity/component data store
//
// Copyright 2013, nocte@hippie.nu            Released under the MIT License.
//---------------------------------------------------------------------------
#pragma once

#include <algorithm>
#include <bitset>
#include <cassert>
#include <cstdint>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <vector>
#include <unordered_map>

#include "component.hpp"
#include "entity.hpp"
#include "traits.hpp"

namespace es {

/** A storage ties entities and components together.
 * Storage associates two other bits of data with every entity:
 * - A 64-bit mask that keeps track of which components are defined
 * - A vector of bytes, holding the actual data
 *
 * The vector of bytes tries to pack the component data as tightly as
 * possible.  It is really fast for plain old datatypes, but it also
 * handles nontrivial types safely.  It packs a virtual table and a
 * pointer to some heap space in the vector, and calls the constructor
 * and destructor as needed.
 */
class storage
{
    // It is assumed the first few components will be accessed the
    // most often.  We keep a cache of the first 12.
    static constexpr uint32_t cache_size = 12;
    static constexpr uint32_t cache_mask = (1 << cache_size) - 1;

    /** This data gets associated with every entity. */
    struct elem
    {
        /** Bitmask to keep track of which components are held in \a data. */
        std::bitset<64>     components;
        /** Component data for this entity. */
        std::vector<char>   data;
    };

    typedef component::placeholder placeholder;

    /** Data types that do not have a flat memory layout are kept in the
     ** elem::data buffer in a placeholder object. */
    template <typename t>
    class holder : public placeholder
    {
    public:
        holder () { }
        holder (t init) : held_(std::move(init)) { }

        const t& held() const { return held_; }
        t&       held()       { return held_; }

        placeholder* clone() const { return new holder<t>(held_); }

    private:
        t held_;
    };

    typedef std::unordered_map<uint32_t, elem> stor_impl;

public:
    typedef uint8_t     component_id;

    typedef stor_impl::iterator         iterator;
    typedef stor_impl::const_iterator   const_iterator;

public:
    /** Variable references are used by systems to access an entity's data.
     *  It keeps track of the elem::data buffer and the offset inside that
     *  buffer. */
    template <typename type>
    class var_ref
    {
        friend class storage;

        type& get()
        {
            if (is_flat<type>::value)
                return *reinterpret_cast<type*>(&*e_.data.begin() + offset_);

            auto ptr (reinterpret_cast<holder<type>*>(&*e_.data.begin() + offset_));
            return ptr->held();
        }

    public:
        operator const type& () const
        {
            if (is_flat<type>::value)
                return *reinterpret_cast<const type*>(&*e_.data.begin() + offset_);

            auto ptr (reinterpret_cast<const holder<type>*>(&*e_.data.begin() + offset_));
            return ptr->held();
        }

        var_ref& operator= (type assign)
        {
            if (is_flat<type>::value)
            {
                new (&*e_.data.begin() + offset_) type(std::move(assign));
            }
            else
            {
                auto ptr (reinterpret_cast<holder<type>*>(&*e_.data.begin() + offset_));
                new (ptr) holder<type>(std::move(assign));
            }
            return *this;
        }

        template <typename s>
        var_ref& operator+= (s val)
            { get() += val; return *this; }

        template <typename s>
        var_ref& operator-= (s val)
            { get() -= val; return *this; }

        template <typename s>
        var_ref& operator*= (s val)
            { get() *= val; return *this; }

        template <typename s>
        var_ref& operator/= (s val)
            { get() /= val; return *this; }

    protected:
        var_ref (size_t offset, elem& e)
            : offset_(offset), e_(e)
            { }

    private:
        size_t offset_;
        elem&  e_;
    };

public:
    storage()
        : next_id_(0)
        {
            component_offsets_.push_back(0);
        }

    template <typename type>
    component_id register_component (std::string name)
        {
            size_t size;

            if (is_flat<type>::value)
            {
                size = sizeof(type);
                components_.emplace_back(name, size, nullptr);
            }
            else
            {
                flat_mask_.set(components_.size());
                size = sizeof(holder<type>);
                components_.emplace_back(name, size,
                                         std::unique_ptr<placeholder>(new holder<type>()));
            }

            if (components_.size() < cache_size)
            {
                size_t i (component_offsets_.size());
                component_offsets_.resize(i * 2);
                for (size_t j (i); j != i * 2; ++j)
                    component_offsets_[j] = component_offsets_[j-i] + size;
            }

            return components_.size() - 1;
        }

    component_id find_component (const std::string& name) const
        {
            auto found (std::find(components_.begin(), components_.end(), name));
            if (found == components_.end())
                throw std::logic_error("component does not exist");

            return std::distance(components_.begin(), found);
        }

    const component& operator[] (component_id id) const
        { return components_[id]; }

    const std::vector<component>& components() const
        { return components_; }

public:
    entity new_entity()
        {
            entities_[next_id_++];
            return next_id_ - 1;
        }

    /** Get an entity with a given ID, or create it if it didn't exist yet. */
    iterator make (uint32_t id)
        {
            if (next_id_ <= id)
                next_id_ = id + 1;

            return entities_.insert(entities_.end(), std::make_pair(id, elem()));
        }

    /** Create a whole bunch of empty entities in one go.
     * @param count     The number of entities to create
     * @return The range of entities created */
    std::pair<entity, entity> new_entities (size_t count)
        {
            auto range_begin (next_id_);
            for (; count > 0; --count)
                entities_[next_id_++];

            return std::make_pair(range_begin, next_id_);
        }

    entity clone_entity (iterator f)
        {
            elem& e (f->second);
            entities_[next_id_++] = e;

            // Quick check if we need to make deep copies
            if ((e.components & flat_mask_).any())
            {
                size_t off (0);
                for (int c_id (0); c_id < 64 && off < e.data.size(); ++c_id)
                {
                    if (e.components[c_id])
                    {
                        if (!components_[c_id].is_flat())
                        {
                            auto ptr (reinterpret_cast<placeholder*>(&*e.data.begin() + off));
                            ptr = ptr->clone();
                        }
                        off += components_[c_id].size();
                    }
                }
            }
            return next_id_ - 1;
        }

    iterator find (entity en)
        {
            auto found (entities_.find(en));
            if (found == entities_.end())
                throw std::logic_error("unknown entity");

            return found;
        }

    const_iterator find (entity en) const
        {
            auto found (entities_.find(en));
            if (found == entities_.end())
                throw std::logic_error("unknown entity");

            return found;
        }

    void delete_entity (entity en)
        {
            auto found (find(en));
            if (found != entities_.end())
                delete_entity(found);
        }

    void delete_entity (iterator f)
        {
            elem& e (f->second);

            // Quick check if we'll have to call any destructors.
            if ((e.components & flat_mask_).any())
            {
                size_t off (0);
                for (int search (0); search < 64 && off < e.data.size(); ++search)
                {
                    if (e.components[search])
                    {
                        if (!components_[search].is_flat())
                        {
                            auto ptr (reinterpret_cast<placeholder*>(&*e.data.begin() + off));
                            ptr->~placeholder();
                        }
                        off += components_[search].size();
                    }
                }
            }

            entities_.erase(f);
        }

    void remove_component_from_entity (iterator en, component_id c)
        {
            auto& e (en->second);
            if (!e.components[c])
                return;

            size_t off (offset(e, c));
            auto& comp_info (components_[c]);
            if (!comp_info.is_flat())
            {
                auto ptr (reinterpret_cast<placeholder*>(&*e.data.begin() + off));
                ptr->~placeholder();
            }
            auto o (e.data.begin() + off);
            e.data.erase(o, o + comp_info.size());
            e.components.reset(c);
        }

    /** Call a function for every entity that has a given component.
     *  The callee can then query and change the value of the component through
     *  a var_ref object, or remove the entity.
     * @param c     The component to look for.
     * @param func  The function to call.  This function will be passed an
     *              iterator to the current entity, and a var_ref corresponding
     *              to the component value in this entity. */
    template <typename t>
    void for_each (component_id c, std::function<void(iterator, var_ref<t>)> func)
        {
            std::bitset<64> mask;
            mask.set(c);
            for (auto i (entities_.begin()); i != entities_.end(); )
            {
                auto next (std::next(i));
                elem& e (i->second);
                if ((e.components & mask) == mask)
                    func(i, var_ref<t>(offset(e, c), e));

                i = next;
            }
        }

    template <typename t1, typename t2>
    void for_each (component_id c1, component_id c2,
                   std::function<void(iterator, var_ref<t1>, var_ref<t2>)> func)
        {
            std::bitset<64> mask;
            mask.set(c1);
            mask.set(c2);
            for (auto i (entities_.begin()); i != entities_.end(); )
            {
                auto next (std::next(i));
                elem& e (i->second);
                if ((e.components & mask) == mask)
                {
                    func(i, var_ref<t1>(offset(e, c1), e),
                            var_ref<t2>(offset(e, c2), e));
                }
                i = next;
            }
        }

    template <typename t1, typename t2, typename t3>
    void for_each (component_id c1, component_id c2, component_id c3,
                   std::function<void(iterator, var_ref<t1>, var_ref<t2>, var_ref<t3>)> func)
        {
            std::bitset<64> mask;
            mask.set(c1);
            mask.set(c2);
            mask.set(c3);
            for (auto i (entities_.begin()); i != entities_.end(); )
            {
                auto next (std::next(i));
                elem& e (i->second);
                if ((e.components & mask) == mask)
                {
                    func(i, var_ref<t1>(offset(e, c1), e),
                            var_ref<t2>(offset(e, c2), e),
                            var_ref<t3>(offset(e, c3), e));
                }
                i = next;
            }
        }

    bool exists (entity en) const
        { return entities_.count(en); }

    template <typename type>
    void set (entity en, component_id c_id, type val)
        { return set<type>(find(en), c_id, std::move(val)); }

    template <typename type>
    void set (iterator en, component_id c_id, type val)
        {
            const component& c (components_[c_id]);
            elem&  e   (en->second);
            size_t off (offset(e, c_id));

            if (!e.components[c_id])
            {
                if (e.data.size() < off)
                    e.data.resize(off + c.size());
                else
                    e.data.insert(e.data.begin() + off, c.size(), 0);

                e.components.set(c_id);
            }

            if (is_flat<type>::value)
            {
                assert(e.data.size() >= off + sizeof(type));
                new (&*e.data.begin() + off) type(std::move(val));
            }
            else
            {
                assert(e.data.size() >= off + sizeof(holder<type>));
                auto ptr (reinterpret_cast<holder<type>*>(&*e.data.begin() + off));
                new (ptr) holder<type>(std::move(val));
            }
        }

    template <typename type>
    const type& get (entity en, component_id c_id) const
        { return get<type>(find(en), c_id); }

    template <typename type>
    const type& get (const_iterator en, component_id c_id) const
        {
            auto& e (en->second);
            if (!e.components[c_id])
                throw std::logic_error("entity does not have component");

            return get<type>(e, c_id);
        }

private:
    template <typename type>
    const type& get (const elem& e, component_id c_id) const
        {
            size_t off (offset(e, c_id));
            if (is_flat<type>::value)
                return *reinterpret_cast<const type*>(&*e.data.begin() + off);

            auto ptr (reinterpret_cast<const holder<type>*>(&*e.data.begin() + off));

            return ptr->held();
        }

    size_t offset (const elem& e, component_id c) const
        {
            assert(c < components_.size());
            assert((c & cache_mask) < component_offsets_.size());
            size_t result (component_offsets_[c & cache_mask]);

            for (component_id search (cache_size); search < c; ++search)
            {
                if (e.components[search])
                    result += components_[search].size();
            }
            return result;
        }

private:
    /** Keeps track of entity IDs to give out. */
    uint32_t                            next_id_;
    /** The list of registered components. */
    std::vector<component>              components_;
    /** Mapping entity IDs to their data. */
    std::unordered_map<uint32_t, elem>  entities_;
    /** A lookup table for the data offsets of components.
     *  The index is the bitmask as used in elem::components. */
    std::vector<size_t>                 component_offsets_;
    /** A bitmask to quickly determine whether a certain combination of
     ** components has a flat memory layout or not. */
    std::bitset<64>                     flat_mask_;
};

} // namespace es
<|endoftext|>


<commit_before>/*
 * Actuator.cc
 *
 *      Author: Lunatic
 */

#include <cstring>
#include <cstdlib>

#include "actuator.h"
#include "lexer.h"
#include "alarm.h"

namespace Script {

bool Env::contains(const std::string& name) {
	return vars.find(name) != vars.end();
}
std::string Env::get(const std::string& name) {
	return vars.find(name)->second;
}
void Env::put(const std::string& name, const std::string& value) {
	vars[name] = value;
}

//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////

Actuator::Actuator(Parser& _parser) :
		parser(_parser) {
}

Actuator::~Actuator() {
}

void Actuator::load() {
	Instruction inst;
	while (parser.has_next()) {
		parser.next(inst);
		if (inst.type == kLabel) {
			std::map<std::string, size_t>::iterator it = labels.find(inst.name);
			if (it == labels.end())
				labels[inst.name] = insts.size();
		} else
			insts.push_back(inst);
	}

}

/*
 * 将Token转换为Instruction{type, name, params}
 *

void Actuator::load(const std::string& script_code) {
	Lexer lexer(script_code);
	std::vector<Token> tokens;
	while (!lexer.finish()) {
		Log::info("新的一行");

		Instruction inst;
		tokens.clear();

		for (;;) {
			Token token;
			int stat = lexer.next_token(token);
			if (stat >= 0)
				break;
			Log::info(token.to_str());
			tokens.push_back(token);
		}

		if (tokens.empty())
			continue;
		if (tokens[0].type != kName) {
			error("语法错误load", tokens[0].pos);
		}

		inst.name = tokens[0].token;
		inst.pos = tokens[0].pos;

		if (tokens.size() == 1) {
			inst.type = kInstruction;
		} else if (tokens[1].type == kColon) {
			inst.type = kLabel;
		} else { //处理指令参数
			inst.type = kInstruction;
			for (size_t i = 1; i < tokens.size(); ++i) {
				switch (tokens[i].type) {
				case kInt:
				case kReal:
				case kString:
				case kName:
				case KCmp:
					inst.params.push_back(tokens[i]);
					break;
				default:
					Log::error("%zu", i);
					Log::error(tokens[i].to_str());
					error("参数不符合要求！", tokens[i].pos);
				}
			}
		}

		if (inst.type == kLabel) {
			std::map<std::string, size_t>::iterator it = labels.find(inst.name);
			if (it == labels.end())
				labels[inst.name] = insts.size();
		} else
			insts.push_back(inst);

	}
	Log::info("加载完毕！");
}
*/

/*
 * 如果是变量 存在则直接取出 否则报错 ？不太妥当
 * 其他直接返回
 *
 */
std::string get_val_or_var(Env& env, Token& token) {
	if (token.type == kName) { //处理变量
		if (env.contains(token.token)) {
			return env.get(token.token);
		} else
			error("变量未定义" + token.token, token.pos); //？不太妥当
	}
	return token.token;
}

bool is_real(const std::string& number) {
	return std::string::npos != number.find('.');
}

bool is_int(const std::string& number) {
	return !is_real(number);
}

long long str2int(const std::string& val) {
//char *err = NULL;
//long long retval = std::strtoll(val.c_str(), &err, 10);
//if (*err == '\0')
//return retval;

//忽略错误
	return std::strtoll(val.c_str(), NULL, 10);
}

long double str2double(const std::string& val) {
//char *err = NULL;
//long double retval = std::strtold(val.c_str(), &err);
//if (*err == '\0')
//return retval;
//else
//
//error!
//return 0.0;
	return std::strtold(val.c_str(), NULL);

}
//3.3333

bool is_zero(const std::string& vfloat) {
	bool left = true, right = true;
	size_t start = 0, pos = vfloat.find('.');

	if (pos == std::string::npos)
		pos = vfloat.size();

	while (start < pos) {
		if (vfloat[start++] != '0') {
			left = false;
		}
	}
	while (++pos < vfloat.size()) {
		if (vfloat[pos] != '0') {
			right = false;
		}
	}
	return left && right;

}

std::string eval(const std::string& cmd, const std::string& arg1,
		const std::string& arg2) {

#define EVAL(RES, X, Y, OP)                          \
	do {                                             \
		if(is_real(X) || is_real(Y)) {               \
			RES << (str2double(X) OP str2double(Y)); \
		} else {                                     \
			RES << (str2int(X) OP str2int(Y));       \
		}                                            \
	}while(0)                                        \

	std::stringstream ss;
	if (cmd == "add") { // +
		EVAL(ss, arg1, arg2, +);
	} else if (cmd == "min") { //-
		EVAL(ss, arg1, arg2, -);
	} else if (cmd == "mul") { // *
		EVAL(ss, arg1, arg2, *);
	} else if (cmd == "div") { // /
		if (is_zero(arg2)) {
			error("除数不能为0！");
		}
		EVAL(ss, arg1, arg2, /);
	}
	return ss.str();

#undef EVAL
}

/*
 *
 * 依次处理指令
 * exit
 * goto
 * set
 *
 *
 *
 */
void Actuator::run(Env& env) {
	for (size_t idx = 0; idx < insts.size(); /*++idx*/) {
		Instruction& pc = insts[idx]; //模拟PC寄存器
		Log::info(pc.to_str());

		if (pc.name == "exit") {
			if (pc.params.empty())
				break;
			else
				error("exit不能有参数", pc.pos);
		} else if (pc.name == "goto") {
			if (pc.params.size() == 1 && pc.params[0].type == kName) {
				//处理指令跳转
				std::map<std::string, size_t>::iterator it = labels.find(
						pc.params[0].token);
				if (it == labels.end()) {
					error("找不到带跳转位置", pc.pos);
				} else {
					idx = it->second; //更新到下一条指令
					continue;
				}
			} else {
				error("goto语句后面必须带有一个跳转Label", pc.pos);
			}
		} else if (pc.name == "mov") {
			if (pc.params.size() == 2 && pc.params[0].type == kName) {
				std::string val = get_val_or_var(env, pc.params[1]);
				env.put(pc.params[0].token, val);
			} else {
				error("set语句必须是一个变量和一个对象参数", pc.pos);
			}
		} else if (pc.name == "add" || pc.name == "min" || pc.name == "mul"
				|| pc.name == "div") {
			//add a b 1 => a = b + 1
			if (pc.params.size() == 3 && pc.params[0].type == kName) {
				//把param1和param2求值 把结果放入param0为key结果为value的环境map中
				std::string arg1 = get_val_or_var(env, pc.params[1]);
				std::string arg2 = get_val_or_var(env, pc.params[2]);
				env.put(pc.params[0].token, eval(pc.name, arg1, arg2));
			} else {
				error(pc.name + "命令需要一个变量和两个参数", pc.pos);
			}
		} else if (pc.name == "if") { //if value1 [opcode value2] goto label
			bool jmp = false;
			if (pc.params.size() == 5 && pc.params[1].type == KCmp) { //if val goto label
				std::string left = get_val_or_var(env, pc.params[0]);
				std::string right = get_val_or_var(env, pc.params[2]);

				if (pc.params[1].token == "==") {
					jmp = left == right;
				} else if (pc.params[1].token == "!=") {
					jmp = left != right;
				} else if (pc.params[1].token == "<") {
					jmp = str2double(left) < str2double(right);
				} else if (pc.params[1].token == ">") {
					jmp = str2double(left) > str2double(right);
				} else if (pc.params[1].token == "<=") {
					jmp = str2double(left) <= str2double(right);
				} else if (pc.params[1].token == ">=") {
					jmp = str2double(left) < str2double(right);
				}

				if (jmp && pc.params[3].type == kName
						&& pc.params[3].token == "goto"
						&& pc.params[4].type == kName) {
					//处理指令跳转
					std::map<std::string, size_t>::iterator it = labels.find(
							pc.params[4].token);
					if (it == labels.end()) {
						error("找不到带跳转位置", pc.params[4].pos);
					} else {
						idx = it->second; //更新到下一条指令
						continue;
					}
				} else if (jmp) {
					error("goto语句后面必须带有一个跳转Label", pc.params[4].pos);
				}
			}
		} else if (pc.name == "print") {

			for (std::vector<Token>::iterator it = pc.params.begin();
					it != pc.params.end(); ++it) {
				if (it->type == kName) {
					if (env.contains(it->token)) {
						std::cout << env.get(it->token);
					} else
						error("变量未定义！", it->pos);
				} else
					std::cout << it->token;
				std::cout << std::endl;
			}
		} else if (pc.name == "read") {

			for (std::vector<Token>::iterator it = pc.params.begin();
					it != pc.params.end(); ++it) {
				if (it->type != kName) {
					error("必须是变量！", it->pos);
				}
				std::string temp;
				std::cin >> temp;
				env.put(it->token, temp);
			}
		}
		idx++;
	}

}

} /* namespace Script */
<commit_msg>精确除0报错行列<commit_after>/*
 * Actuator.cc
 *
 *      Author: Lunatic
 */

#include <cstring>
#include <cstdlib>

#include "actuator.h"
#include "lexer.h"
#include "alarm.h"

namespace Script {

bool Env::contains(const std::string& name) {
	return vars.find(name) != vars.end();
}
std::string Env::get(const std::string& name) {
	return vars.find(name)->second;
}
void Env::put(const std::string& name, const std::string& value) {
	vars[name] = value;
}

//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////

Actuator::Actuator(Parser& _parser) :
		parser(_parser) {
}

Actuator::~Actuator() {
}

void Actuator::load() {
	Instruction inst;
	while (parser.has_next()) {
		parser.next(inst);
		if (inst.type == kLabel) {
			std::map<std::string, size_t>::iterator it = labels.find(inst.name);
			if (it == labels.end())
				labels[inst.name] = insts.size();
		} else
			insts.push_back(inst);
	}

}

/*
 * 将Token转换为Instruction{type, name, params}
 *

void Actuator::load(const std::string& script_code) {
	Lexer lexer(script_code);
	std::vector<Token> tokens;
	while (!lexer.finish()) {
		Log::info("新的一行");

		Instruction inst;
		tokens.clear();

		for (;;) {
			Token token;
			int stat = lexer.next_token(token);
			if (stat >= 0)
				break;
			Log::info(token.to_str());
			tokens.push_back(token);
		}

		if (tokens.empty())
			continue;
		if (tokens[0].type != kName) {
			error("语法错误load", tokens[0].pos);
		}

		inst.name = tokens[0].token;
		inst.pos = tokens[0].pos;

		if (tokens.size() == 1) {
			inst.type = kInstruction;
		} else if (tokens[1].type == kColon) {
			inst.type = kLabel;
		} else { //处理指令参数
			inst.type = kInstruction;
			for (size_t i = 1; i < tokens.size(); ++i) {
				switch (tokens[i].type) {
				case kInt:
				case kReal:
				case kString:
				case kName:
				case KCmp:
					inst.params.push_back(tokens[i]);
					break;
				default:
					Log::error("%zu", i);
					Log::error(tokens[i].to_str());
					error("参数不符合要求！", tokens[i].pos);
				}
			}
		}

		if (inst.type == kLabel) {
			std::map<std::string, size_t>::iterator it = labels.find(inst.name);
			if (it == labels.end())
				labels[inst.name] = insts.size();
		} else
			insts.push_back(inst);

	}
	Log::info("加载完毕！");
}
*/

/*
 * 如果是变量 存在则直接取出 否则报错 ？不太妥当
 * 其他直接返回
 *
 */
std::string get_val_or_var(Env& env, Token& token) {
	if (token.type == kName) { //处理变量
		if (env.contains(token.token)) {
			return env.get(token.token);
		} else
			error("变量未定义" + token.token, token.pos); //？不太妥当
	}
	return token.token;
}

bool is_real(const std::string& number) {
	return std::string::npos != number.find('.');
}

bool is_int(const std::string& number) {
	return !is_real(number);
}

long long str2int(const std::string& val) {
//char *err = NULL;
//long long retval = std::strtoll(val.c_str(), &err, 10);
//if (*err == '\0')
//return retval;

//忽略错误
	return std::strtoll(val.c_str(), NULL, 10);
}

long double str2double(const std::string& val) {
//char *err = NULL;
//long double retval = std::strtold(val.c_str(), &err);
//if (*err == '\0')
//return retval;
//else
//
//error!
//return 0.0;
	return std::strtold(val.c_str(), NULL);

}
//3.3333

bool is_zero(const std::string& vfloat) {
	bool left = true, right = true;
	size_t start = 0, pos = vfloat.find('.');

	if (pos == std::string::npos)
		pos = vfloat.size();

	while (start < pos) {
		if (vfloat[start++] != '0') {
			left = false;
		}
	}
	while (++pos < vfloat.size()) {
		if (vfloat[pos] != '0') {
			right = false;
		}
	}
	return left && right;

}

std::string eval(const std::string& cmd, const std::string& arg1,
		const std::string& arg2, const Position& pos) {

#define EVAL(RES, X, Y, OP)                          \
	do {                                             \
		if(is_real(X) || is_real(Y)) {               \
			RES << (str2double(X) OP str2double(Y)); \
		} else {                                     \
			RES << (str2int(X) OP str2int(Y));       \
		}                                            \
	}while(0)                                        \

	std::stringstream ss;
	if (cmd == "add") { // +
		EVAL(ss, arg1, arg2, +);
	} else if (cmd == "min") { //-
		EVAL(ss, arg1, arg2, -);
	} else if (cmd == "mul") { // *
		EVAL(ss, arg1, arg2, *);
	} else if (cmd == "div") { // /
		if (is_zero(arg2)) {
			error("除数不能为0！", pos);
		}
		EVAL(ss, arg1, arg2, /);
	}
	return ss.str();

#undef EVAL
}

/*
 *
 * 依次处理指令
 * exit
 * goto
 * set
 *
 *
 *
 */
void Actuator::run(Env& env) {
	for (size_t idx = 0; idx < insts.size(); /*++idx*/) {
		Instruction& pc = insts[idx]; //模拟PC寄存器
		Log::info(pc.to_str());

		if (pc.name == "exit") {
			if (pc.params.empty())
				break;
			else
				error("exit不能有参数", pc.pos);
		} else if (pc.name == "goto") {
			if (pc.params.size() == 1 && pc.params[0].type == kName) {
				//处理指令跳转
				std::map<std::string, size_t>::iterator it = labels.find(
						pc.params[0].token);
				if (it == labels.end()) {
					error("找不到带跳转位置", pc.pos);
				} else {
					idx = it->second; //更新到下一条指令
					continue;
				}
			} else {
				error("goto语句后面必须带有一个跳转Label", pc.pos);
			}
		} else if (pc.name == "mov") {
			if (pc.params.size() == 2 && pc.params[0].type == kName) {
				std::string val = get_val_or_var(env, pc.params[1]);
				env.put(pc.params[0].token, val);
			} else {
				error("set语句必须是一个变量和一个对象参数", pc.pos);
			}
		} else if (pc.name == "add" || pc.name == "min" || pc.name == "mul"
				|| pc.name == "div") {
			//add a b 1 => a = b + 1
			if (pc.params.size() == 3 && pc.params[0].type == kName) {
				//把param1和param2求值 把结果放入param0为key结果为value的环境map中
				std::string arg1 = get_val_or_var(env, pc.params[1]);
				std::string arg2 = get_val_or_var(env, pc.params[2]);
				env.put(pc.params[0].token, eval(pc.name, arg1, arg2, pc.params[2].pos));
			} else {
				error(pc.name + "命令需要一个变量和两个参数", pc.pos);
			}
		} else if (pc.name == "if") { //if value1 [opcode value2] goto label
			bool jmp = false;
			if (pc.params.size() == 5 && pc.params[1].type == KCmp) { //if val goto label
				std::string left = get_val_or_var(env, pc.params[0]);
				std::string right = get_val_or_var(env, pc.params[2]);

				if (pc.params[1].token == "==") {
					jmp = left == right;
				} else if (pc.params[1].token == "!=") {
					jmp = left != right;
				} else if (pc.params[1].token == "<") {
					jmp = str2double(left) < str2double(right);
				} else if (pc.params[1].token == ">") {
					jmp = str2double(left) > str2double(right);
				} else if (pc.params[1].token == "<=") {
					jmp = str2double(left) <= str2double(right);
				} else if (pc.params[1].token == ">=") {
					jmp = str2double(left) < str2double(right);
				}

				if (jmp && pc.params[3].type == kName
						&& pc.params[3].token == "goto"
						&& pc.params[4].type == kName) {
					//处理指令跳转
					std::map<std::string, size_t>::iterator it = labels.find(
							pc.params[4].token);
					if (it == labels.end()) {
						error("找不到带跳转位置", pc.params[4].pos);
					} else {
						idx = it->second; //更新到下一条指令
						continue;
					}
				} else if (jmp) {
					error("goto语句后面必须带有一个跳转Label", pc.params[4].pos);
				}
			}
		} else if (pc.name == "print") {

			for (std::vector<Token>::iterator it = pc.params.begin();
					it != pc.params.end(); ++it) {
				if (it->type == kName) {
					if (env.contains(it->token)) {
						std::cout << env.get(it->token);
					} else
						error("变量未定义！", it->pos);
				} else
					std::cout << it->token;
				std::cout << std::endl;
			}
		} else if (pc.name == "read") {

			for (std::vector<Token>::iterator it = pc.params.begin();
					it != pc.params.end(); ++it) {
				if (it->type != kName) {
					error("必须是变量！", it->pos);
				}
				std::string temp;
				std::cin >> temp;
				env.put(it->token, temp);
			}
		}
		idx++;
	}

}

} /* namespace Script */
<|endoftext|>