<commit_before>/* ---------------------------------------------------------------------
 * Numenta Platform for Intelligent Computing (NuPIC)
 * Copyright (C) 2013-2015, Numenta, Inc.  Unless you have an agreement
 * with Numenta, Inc., for a separate license for this software code, the
 * following terms and conditions apply:
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero Public License version 3 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero Public License for more details.
 *
 * You should have received a copy of the GNU Affero Public License
 * along with this program.  If not, see http://www.gnu.org/licenses.
 *
 * http://numenta.org/licenses/
 * ---------------------------------------------------------------------
 */

#include <algorithm> // std::generate
#include <iostream>
#include <vector>

#include "nupic/algorithms/Anomaly.hpp"
#include "nupic/algorithms/Cells4.hpp"  //TODO use TM instead
#include "nupic/algorithms/SpatialPooler.hpp"
#include "nupic/encoders/ScalarEncoder.hpp"

#include "nupic/os/Timer.hpp"
#include "nupic/utils/VectorHelpers.hpp"
#include "nupic/utils/Random.hpp"

namespace examples {

using namespace std;
using namespace nupic;
using namespace nupic::utils;
using nupic::ScalarEncoder;
using nupic::algorithms::spatial_pooler::SpatialPooler;
using nupic::algorithms::Cells4::Cells4;
using nupic::algorithms::anomaly::Anomaly;
using nupic::algorithms::anomaly::AnomalyMode;

// work-load
void run() {
  const UInt COLS = 2048; // number of columns in SP, TP
  const UInt DIM_INPUT = 10000;
  const UInt CELLS = 10; // cells per column in TP
  const UInt EPOCHS = 5000; // number of iterations (calls to SP/TP compute() )
  std::cout << "starting test. DIM_INPUT=" << DIM_INPUT
  		<< ", DIM=" << COLS << ", CELLS=" << CELLS << std::endl;
  std::cout << "EPOCHS = " << EPOCHS << std::endl;

  // initialize SP, TP, Anomaly, AnomalyLikelihood
  Timer tInit(true);
  ScalarEncoder enc(133, -100.0, 100.0, DIM_INPUT, 0.0, 0.0, false);
  SpatialPooler sp(vector<UInt>{DIM_INPUT}, vector<UInt>{COLS});
  Cells4 tp(COLS, CELLS, 12, 8, 15, 5, .5f, .8f, 1.0f, .1f, .1f, 0.0f,
            false, 42, true, false);
  Anomaly an(5, AnomalyMode::LIKELIHOOD);
  tInit.stop();

  // data for processing input
  vector<UInt> input(DIM_INPUT);
  vector<UInt> outSP(COLS); // active array, output of SP/TP
  vector<UInt> outTP(tp.nCells());
  vector<Real> rIn(COLS); // input for TP (must be Reals)
  vector<Real> rOut(tp.nCells());
  Real res = 0.0; //for anomaly:
  vector<UInt> prevPred_(outSP.size());
  Random rnd;

  // Start a stopwatch timer
  printf("starting:  %d iterations.", EPOCHS);
  Timer tAll(true);
  Timer tRng, tEnc, tSP, tTP, tAn;

  //run
  for (UInt e = 0; e < EPOCHS; e++) {
    //Input
//    generate(input.begin(), input.end(), [&] () { return rnd.getUInt32(2); });
    tRng.start();
    const Real r = rnd.getUInt32(100) - rnd.getUInt32(100)*rnd.getReal64(); //rnd from range -100..100
    tRng.stop();

    //Encode
    tEnc.start();
    enc.encodeIntoArray(r, input.data());
    tEnc.stop();

    //SP
    tSP.start();
    fill(outSP.begin(), outSP.end(), 0);
    sp.compute(input.data(), true, outSP.data());
    sp.stripUnlearnedColumns(outSP.data());
    tSP.stop();

    //TP
    tTP.start();
    rIn = VectorHelpers::castVectorType<UInt, Real>(outSP);
    tp.compute(rIn.data(), rOut.data(), true, true);
    outTP = VectorHelpers::castVectorType<Real, UInt>(rOut);
    tTP.stop();

    //Anomaly
    tAn.start();
    res = an.compute(outSP /*active*/, prevPred_ /*prev predicted*/);
    prevPred_ = outTP; //to be used as predicted T-1
    tAn.stop();

    // print
    if (e == EPOCHS - 1) {
      tAll.stop();

      cout << "Epoch = " << e << endl;
      cout << "Anomaly = " << res << endl;
      VectorHelpers::print_vector(VectorHelpers::binaryToSparse<UInt>(outSP), ",", "SP= ");
      VectorHelpers::print_vector(VectorHelpers::binaryToSparse<UInt>(VectorHelpers::cellsToColumns(outTP, CELLS)), ",", "TP= ");
      NTA_CHECK(outSP[69] == 0) << "A value in SP computed incorrectly";
      NTA_CHECK(outTP[42] == 0) << "Incorrect value in TP";
      cout << "==============TIMERS============" << endl;
      cout << "Init:\t" << tInit.getElapsed() << endl;
      cout << "Random:\t" << tRng.getElapsed() << endl;
      cout << "Encode:\t" << tEnc.getElapsed() << endl;
      cout << "SP:\t" << tSP.getElapsed() << endl;
      cout << "TP:\t" << tTP.getElapsed() << endl;
      cout << "AN:\t" << tAn.getElapsed() << endl;

      const size_t timeTotal = tAll.getElapsed();
      cout << "Total elapsed time = " << timeTotal << " seconds" << endl;
      #ifdef NDEBUG
        const size_t CI_avg_time = 7*Timer::getSpeed(); //sec
        NTA_CHECK(timeTotal <= CI_avg_time) << //we'll see how stable the time result in CI is, if usable
          "HelloSPTP test slower than expected! (" << timeTotal << ",should be "<< CI_avg_time;
      #endif
    }
  } //end for

} //end run()
} //-ns
<commit_msg> Merge remote-tracking branch 'origin/master' into OSX_build<commit_after>/* ---------------------------------------------------------------------
 * Numenta Platform for Intelligent Computing (NuPIC)
 * Copyright (C) 2013-2015, Numenta, Inc.  Unless you have an agreement
 * with Numenta, Inc., for a separate license for this software code, the
 * following terms and conditions apply:
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero Public License version 3 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero Public License for more details.
 *
 * You should have received a copy of the GNU Affero Public License
 * along with this program.  If not, see http://www.gnu.org/licenses.
 *
 * http://numenta.org/licenses/
 * ---------------------------------------------------------------------
 */

#include <algorithm> // std::generate
#include <iostream>
#include <vector>

#include "nupic/algorithms/Anomaly.hpp"
#include "nupic/algorithms/Cells4.hpp"  //TODO use TM instead
#include "nupic/algorithms/SpatialPooler.hpp"
#include "nupic/encoders/ScalarEncoder.hpp"

#include "nupic/os/Timer.hpp"
#include "nupic/utils/VectorHelpers.hpp"
#include "nupic/utils/Random.hpp"

namespace examples {

using namespace std;
using namespace nupic;
using namespace nupic::utils;
using nupic::ScalarEncoder;
using nupic::algorithms::spatial_pooler::SpatialPooler;
using nupic::algorithms::Cells4::Cells4;
using nupic::algorithms::anomaly::Anomaly;
using nupic::algorithms::anomaly::AnomalyMode;

// work-load
void run() {
  const UInt COLS = 2048; // number of columns in SP, TP
  const UInt DIM_INPUT = 10000;
  const UInt CELLS = 10; // cells per column in TP
#ifdef NDEBUG
  const UInt EPOCHS = 5000; // number of iterations (calls to SP/TP compute() )
#else
  const UInt EPOCHS = 500; // make test faster in Debug
#endif

  std::cout << "starting test. DIM_INPUT=" << DIM_INPUT
  		<< ", DIM=" << COLS << ", CELLS=" << CELLS << std::endl;
  std::cout << "EPOCHS = " << EPOCHS << std::endl;

  // initialize SP, TP, Anomaly, AnomalyLikelihood
  Timer tInit(true);
  ScalarEncoder enc(133, -100.0, 100.0, DIM_INPUT, 0.0, 0.0, false);
  SpatialPooler sp(vector<UInt>{DIM_INPUT}, vector<UInt>{COLS});
  Cells4 tp(COLS, CELLS, 12, 8, 15, 5, .5f, .8f, 1.0f, .1f, .1f, 0.0f,
            false, 42, true, false);
  Anomaly an(5, AnomalyMode::LIKELIHOOD);
  tInit.stop();

  // data for processing input
  vector<UInt> input(DIM_INPUT);
  vector<UInt> outSP(COLS); // active array, output of SP/TP
  vector<UInt> outTP(tp.nCells());
  vector<Real> rIn(COLS); // input for TP (must be Reals)
  vector<Real> rOut(tp.nCells());
  Real res = 0.0; //for anomaly:
  vector<UInt> prevPred_(outSP.size());
  Random rnd;

  // Start a stopwatch timer
  printf("starting:  %d iterations.", EPOCHS);
  Timer tAll(true);
  Timer tRng, tEnc, tSP, tTP, tAn;

  //run
  for (UInt e = 0; e < EPOCHS; e++) {
    //Input
//    generate(input.begin(), input.end(), [&] () { return rnd.getUInt32(2); });
    tRng.start();
    const Real r = rnd.getUInt32(100) - rnd.getUInt32(100)*rnd.getReal64(); //rnd from range -100..100
    tRng.stop();

    //Encode
    tEnc.start();
    enc.encodeIntoArray(r, input.data());
    tEnc.stop();

    //SP
    tSP.start();
    fill(outSP.begin(), outSP.end(), 0);
    sp.compute(input.data(), true, outSP.data());
    sp.stripUnlearnedColumns(outSP.data());
    tSP.stop();

    //TP
    tTP.start();
    rIn = VectorHelpers::castVectorType<UInt, Real>(outSP);
    tp.compute(rIn.data(), rOut.data(), true, true);
    outTP = VectorHelpers::castVectorType<Real, UInt>(rOut);
    tTP.stop();

    //Anomaly
    tAn.start();
    res = an.compute(outSP /*active*/, prevPred_ /*prev predicted*/);
    prevPred_ = outTP; //to be used as predicted T-1
    tAn.stop();

    // print
    if (e == EPOCHS - 1) {
      tAll.stop();

      cout << "Epoch = " << e << endl;
      cout << "Anomaly = " << res << endl;
      VectorHelpers::print_vector(VectorHelpers::binaryToSparse<UInt>(outSP), ",", "SP= ");
      VectorHelpers::print_vector(VectorHelpers::binaryToSparse<UInt>(VectorHelpers::cellsToColumns(outTP, CELLS)), ",", "TP= ");
      NTA_CHECK(outSP[69] == 0) << "A value in SP computed incorrectly";
      NTA_CHECK(outTP[42] == 0) << "Incorrect value in TP";
      cout << "==============TIMERS============" << endl;
      cout << "Init:\t" << tInit.getElapsed() << endl;
      cout << "Random:\t" << tRng.getElapsed() << endl;
      cout << "Encode:\t" << tEnc.getElapsed() << endl;
      cout << "SP:\t" << tSP.getElapsed() << endl;
      cout << "TP:\t" << tTP.getElapsed() << endl;
      cout << "AN:\t" << tAn.getElapsed() << endl;

      const size_t timeTotal = tAll.getElapsed();
      cout << "Total elapsed time = " << timeTotal << " seconds" << endl;
      #ifdef NDEBUG
        const size_t CI_avg_time = 7*Timer::getSpeed(); //sec
        NTA_CHECK(timeTotal <= CI_avg_time) << //we'll see how stable the time result in CI is, if usable
          "HelloSPTP test slower than expected! (" << timeTotal << ",should be "<< CI_avg_time;
      #endif
    }
  } //end for

} //end run()
} //-ns
<|endoftext|>


<commit_before>/*
   For more information, please see: http://software.sci.utah.edu

   The MIT License

   Copyright (c) 2008 Scientific Computing and Imaging Institute,
   University of Utah.

   Permission is hereby granted, free of charge, to any person obtaining a
   copy of this software and associated documentation files (the "Software"),
   to deal in the Software without restriction, including without limitation
   the rights to use, copy, modify, merge, publish, distribute, sublicense,
   and/or sell copies of the Software, and to permit persons to whom the
   Software is furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included
   in all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
   DEALINGS IN THE SOFTWARE.
*/

/**
  \file    BOVConverter.h
  \author  Tom Fogal
           SCI Institute
           University of Utah
*/
#include <cstdarg>
#include <fstream>
#include <sstream>
#include "BOVConverter.h"
#include "IO/KeyValueFileParser.h"
#include "Controller/Controller.h"
#include "Basics/SysTools.h"

enum DataType {
  UnknownType,
  Short,
  UShort, /* extension! */
  Float,
  Integer,
  Char
};

static DataType bov_type(const KeyValueFileParser &kvp);
static const KeyValPair *find_header(const KeyValueFileParser &,
                                     const char *s, ...);

BOVConverter::BOVConverter()
{
  m_vConverterDesc = "Brick of Values";
  m_vSupportedExt.push_back("BOV");
}

bool BOVConverter::ConvertToRAW(
                            const std::string& strSourceFilename,
                            const std::string&, bool,
                            UINT64& iHeaderSkip, UINT64& iComponentSize,
                            UINT64& iComponentCount, bool& bConvertEndianness,
                            bool& bSigned, bool& bIsFloat,
                            UINT64VECTOR3& vVolumeSize,
                            FLOATVECTOR3& vVolumeAspect, std::string& strTitle,
                            UVFTables::ElementSemanticTable& eType,
                            std::string& strIntermediateFile,
                            bool& bDeleteIntermediateFile)
{
  MESSAGE("Attempting to convert BOV: %s", strSourceFilename.c_str());
  KeyValueFileParser hdr(strSourceFilename.c_str());

  if(!hdr.FileReadable()) {
    T_ERROR("Could not parse %s; could not open.", strSourceFilename.c_str());
    return false;
  }
  KeyValPair *file = hdr.GetData("DATA_FILE");
  const KeyValPair *size = find_header(hdr, "DATA SIZE", "DATA_SIZE", NULL);
  KeyValPair *aspect_x = hdr.GetData("BRICK X_AXIS");
  KeyValPair *aspect_y = hdr.GetData("BRICK Y_AXIS");
  KeyValPair *aspect_z = hdr.GetData("BRICK Z_AXIS");
  strTitle = "BOV Volume";
  eType = UVFTables::ES_UNDEFINED;

  {
    iHeaderSkip = 0;
    strIntermediateFile = SysTools::CanonicalizePath(file->strValue);
    // Try the path the file gave first..
    if(!SysTools::FileExists(strIntermediateFile)) {
      // .. but if that didn't work, prepend the directory of the .bov file.
      strIntermediateFile = SysTools::CanonicalizePath(
                              SysTools::GetPath(strSourceFilename) +
                              file->strValue
                            );
      if(!SysTools::FileExists(strIntermediateFile)) {
        T_ERROR("Cannot find file (%s) referenced in BOV!",
                file->strValue.c_str());
      }
    }
    MESSAGE("Reading data from %s", strIntermediateFile.c_str());
    bDeleteIntermediateFile = false;
  }
  {
    std::istringstream ss(size->strValue);
    ss >> vVolumeSize[0] >> vVolumeSize[1] >> vVolumeSize[2];
    MESSAGE("Dimensions: %ux%ux%u",
            UINT32(vVolumeSize[0]), UINT32(vVolumeSize[1]), UINT32(vVolumeSize[2]));
    iHeaderSkip = 0;
  }
  {
    iComponentCount = 1;
    iComponentSize = 8;
    bConvertEndianness = false;
    bIsFloat = false;
    bSigned = false;

    switch(bov_type(hdr)) {
      case Char:
        iComponentSize = 8;
        bSigned = true;
        break;
      case Short:
        iComponentSize = 16;
        bSigned = true;
        break;
      case UShort: // extension!
        iComponentSize = 16;
        break;
      case Integer:
        iComponentSize = 32;
        bSigned = true;
        break;
      case Float:
        iComponentSize = 32;
        bSigned = true;
        bIsFloat = true;
        break;
      default:
        T_ERROR("Unknown BOV data type.");
        return false;
    }
    MESSAGE("%lu-bit %s, %s data", iComponentSize,
            bSigned ? "signed" : "unsigned",
            bIsFloat ? "floating point" : "integer");
  }
  {
    // e.g. "BRICK X_AXIS 1.000 0.000 0.000".  Might not exist.
    vVolumeAspect[0] = vVolumeAspect[1] = vVolumeAspect[2] = 1.0;
    if(aspect_x && aspect_y && aspect_z) {
      std::stringstream x(aspect_x->strValue);
      std::stringstream y(aspect_y->strValue);
      std::stringstream z(aspect_z->strValue);
      float junk;
      x >> vVolumeAspect[0];
      y >> junk >> vVolumeAspect[1];
      z >> junk >> junk >> vVolumeAspect[2];
    }
    MESSAGE("Aspect: %2.2fx%2.2fx%2.2f",
            vVolumeAspect[0], vVolumeAspect[1], vVolumeAspect[2]);
  }
  return true;
}

bool BOVConverter::ConvertToNative(const std::string& raw,
                                   const std::string& target,
                                   UINT64 skip, UINT64 component_size,
                                   UINT64 n_components, bool is_signed,
                                   bool fp, UINT64VECTOR3 dimensions,
                                   FLOATVECTOR3 aspect, bool batch,
                                   const bool bQuantizeTo8Bit)
{
  std::string fn_data = SysTools::RemoveExt(target);

  std::ofstream header(target.c_str(), std::ios::out | std::ios::trunc);

  std::string data_format;
  switch(component_size) {
    case 8: data_format = "BYTE"; break;
    case 16: data_format = "SHORT"; break;
    case 32:
      if(fp) { data_format = "FLOAT"; }
      else   { data_format = "INT"; }
      break;
    case 64:
      // In BOV, a 64bit integer dataset is a 2-component 32-bit integer
      // dataset.  As far as I can tell...
      if(fp) { data_format = "DOUBLE"; }
      else   { data_format = "INT"; }
      break;
  }
  header << "DATA_FILE: " << raw << ".data" << std::endl
         << "DATA SIZE: "
            << dimensions[0] << " " << dimensions[1] << " " << dimensions[2]
         << std::endl
         << "DATA FORMAT: " << data_format << std::endl
         << "DATA_COMPONENTS: " << n_components << std::endl
         << "VARIABLE: from_imagevis3d" << std::endl
         << "BRICK_SIZE: "
            << dimensions[0] << " " << dimensions[1] << " " << dimensions[2]
         << std::endl
         << "CENTERING: nodal" << std::endl;
  header.close();

  // copy the raw file.
  return RAWConverter::ConvertToNative(raw, raw + ".data", skip,
                                       component_size, n_components, is_signed,
                                       fp, dimensions, aspect, batch,
                                       bQuantizeTo8Bit);
}

static DataType
bov_type(const KeyValueFileParser &kvp)
{
  const KeyValPair *format = NULL;
  DataType retval = UnknownType;

  // Search a list of strings until we find one.
  static const char formats[][16] = {
    "DATA FORMAT", "DATA_FORMAT", "FORMAT"
  };
  for(size_t i=0; i < sizeof(formats); ++i) {
    format = kvp.GetData(formats[i]);
    if(format != NULL) { break; }
  }

  // Might not have found any of those.
  if(format == NULL) {
    T_ERROR("Could not determine data format.  "
            "Is this a BOV file?  Potentially corrupt.");
    return UnknownType;
  }

  // Data type is specified by strings, but the exact string isn't quite
  // uniform.  Another search.
  static const char floats[][16] = {
    "FLOAT", "FLOATS"
  };
  for(size_t i=0; i < sizeof(floats); ++i) {
    if(format->strValueUpper == std::string(floats[i])) {
      retval = Float;
      break;
    }
  }
  if(format->strValueUpper == std::string("SHORT")) {
    retval = Short;
  }
  if(format->strValueUpper == std::string("USHORT")) {
    retval = UShort;
  }

  if(retval != Float && retval != Short) {
    WARNING("Unknown BOV data type '%s'", format->strValue.c_str());
  }
  return retval;
}

// Given a list of strings, returns the first KeyValPair that exists.  Useful
// since some bovs use "_" between words in a key, some don't...
// The argument list must be NULL-terminated.
static const KeyValPair *
find_header(const KeyValueFileParser &kvp, const char *s, ...)
{
  va_list args;
  const KeyValPair *ret;

  // First try 's'.
  ret = kvp.GetData(s);
  if(ret) { return ret; }

  va_start(args, s);
    const char *key = va_arg(args, const char *);
    while(key) {
      ret = kvp.GetData(key);
      if(ret) { return ret; }
    }
  va_end(args);
  return NULL;
}
<commit_msg>bov: support 8bit data.<commit_after>/*
   For more information, please see: http://software.sci.utah.edu

   The MIT License

   Copyright (c) 2008 Scientific Computing and Imaging Institute,
   University of Utah.

   Permission is hereby granted, free of charge, to any person obtaining a
   copy of this software and associated documentation files (the "Software"),
   to deal in the Software without restriction, including without limitation
   the rights to use, copy, modify, merge, publish, distribute, sublicense,
   and/or sell copies of the Software, and to permit persons to whom the
   Software is furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included
   in all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
   DEALINGS IN THE SOFTWARE.
*/

/**
  \file    BOVConverter.h
  \author  Tom Fogal
           SCI Institute
           University of Utah
*/
#include <cstdarg>
#include <fstream>
#include <sstream>
#include "BOVConverter.h"
#include "IO/KeyValueFileParser.h"
#include "Controller/Controller.h"
#include "Basics/SysTools.h"

enum DataType {
  UnknownType,
  Short,
  UShort, /* extension! */
  Float,
  Integer,
  Char
};

static DataType bov_type(const KeyValueFileParser &kvp);
static const KeyValPair *find_header(const KeyValueFileParser &,
                                     const char *s, ...);

BOVConverter::BOVConverter()
{
  m_vConverterDesc = "Brick of Values";
  m_vSupportedExt.push_back("BOV");
}

bool BOVConverter::ConvertToRAW(
                            const std::string& strSourceFilename,
                            const std::string&, bool,
                            UINT64& iHeaderSkip, UINT64& iComponentSize,
                            UINT64& iComponentCount, bool& bConvertEndianness,
                            bool& bSigned, bool& bIsFloat,
                            UINT64VECTOR3& vVolumeSize,
                            FLOATVECTOR3& vVolumeAspect, std::string& strTitle,
                            UVFTables::ElementSemanticTable& eType,
                            std::string& strIntermediateFile,
                            bool& bDeleteIntermediateFile)
{
  MESSAGE("Attempting to convert BOV: %s", strSourceFilename.c_str());
  KeyValueFileParser hdr(strSourceFilename.c_str());

  if(!hdr.FileReadable()) {
    T_ERROR("Could not parse %s; could not open.", strSourceFilename.c_str());
    return false;
  }
  KeyValPair *file = hdr.GetData("DATA_FILE");
  const KeyValPair *size = find_header(hdr, "DATA SIZE", "DATA_SIZE", NULL);
  KeyValPair *aspect_x = hdr.GetData("BRICK X_AXIS");
  KeyValPair *aspect_y = hdr.GetData("BRICK Y_AXIS");
  KeyValPair *aspect_z = hdr.GetData("BRICK Z_AXIS");
  strTitle = "BOV Volume";
  eType = UVFTables::ES_UNDEFINED;

  {
    iHeaderSkip = 0;
    strIntermediateFile = SysTools::CanonicalizePath(file->strValue);
    // Try the path the file gave first..
    if(!SysTools::FileExists(strIntermediateFile)) {
      // .. but if that didn't work, prepend the directory of the .bov file.
      strIntermediateFile = SysTools::CanonicalizePath(
                              SysTools::GetPath(strSourceFilename) +
                              file->strValue
                            );
      if(!SysTools::FileExists(strIntermediateFile)) {
        T_ERROR("Cannot find file (%s) referenced in BOV!",
                file->strValue.c_str());
      }
    }
    MESSAGE("Reading data from %s", strIntermediateFile.c_str());
    bDeleteIntermediateFile = false;
  }
  {
    std::istringstream ss(size->strValue);
    ss >> vVolumeSize[0] >> vVolumeSize[1] >> vVolumeSize[2];
    MESSAGE("Dimensions: %ux%ux%u",
            UINT32(vVolumeSize[0]), UINT32(vVolumeSize[1]), UINT32(vVolumeSize[2]));
    iHeaderSkip = 0;
  }
  {
    iComponentCount = 1;
    iComponentSize = 8;
    bConvertEndianness = false;
    bIsFloat = false;
    bSigned = false;

    switch(bov_type(hdr)) {
      case Char:
        iComponentSize = 8;
        bSigned = true;
        break;
      case Short:
        iComponentSize = 16;
        bSigned = true;
        break;
      case UShort: // extension!
        iComponentSize = 16;
        break;
      case Integer:
        iComponentSize = 32;
        bSigned = true;
        break;
      case Float:
        iComponentSize = 32;
        bSigned = true;
        bIsFloat = true;
        break;
      default:
        T_ERROR("Unknown BOV data type.");
        return false;
    }
    MESSAGE("%lu-bit %s, %s data", iComponentSize,
            bSigned ? "signed" : "unsigned",
            bIsFloat ? "floating point" : "integer");
  }
  {
    // e.g. "BRICK X_AXIS 1.000 0.000 0.000".  Might not exist.
    vVolumeAspect[0] = vVolumeAspect[1] = vVolumeAspect[2] = 1.0;
    if(aspect_x && aspect_y && aspect_z) {
      std::stringstream x(aspect_x->strValue);
      std::stringstream y(aspect_y->strValue);
      std::stringstream z(aspect_z->strValue);
      float junk;
      x >> vVolumeAspect[0];
      y >> junk >> vVolumeAspect[1];
      z >> junk >> junk >> vVolumeAspect[2];
    }
    MESSAGE("Aspect: %2.2fx%2.2fx%2.2f",
            vVolumeAspect[0], vVolumeAspect[1], vVolumeAspect[2]);
  }
  return true;
}

bool BOVConverter::ConvertToNative(const std::string& raw,
                                   const std::string& target,
                                   UINT64 skip, UINT64 component_size,
                                   UINT64 n_components, bool is_signed,
                                   bool fp, UINT64VECTOR3 dimensions,
                                   FLOATVECTOR3 aspect, bool batch,
                                   const bool bQuantizeTo8Bit)
{
  std::string fn_data = SysTools::RemoveExt(target);

  std::ofstream header(target.c_str(), std::ios::out | std::ios::trunc);

  std::string data_format;
  switch(component_size) {
    case 8: data_format = "BYTE"; break;
    case 16: data_format = "SHORT"; break;
    case 32:
      if(fp) { data_format = "FLOAT"; }
      else   { data_format = "INT"; }
      break;
    case 64:
      // In BOV, a 64bit integer dataset is a 2-component 32-bit integer
      // dataset.  As far as I can tell...
      if(fp) { data_format = "DOUBLE"; }
      else   { data_format = "INT"; }
      break;
  }
  header << "DATA_FILE: " << raw << ".data" << std::endl
         << "DATA SIZE: "
            << dimensions[0] << " " << dimensions[1] << " " << dimensions[2]
         << std::endl
         << "DATA FORMAT: " << data_format << std::endl
         << "DATA_COMPONENTS: " << n_components << std::endl
         << "VARIABLE: from_imagevis3d" << std::endl
         << "BRICK_SIZE: "
            << dimensions[0] << " " << dimensions[1] << " " << dimensions[2]
         << std::endl
         << "CENTERING: nodal" << std::endl;
  header.close();

  // copy the raw file.
  return RAWConverter::ConvertToNative(raw, raw + ".data", skip,
                                       component_size, n_components, is_signed,
                                       fp, dimensions, aspect, batch,
                                       bQuantizeTo8Bit);
}

static DataType
bov_type(const KeyValueFileParser &kvp)
{
  const KeyValPair *format = NULL;
  DataType retval = UnknownType;

  // Search a list of strings until we find one.
  static const char formats[][16] = {
    "DATA FORMAT", "DATA_FORMAT", "FORMAT"
  };
  for(size_t i=0; i < sizeof(formats); ++i) {
    format = kvp.GetData(formats[i]);
    if(format != NULL) { break; }
  }

  // Might not have found any of those.
  if(format == NULL) {
    T_ERROR("Could not determine data format.  "
            "Is this a BOV file?  Potentially corrupt.");
    return UnknownType;
  }

  // Data type is specified by strings, but the exact string isn't quite
  // uniform.  Another search.
  static const char floats[][16] = {
    "FLOAT", "FLOATS"
  };
  for(size_t i=0; i < sizeof(floats); ++i) {
    if(format->strValueUpper == std::string(floats[i])) {
      retval = Float;
      break;
    }
  }
  if(format->strValueUpper == std::string("SHORT")) {
    retval = Short;
  }
  if(format->strValueUpper == std::string("USHORT")) {
    retval = UShort;
  }
  if(format->strValueUpper == std::string("BYTE")) {
    retval = Char;
  }

  if(retval != Float && retval != Short && retval != Char) {
    WARNING("Unknown BOV data type '%s'", format->strValue.c_str());
  }
  return retval;
}

// Given a list of strings, returns the first KeyValPair that exists.  Useful
// since some bovs use "_" between words in a key, some don't...
// The argument list must be NULL-terminated.
static const KeyValPair *
find_header(const KeyValueFileParser &kvp, const char *s, ...)
{
  va_list args;
  const KeyValPair *ret;

  // First try 's'.
  ret = kvp.GetData(s);
  if(ret) { return ret; }

  va_start(args, s);
    const char *key = va_arg(args, const char *);
    while(key) {
      ret = kvp.GetData(key);
      if(ret) { return ret; }
    }
  va_end(args);
  return NULL;
}
<|endoftext|>


<commit_before>#include "txprocessor.h"

#include "rules.h"
#include "sigma.h"
#include "signaturebuilder.h"

#include "../base58.h"

namespace exodus {

TxProcessor *txProcessor;

int TxProcessor::ProcessTx(CMPTransaction& tx)
{
    LOCK(cs_main);

    tx.unlockLogic();

    auto result = tx.interpretPacket();

    if (result == (PKT_ERROR - 100)) {
        // Unknow transaction type.
        switch (tx.getType()) {
        case EXODUS_TYPE_SIMPLE_MINT:
            result = ProcessSimpleMint(tx);
            break;

        case EXODUS_TYPE_SIMPLE_SPEND:
            result = ProcessSimpleSpend(tx);
            break;
        }
    }

    if (result) {
        return result; // Error.
    }

    TransactionProcessed(tx);

    return 0;
}

int TxProcessor::ProcessSimpleMint(const CMPTransaction& tx)
{
    auto block = tx.getBlock();
    auto type = tx.getType();
    auto version = tx.getVersion();
    auto property = tx.getProperty();

    if (!IsTransactionTypeAllowed(block, property, type, version)) {
        PrintToLog("%s(): rejected: type %d or version %d not permitted for property %d at block %d\n",
            __func__,
            type,
            version,
            property,
            block
        );
        return PKT_ERROR_SIGMA - 22;
    }

    if (!IsPropertyIdValid(property)) {
        PrintToLog("%s(): rejected: property %d does not exist\n", __func__, property);
        return PKT_ERROR_SIGMA - 24;
    }

    if (!IsSigmaEnabled(property)) {
        PrintToLog("%s(): rejected: property %d does not enable sigma\n", __func__, property);
        return PKT_ERROR_SIGMA - 901;
    }

    std::vector<SigmaDenomination> denominations;
    denominations.reserve(tx.getMints().size());

    for (auto &mint : tx.getMints()) {
        auto denom = mint.first;
        auto& pubkey = mint.second;

        if (!pubkey.IsValid()) {
            PrintToLog("%s(): rejected: public key is invalid\n", __func__);
            return PKT_ERROR_SIGMA - 904;
        }

        denominations.push_back(denom);
    }

    int64_t amount;
    try {
        amount = SumDenominationsValue(property, denominations.begin(), denominations.end());
    } catch (std::invalid_argument const &e) {
        // The only possible cases is invalid denomination.
        PrintToLog("%s(): rejected: error %s\n", __func__, e.what());
        return PKT_ERROR_SIGMA - 905;
    } catch (std::overflow_error const &e) {
        PrintToLog("%s(): rejected: overflow error %s\n", __func__, e.what());
        return PKT_ERROR_SIGMA - 906;
    }

    auto& sender = tx.getSender();
    int64_t balance = getMPbalance(sender, property, BALANCE);

    if (balance < amount) {
        PrintToLog("%s(): rejected: sender %s has insufficient balance of property %d [%s < %s]\n",
            __func__,
            tx.getSender(),
            property,
            FormatMP(property, balance),
            FormatMP(property, amount)
        );
        return PKT_ERROR_SIGMA - 25;
    }

    // subtract balance
    assert(update_tally_map(sender, property, -amount, BALANCE));

    for (auto &mint : tx.getMints()) {
        SigmaMintGroup group;
        SigmaMintIndex index;

        auto denom = mint.first;
        auto& pubkey = mint.second;

        std::tie(group, index) = sigmaDb->RecordMint(property, denom, pubkey, block);

        SimpleMintProcessed(property, denom, group, index, pubkey);
    }

    return 0;
}

int TxProcessor::ProcessSimpleSpend(const CMPTransaction& tx)
{
    auto block = tx.getBlock();
    auto type = tx.getType();
    auto version = tx.getVersion();
    auto property = tx.getProperty();

    if (!IsTransactionTypeAllowed(block, property, type, version)) {
        PrintToLog("%s(): rejected: type %d or version %d not permitted for property %d at block %d\n",
            __func__,
            type,
            version,
            property,
            block);
        return PKT_ERROR_SIGMA - 22;
    }

    if (!IsPropertyIdValid(property)) {
        PrintToLog("%s(): rejected: property %d does not exist\n", __func__, property);
        return PKT_ERROR_SIGMA - 24;
    }

    if (!IsSigmaEnabled(property)) {
        PrintToLog("%s(): rejected: property %d does not enable sigma\n", __func__, property);
        return PKT_ERROR_SIGMA - 901;
    }

    auto spend = tx.getSpend();
    auto denomination = tx.getDenomination();
    auto group = tx.getGroup();
    auto groupSize = tx.getGroupSize();

    bool const fPadding = block >= ::Params().GetConsensus().nSigmaPaddingBlock;

    assert(spend);

    // check signature
    CBitcoinAddress receiver(tx.getReceiver());
    int64_t referenceAmount = tx.getReferenceAmount().value_or(0);
    auto &publicKey = tx.getSigmaECDSAPublicKey();

    SigmaV1SignatureBuilder sigBuilder(
        receiver,
        referenceAmount,
        *spend,
        publicKey);

    if (!sigBuilder.Verify(tx.getSigmaECDSASignature())) {
        PrintToLog("%s(): rejected: signature is invalid\n", __func__);
        return PKT_ERROR_SIGMA - 907;
    }

    // check serial in database
    uint256 spendTx;
    if (sigmaDb->HasSpendSerial(property, denomination, spend->serial, spendTx)
        || !VerifySigmaSpend(property, denomination, group, groupSize, *spend, fPadding)) {
        PrintToLog("%s(): rejected: spend is invalid\n", __func__);
        return PKT_ERROR_SIGMA - 907;
    }
    std::array<uint8_t, 1> denoms = {denomination};

    uint64_t amount;
    try {
        amount = SumDenominationsValue(property, denoms.begin(), denoms.end());
    } catch (std::invalid_argument const& e) {
        PrintToLog("%s(): rejected: error %s\n", __func__, e.what());
        return PKT_ERROR_SIGMA - 905;
    }

    // subtract balance
    CBitcoinAddress recvAddr(tx.getReceiver());
    if (!recvAddr.IsValid()) {
        PrintToLog("%s(): rejected: receiver address is invalid\n", __func__);
        return PKT_ERROR_SIGMA - 45;
    }

    sigmaDb->RecordSpendSerial(property, denomination, spend->serial, block, tx.getHash());
    assert(update_tally_map(tx.getReceiver(), property, amount, BALANCE));

    return 0;
}

}
<commit_msg>Add condition to check signature only if exodus message version is v1<commit_after>#include "txprocessor.h"

#include "rules.h"
#include "sigma.h"
#include "signaturebuilder.h"

#include "../base58.h"

namespace exodus {

TxProcessor *txProcessor;

int TxProcessor::ProcessTx(CMPTransaction& tx)
{
    LOCK(cs_main);

    tx.unlockLogic();

    auto result = tx.interpretPacket();

    if (result == (PKT_ERROR - 100)) {
        // Unknow transaction type.
        switch (tx.getType()) {
        case EXODUS_TYPE_SIMPLE_MINT:
            result = ProcessSimpleMint(tx);
            break;

        case EXODUS_TYPE_SIMPLE_SPEND:
            result = ProcessSimpleSpend(tx);
            break;
        }
    }

    if (result) {
        return result; // Error.
    }

    TransactionProcessed(tx);

    return 0;
}

int TxProcessor::ProcessSimpleMint(const CMPTransaction& tx)
{
    auto block = tx.getBlock();
    auto type = tx.getType();
    auto version = tx.getVersion();
    auto property = tx.getProperty();

    if (!IsTransactionTypeAllowed(block, property, type, version)) {
        PrintToLog("%s(): rejected: type %d or version %d not permitted for property %d at block %d\n",
            __func__,
            type,
            version,
            property,
            block
        );
        return PKT_ERROR_SIGMA - 22;
    }

    if (!IsPropertyIdValid(property)) {
        PrintToLog("%s(): rejected: property %d does not exist\n", __func__, property);
        return PKT_ERROR_SIGMA - 24;
    }

    if (!IsSigmaEnabled(property)) {
        PrintToLog("%s(): rejected: property %d does not enable sigma\n", __func__, property);
        return PKT_ERROR_SIGMA - 901;
    }

    std::vector<SigmaDenomination> denominations;
    denominations.reserve(tx.getMints().size());

    for (auto &mint : tx.getMints()) {
        auto denom = mint.first;
        auto& pubkey = mint.second;

        if (!pubkey.IsValid()) {
            PrintToLog("%s(): rejected: public key is invalid\n", __func__);
            return PKT_ERROR_SIGMA - 904;
        }

        denominations.push_back(denom);
    }

    int64_t amount;
    try {
        amount = SumDenominationsValue(property, denominations.begin(), denominations.end());
    } catch (std::invalid_argument const &e) {
        // The only possible cases is invalid denomination.
        PrintToLog("%s(): rejected: error %s\n", __func__, e.what());
        return PKT_ERROR_SIGMA - 905;
    } catch (std::overflow_error const &e) {
        PrintToLog("%s(): rejected: overflow error %s\n", __func__, e.what());
        return PKT_ERROR_SIGMA - 906;
    }

    auto& sender = tx.getSender();
    int64_t balance = getMPbalance(sender, property, BALANCE);

    if (balance < amount) {
        PrintToLog("%s(): rejected: sender %s has insufficient balance of property %d [%s < %s]\n",
            __func__,
            tx.getSender(),
            property,
            FormatMP(property, balance),
            FormatMP(property, amount)
        );
        return PKT_ERROR_SIGMA - 25;
    }

    // subtract balance
    assert(update_tally_map(sender, property, -amount, BALANCE));

    for (auto &mint : tx.getMints()) {
        SigmaMintGroup group;
        SigmaMintIndex index;

        auto denom = mint.first;
        auto& pubkey = mint.second;

        std::tie(group, index) = sigmaDb->RecordMint(property, denom, pubkey, block);

        SimpleMintProcessed(property, denom, group, index, pubkey);
    }

    return 0;
}

int TxProcessor::ProcessSimpleSpend(const CMPTransaction& tx)
{
    auto block = tx.getBlock();
    auto type = tx.getType();
    auto version = tx.getVersion();
    auto property = tx.getProperty();

    if (!IsTransactionTypeAllowed(block, property, type, version)) {
        PrintToLog("%s(): rejected: type %d or version %d not permitted for property %d at block %d\n",
            __func__,
            type,
            version,
            property,
            block);
        return PKT_ERROR_SIGMA - 22;
    }

    if (!IsPropertyIdValid(property)) {
        PrintToLog("%s(): rejected: property %d does not exist\n", __func__, property);
        return PKT_ERROR_SIGMA - 24;
    }

    if (!IsSigmaEnabled(property)) {
        PrintToLog("%s(): rejected: property %d does not enable sigma\n", __func__, property);
        return PKT_ERROR_SIGMA - 901;
    }

    auto spend = tx.getSpend();
    auto denomination = tx.getDenomination();
    auto group = tx.getGroup();
    auto groupSize = tx.getGroupSize();

    bool const fPadding = block >= ::Params().GetConsensus().nSigmaPaddingBlock;

    assert(spend);

    // check signature
    if (version == MP_TX_PKT_V1) {
        CBitcoinAddress receiver(tx.getReceiver());
        int64_t referenceAmount = tx.getReferenceAmount().value_or(0);
        auto &publicKey = tx.getSigmaECDSAPublicKey();

        SigmaV1SignatureBuilder sigBuilder(
            receiver,
            referenceAmount,
            *spend,
            publicKey);

        if (!sigBuilder.Verify(tx.getSigmaECDSASignature())) {
            PrintToLog("%s(): rejected: signature is invalid\n", __func__);
            return PKT_ERROR_SIGMA - 907;
        }
    }

    // check serial in database
    uint256 spendTx;
    if (sigmaDb->HasSpendSerial(property, denomination, spend->serial, spendTx)
        || !VerifySigmaSpend(property, denomination, group, groupSize, *spend, fPadding)) {
        PrintToLog("%s(): rejected: spend is invalid\n", __func__);
        return PKT_ERROR_SIGMA - 907;
    }
    std::array<uint8_t, 1> denoms = {denomination};

    uint64_t amount;
    try {
        amount = SumDenominationsValue(property, denoms.begin(), denoms.end());
    } catch (std::invalid_argument const& e) {
        PrintToLog("%s(): rejected: error %s\n", __func__, e.what());
        return PKT_ERROR_SIGMA - 905;
    }

    // subtract balance
    CBitcoinAddress recvAddr(tx.getReceiver());
    if (!recvAddr.IsValid()) {
        PrintToLog("%s(): rejected: receiver address is invalid\n", __func__);
        return PKT_ERROR_SIGMA - 45;
    }

    sigmaDb->RecordSpendSerial(property, denomination, spend->serial, block, tx.getHash());
    assert(update_tally_map(tx.getReceiver(), property, amount, BALANCE));

    return 0;
}

}
<|endoftext|>


<commit_before>#include "server_universe.hpp"
#include "server_universe_serialize.hpp"
#include "server_space_object.hpp"
#include "server_state_game.hpp"
#include <std_addon.hpp>
#include <string.hpp>
#include <filesystem.hpp>
#include <fstream>

namespace server {
    universe::universe() : object_factory_(std::make_unique<space_object_factory>()) {
        // Add object factories?
    }

    universe::~universe() {}

    bool universe::create_space(std::size_t size) {
        // Ugly but...
        switch (size) {
            case  0 : return true;
            case  2 : space_ = space_universe::make< 2>(); return true;
            case  3 : space_ = space_universe::make< 3>(); return true;
            case  4 : space_ = space_universe::make< 4>(); return true;
            case  5 : space_ = space_universe::make< 5>(); return true;
            case  6 : space_ = space_universe::make< 6>(); return true;
            case  7 : space_ = space_universe::make< 7>(); return true;
            case  8 : space_ = space_universe::make< 8>(); return true;
            case  9 : space_ = space_universe::make< 9>(); return true;
            case 10 : space_ = space_universe::make<10>(); return true;
            case 11 : space_ = space_universe::make<11>(); return true;
            case 12 : space_ = space_universe::make<12>(); return true;
            case 13 : space_ = space_universe::make<13>(); return true;
            case 14 : space_ = space_universe::make<14>(); return true;
            case 15 : space_ = space_universe::make<15>(); return true;
            case 16 : space_ = space_universe::make<16>(); return true;
            default : return false;
        }
    }

    /// Serialization

    static const std::string master_file_name = "universe.csf";

    struct universe_serializer_internal_buffer : v1::universe_serializer_internal_buffer {};

    std::unique_ptr<universe_serializer> universe::make_serializer() {
        return std::make_unique<universe_serializer>(*this);
    }

    universe_serializer::universe_serializer(universe& uni) :
        server::serializable("serializer_universe"), universe_(uni) {}

    universe_serializer::~universe_serializer() {}

    // NB: this function must always use the latest format
    void universe_serializer::save_data() {
        buffer_ = std::make_unique<universe_serializer_internal_buffer>();

        if (!universe_.space_) {
            return;
        }

        // Basic info
        buffer_->header.depth = universe_.space_->depth();

        // Now explore the whole space to find objects and serialize them
        // First find out how many objects we have to pre-allocate enough memory
        universe_.space_->for_each_cell([&](const space_object&) {
            ++buffer_->header.nobject;
        });

        buffer_->objects.reserve(buffer_->header.nobject);

        // Now serialize each object
        universe_.space_->for_each_cell([&](const space_object& obj) {
            buffer_->objects.push_back(serialized_packet{});
            auto& sp = buffer_->objects.back();
            sp << obj.id();
            if (obj.cell()) {
                sp << obj.cell()->get_coordinates();
            } else {
                sp << space::vec_t();
            }

            sp << obj.type();

            obj.serialize(sp);
        });
    }

    // NB: this function must always use the latest format
    void universe_serializer::serialize(const std::string& dir) const {
        if (!buffer_) {
            throw std::runtime_error("cannot serialize if save_data() has not been called");
        }

        // Write header
        std::ofstream file(dir+master_file_name, std::ios::binary);

        serialized_packet tp;
        tp << buffer_->header.header << buffer_->header.depth << buffer_->header.nobject;
        file << tp;

        // Write objects
        for (auto& so : buffer_->objects) {
            file << so;
        }
    }

    void universe_serializer::deserialize(const std::string& dir) {
        buffer_ = std::make_unique<universe_serializer_internal_buffer>();

        // Read file
        std::ifstream file(dir+master_file_name, std::ios::binary);

        serialized_packet phdr;
        file >> phdr;
        if (!(phdr >> buffer_->header.header)) {
            throw request::server::game_load::failure{
                request::server::game_load::failure::reason::invalid_saved_game,
                "could not read save format version"
            };
        }

        if (buffer_->header.header == v1::version_header) {
            if (!(phdr >> buffer_->header.depth)) {
                throw request::server::game_load::failure{
                    request::server::game_load::failure::reason::invalid_saved_game,
                    "could not read universe size"
                };
            };

            if (!(phdr >> buffer_->header.nobject)) {
                throw request::server::game_load::failure{
                    request::server::game_load::failure::reason::invalid_saved_game,
                    "could not read number of objects in universe"
                };
            };

            buffer_->objects.resize(buffer_->header.nobject);
            for (auto& so : buffer_->objects) {
                file >> so;
            }
        } else {
            throw request::server::game_load::failure{
                request::server::game_load::failure::reason::invalid_saved_game,
                "unsupported save file version"
            };
        }
    }

    // NB: this function must always use the latest format
    void universe_serializer::load_data_first_pass() {
        if (!universe_.create_space(buffer_->header.depth)) {
            throw request::server::game_load::failure{
                request::server::game_load::failure::reason::invalid_saved_game,
                "the depth of the universe must be comprised between 2 and 16"
            };
        }

        // Create objects
        std::size_t i = 0;
        for (auto& so : buffer_->objects) {
            ++i;
            uuid_t id;
            space::vec_t pos;
            std::uint16_t type;

            if (!(so >> id >> pos >> type)) {
                throw request::server::game_load::failure{
                    request::server::game_load::failure::reason::invalid_saved_game,
                    "could not read generic object properties for object "+
                    string::convert(i)+"/"+string::convert(buffer_->objects.size())
                };
            }

            space_cell* cell = universe_.space_->try_reach(pos);
            if (!cell) {
                throw request::server::game_load::failure{
                    request::server::game_load::failure::reason::invalid_saved_game,
                    "invalid position for object "+string::convert(id)+
                    " ("+string::convert(pos)+"), out of universe"
                };
            } else if (!cell->empty()) {
                throw request::server::game_load::failure{
                    request::server::game_load::failure::reason::invalid_saved_game,
                    "invalid position for object "+string::convert(id)+
                    " ("+string::convert(pos)+"), object "+
                    string::convert(cell->content().id())+" is already there"
                };
            }

            std::unique_ptr<space_object> ptr = universe_.object_factory_->make(type, id);
            if (!ptr) {
                throw request::server::game_load::failure{
                    request::server::game_load::failure::reason::invalid_saved_game,
                    "invalid object type code for object "+string::convert(id)+
                    " ("+string::convert(type)+")"
                };
            }

            ptr->deserialize(so);
            cell->fill(std::move(ptr));
        }
    }

    // NB: this function must always use the latest format
    void universe_serializer::load_data_second_pass() {};

    void universe_serializer::clear() {
        buffer_ = nullptr;
    };

    bool universe_serializer::is_valid_directory(const std::string& dir) const {
        return file::exists(dir+master_file_name);
    }
}
<commit_msg>Fixed possible serializing of uninitialized value<commit_after>#include "server_universe.hpp"
#include "server_universe_serialize.hpp"
#include "server_space_object.hpp"
#include "server_state_game.hpp"
#include <std_addon.hpp>
#include <string.hpp>
#include <filesystem.hpp>
#include <fstream>

namespace server {
    universe::universe() : object_factory_(std::make_unique<space_object_factory>()) {
        // Add object factories?
    }

    universe::~universe() {}

    bool universe::create_space(std::size_t size) {
        // Ugly but...
        switch (size) {
            case  0 : return true;
            case  2 : space_ = space_universe::make< 2>(); return true;
            case  3 : space_ = space_universe::make< 3>(); return true;
            case  4 : space_ = space_universe::make< 4>(); return true;
            case  5 : space_ = space_universe::make< 5>(); return true;
            case  6 : space_ = space_universe::make< 6>(); return true;
            case  7 : space_ = space_universe::make< 7>(); return true;
            case  8 : space_ = space_universe::make< 8>(); return true;
            case  9 : space_ = space_universe::make< 9>(); return true;
            case 10 : space_ = space_universe::make<10>(); return true;
            case 11 : space_ = space_universe::make<11>(); return true;
            case 12 : space_ = space_universe::make<12>(); return true;
            case 13 : space_ = space_universe::make<13>(); return true;
            case 14 : space_ = space_universe::make<14>(); return true;
            case 15 : space_ = space_universe::make<15>(); return true;
            case 16 : space_ = space_universe::make<16>(); return true;
            default : return false;
        }
    }

    /// Serialization

    static const std::string master_file_name = "universe.csf";

    struct universe_serializer_internal_buffer : v1::universe_serializer_internal_buffer {};

    std::unique_ptr<universe_serializer> universe::make_serializer() {
        return std::make_unique<universe_serializer>(*this);
    }

    universe_serializer::universe_serializer(universe& uni) :
        server::serializable("serializer_universe"), universe_(uni) {}

    universe_serializer::~universe_serializer() {}

    // NB: this function must always use the latest format
    void universe_serializer::save_data() {
        buffer_ = std::make_unique<universe_serializer_internal_buffer>();

        if (!universe_.space_) {
            return;
        }

        // Basic info
        buffer_->header.depth = universe_.space_->depth();

        // Now explore the whole space to find objects and serialize them
        // First find out how many objects we have to pre-allocate enough memory
        universe_.space_->for_each_cell([&](const space_object&) {
            ++buffer_->header.nobject;
        });

        buffer_->objects.reserve(buffer_->header.nobject);

        // Now serialize each object
        universe_.space_->for_each_cell([&](const space_object& obj) {
            buffer_->objects.push_back(serialized_packet{});
            auto& sp = buffer_->objects.back();
            sp << obj.id();
            if (obj.cell()) {
                sp << obj.cell()->get_coordinates();
            } else {
                sp << space::vec_t::zero;
            }

            sp << obj.type();

            obj.serialize(sp);
        });
    }

    // NB: this function must always use the latest format
    void universe_serializer::serialize(const std::string& dir) const {
        if (!buffer_) {
            throw std::runtime_error("cannot serialize if save_data() has not been called");
        }

        // Write header
        std::ofstream file(dir+master_file_name, std::ios::binary);

        serialized_packet tp;
        tp << buffer_->header.header << buffer_->header.depth << buffer_->header.nobject;
        file << tp;

        // Write objects
        for (auto& so : buffer_->objects) {
            file << so;
        }
    }

    void universe_serializer::deserialize(const std::string& dir) {
        buffer_ = std::make_unique<universe_serializer_internal_buffer>();

        // Read file
        std::ifstream file(dir+master_file_name, std::ios::binary);

        serialized_packet phdr;
        file >> phdr;
        if (!(phdr >> buffer_->header.header)) {
            throw request::server::game_load::failure{
                request::server::game_load::failure::reason::invalid_saved_game,
                "could not read save format version"
            };
        }

        if (buffer_->header.header == v1::version_header) {
            if (!(phdr >> buffer_->header.depth)) {
                throw request::server::game_load::failure{
                    request::server::game_load::failure::reason::invalid_saved_game,
                    "could not read universe size"
                };
            };

            if (!(phdr >> buffer_->header.nobject)) {
                throw request::server::game_load::failure{
                    request::server::game_load::failure::reason::invalid_saved_game,
                    "could not read number of objects in universe"
                };
            };

            buffer_->objects.resize(buffer_->header.nobject);
            for (auto& so : buffer_->objects) {
                file >> so;
            }
        } else {
            throw request::server::game_load::failure{
                request::server::game_load::failure::reason::invalid_saved_game,
                "unsupported save file version"
            };
        }
    }

    // NB: this function must always use the latest format
    void universe_serializer::load_data_first_pass() {
        if (!universe_.create_space(buffer_->header.depth)) {
            throw request::server::game_load::failure{
                request::server::game_load::failure::reason::invalid_saved_game,
                "the depth of the universe must be comprised between 2 and 16"
            };
        }

        // Create objects
        std::size_t i = 0;
        for (auto& so : buffer_->objects) {
            ++i;
            uuid_t id;
            space::vec_t pos;
            std::uint16_t type;

            if (!(so >> id >> pos >> type)) {
                throw request::server::game_load::failure{
                    request::server::game_load::failure::reason::invalid_saved_game,
                    "could not read generic object properties for object "+
                    string::convert(i)+"/"+string::convert(buffer_->objects.size())
                };
            }

            space_cell* cell = universe_.space_->try_reach(pos);
            if (!cell) {
                throw request::server::game_load::failure{
                    request::server::game_load::failure::reason::invalid_saved_game,
                    "invalid position for object "+string::convert(id)+
                    " ("+string::convert(pos)+"), out of universe"
                };
            } else if (!cell->empty()) {
                throw request::server::game_load::failure{
                    request::server::game_load::failure::reason::invalid_saved_game,
                    "invalid position for object "+string::convert(id)+
                    " ("+string::convert(pos)+"), object "+
                    string::convert(cell->content().id())+" is already there"
                };
            }

            std::unique_ptr<space_object> ptr = universe_.object_factory_->make(type, id);
            if (!ptr) {
                throw request::server::game_load::failure{
                    request::server::game_load::failure::reason::invalid_saved_game,
                    "invalid object type code for object "+string::convert(id)+
                    " ("+string::convert(type)+")"
                };
            }

            ptr->deserialize(so);
            cell->fill(std::move(ptr));
        }
    }

    // NB: this function must always use the latest format
    void universe_serializer::load_data_second_pass() {};

    void universe_serializer::clear() {
        buffer_ = nullptr;
    };

    bool universe_serializer::is_valid_directory(const std::string& dir) const {
        return file::exists(dir+master_file_name);
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2016+     AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-GPL2
 * Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
 * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
 */

/* ScriptData
SDName: Tirisfal_Glades
SD%Complete: 100
SDComment: Quest support: 590, 1819
SDCategory: Tirisfal Glades
EndScriptData */

/* ContentData
npc_calvin_montague
go_mausoleum_door
go_mausoleum_trigger
EndContentData */

#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "Player.h"

/*######
## npc_calvin_montague
######*/

enum Calvin
{
    SAY_COMPLETE        = 0,
    SPELL_DRINK         = 2639,                             // possibly not correct spell (but iconId is correct)
    QUEST_590           = 590,
    FACTION_HOSTILE     = 168
};

class npc_calvin_montague : public CreatureScript
{
public:
    npc_calvin_montague() : CreatureScript("npc_calvin_montague") { }

    bool OnQuestAccept(Player* player, Creature* creature, Quest const* quest)
    {
        if (quest->GetQuestId() == QUEST_590)
        {
            creature->setFaction(FACTION_HOSTILE);
            creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
            CAST_AI(npc_calvin_montague::npc_calvin_montagueAI, creature->AI())->AttackStart(player);
        }
        return true;
    }

    CreatureAI* GetAI(Creature* creature) const
    {
        return new npc_calvin_montagueAI(creature);
    }

    struct npc_calvin_montagueAI : public ScriptedAI
    {
        npc_calvin_montagueAI(Creature* creature) : ScriptedAI(creature) { }

        uint32 m_uiPhase;
        uint32 m_uiPhaseTimer;
        uint64 m_uiPlayerGUID;

        void Reset()
        {
            m_uiPhase = 0;
            m_uiPhaseTimer = 5000;
            m_uiPlayerGUID = 0;

            me->RestoreFaction();

            if (!me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC))
                me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
        }

        void EnterCombat(Unit* /*who*/) { }

        void AttackedBy(Unit* pAttacker)
        {
            if (me->GetVictim() || me->IsFriendlyTo(pAttacker))
                return;

            AttackStart(pAttacker);
        }

        void DamageTaken(Unit* pDoneBy, uint32 &uiDamage, DamageEffectType, SpellSchoolMask)
        {
            if (!pDoneBy)
                return;

            if (uiDamage >= me->GetHealth() || me->HealthBelowPctDamaged(15, uiDamage))
            {
                uiDamage = 0;

                me->RestoreFaction();
                me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
                me->CombatStop(true);

                m_uiPhase = 1;

                if (pDoneBy->GetTypeId() == TYPEID_PLAYER)
                    m_uiPlayerGUID = pDoneBy->GetGUID();
            }
        }

        void UpdateAI(uint32 diff)
        {
            if (m_uiPhase)
            {
                if (m_uiPhaseTimer <= diff)
                    m_uiPhaseTimer = 7500;
                else
                {
                    m_uiPhaseTimer -= diff;
                    return;
                }

                switch (m_uiPhase)
                {
                    case 1:
                        Talk(SAY_COMPLETE);
                        ++m_uiPhase;
                        break;
                    case 2:
                        if (Player* player = ObjectAccessor::GetPlayer(*me, m_uiPlayerGUID))
                            player->AreaExploredOrEventHappens(QUEST_590);

                        DoCast(me, SPELL_DRINK, true);
                        ++m_uiPhase;
                        break;
                    case 3:
                        EnterEvadeMode();
                        break;
                }

                return;
            }

            if (!UpdateVictim())
                return;

            DoMeleeAttackIfReady();
        }
    };
};

/*######
## go_mausoleum_door
## go_mausoleum_trigger
######*/

enum Mausoleum
{
    QUEST_ULAG      = 1819,
    NPC_ULAG        = 6390,
    GO_TRIGGER      = 104593,
    GO_DOOR         = 176594
};

class go_mausoleum_door : public GameObjectScript
{
public:
    go_mausoleum_door() : GameObjectScript("go_mausoleum_door") { }

    bool OnGossipHello(Player* player, GameObject* /*go*/)
    {
        if (player->GetQuestStatus(QUEST_ULAG) != QUEST_STATUS_INCOMPLETE)
            return false;

        if (!player->FindNearestCreature(NPC_ULAG, 50.0f))
            if (GameObject* pTrigger = player->FindNearestGameObject(GO_TRIGGER, 30.0f))
            {
                pTrigger->SetGoState(GO_STATE_READY);
                player->SummonCreature(NPC_ULAG, 2390.26f, 336.47f, 40.01f, 2.26f, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 300000);
                return false;
            }

        return false;
    }
};

class go_mausoleum_trigger : public GameObjectScript
{
public:
    go_mausoleum_trigger() : GameObjectScript("go_mausoleum_trigger") { }

    bool OnGossipHello(Player* player, GameObject* go)
    {
        if (player->GetQuestStatus(QUEST_ULAG) != QUEST_STATUS_INCOMPLETE)
            return false;

        if (GameObject* pDoor = player->FindNearestGameObject(GO_DOOR, 30.0f))
        {
            go->SetGoState(GO_STATE_ACTIVE);
            pDoor->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_INTERACT_COND);
            return true;
        }

        return false;
    }
};

void AddSC_tirisfal_glades()
{
    new npc_calvin_montague();
    new go_mausoleum_door();
    new go_mausoleum_trigger();
}
<commit_msg>Update zone_tirisfal_glades.cpp<commit_after>/*
 * Copyright (C) 2016+     AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-GPL2
 * Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
 * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
 */

/* ScriptData
SDName: Tirisfal_Glades
SD%Complete: 100
SDComment: Quest support: 590, 1819
SDCategory: Tirisfal Glades
EndScriptData */

/* ContentData
npc_calvin_montague
go_mausoleum_door
go_mausoleum_trigger
EndContentData */

#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "Player.h"

/*######
## npc_calvin_montague
######*/

enum Calvin
{
    SAY_COMPLETE        = 0,
    SPELL_DRINK         = 2639,                             // possibly not correct spell (but iconId is correct)
    QUEST_590           = 590,
    FACTION_HOSTILE     = 168
};

class npc_calvin_montague : public CreatureScript
{
public:
    npc_calvin_montague() : CreatureScript("npc_calvin_montague") { }

    bool OnQuestAccept(Player* player, Creature* creature, Quest const* quest)
    {
        if (quest->GetQuestId() == QUEST_590)
        {
            creature->setFaction(FACTION_HOSTILE);
            creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
            CAST_AI(npc_calvin_montague::npc_calvin_montagueAI, creature->AI())->AttackStart(player);
        }
        return true;
    }

    CreatureAI* GetAI(Creature* creature) const
    {
        return new npc_calvin_montagueAI(creature);
    }

    struct npc_calvin_montagueAI : public ScriptedAI
    {
        npc_calvin_montagueAI(Creature* creature) : ScriptedAI(creature) { }

        uint32 m_uiPhase;
        uint32 m_uiPhaseTimer;
        uint64 m_uiPlayerGUID;

        void Reset()
        {
            m_uiPhase = 0;
            m_uiPhaseTimer = 5000;
            m_uiPlayerGUID = 0;

            me->RestoreFaction();

            if (!me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC))
                me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
        }

        void EnterCombat(Unit* /*who*/) { }

        void AttackedBy(Unit* pAttacker)
        {
            if (me->GetVictim() || me->IsFriendlyTo(pAttacker))
                return;

            AttackStart(pAttacker);
        }

        void DamageTaken(Unit* pDoneBy, uint32 &uiDamage, DamageEffectType, SpellSchoolMask)
        {
            if (!pDoneBy)
                return;

            if (uiDamage >= me->GetHealth() || me->HealthBelowPctDamaged(15, uiDamage))
            {
                uiDamage = 0;

                me->RestoreFaction();
                me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
                me->CombatStop(true);

                m_uiPhase = 1;

                if (pDoneBy->GetTypeId() == TYPEID_PLAYER)
                    m_uiPlayerGUID = pDoneBy->GetGUID();
            }
        }

        void UpdateAI(uint32 diff)
        {
            if (m_uiPhase)
            {
                if (m_uiPhaseTimer <= diff)
                    m_uiPhaseTimer = 7500;
                else
                {
                    m_uiPhaseTimer -= diff;
                    return;
                }

                switch (m_uiPhase)
                {
                    case 1:
                        Talk(SAY_COMPLETE);
                        ++m_uiPhase;
                        break;
                    case 2:
                        if (Player* player = ObjectAccessor::GetPlayer(*me, m_uiPlayerGUID))
                            player->AreaExploredOrEventHappens(QUEST_590);

                        DoCast(me, SPELL_DRINK, true);
                        ++m_uiPhase;
                        break;
                    case 3:
                        EnterEvadeMode();
                        break;
                }

                return;
            }

            if (!UpdateVictim())
                return;

            DoMeleeAttackIfReady();
        }
    };
};

void AddSC_tirisfal_glades()
{
    new npc_calvin_montague();
}
<|endoftext|>


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

namespace sqlite3cpp {
namespace detail {

template <int>
struct placeholder_tmpl {};

template <int... Ints>
using indexes = std::integer_sequence<int, Ints...>;

template <int Max>
using make_indexes_t = std::make_integer_sequence<int, Max>;
}  // namespace detail
}  // namespace sqlite3cpp

// custome placeholders
namespace std {
template <int N>
struct is_placeholder<sqlite3cpp::detail::placeholder_tmpl<N>>
    : integral_constant<int, N + 1> {};
}  // namespace std

namespace sqlite3cpp {
namespace detail {

/**
 * tuple enumerate
 */
template <class F, class Tuple, int... I>
constexpr decltype(auto) enumerate_impl(F &&f, Tuple &&t,
                                        std::integer_sequence<int, I...>) {
  return (
      std::invoke(std::forward<F>(f), I, std::get<I>(std::forward<Tuple>(t))),
      ...);
}

template <class F, class Tuple>
constexpr decltype(auto) enumerate(F &&f, Tuple &&t) {
  return detail::enumerate_impl(
      std::forward<F>(f), std::forward<Tuple>(t),
      std::make_integer_sequence<
          int, std::tuple_size_v<std::remove_reference_t<Tuple>>>{});
}

/**
 * Helpers for retrieve column values.
 */
inline void get_col_val_aux(sqlite3_stmt *stmt, sqlite3 *, int index,
                            int &val) {
  val = sqlite3_column_int(stmt, index);
}

inline void get_col_val_aux(sqlite3_stmt *stmt, sqlite3 *, int index,
                            int64_t &val) {
  val = sqlite3_column_int64(stmt, index);
}

inline void get_col_val_aux(sqlite3_stmt *stmt, sqlite3 *, int index,
                            double &val) {
  val = sqlite3_column_double(stmt, index);
}

inline void get_col_val_aux(sqlite3_stmt *stmt, sqlite3 *db, int index,
                            std::string &val) {
  char const *res = (char const *)sqlite3_column_text(stmt, index);
  if (!res) {
    int ec = sqlite3_errcode(db);
    if (ec != SQLITE_OK) throw error(ec);
  }
  val.assign(res, sqlite3_column_bytes(stmt, index));
}

inline void get_col_val_aux(sqlite3_stmt *stmt, sqlite3 *db, int index,
                            std::string_view &val) {
  char const *res = (char const *)sqlite3_column_text(stmt, index);
  if (!res) {
    int ec = sqlite3_errcode(db);
    if (ec != SQLITE_OK) throw error(ec);
  }
  val = std::string_view{
      (char const *)sqlite3_column_text(stmt, index),
      (std::string_view::size_type)sqlite3_column_bytes(stmt, index)};
}

inline void get_col_val_aux(sqlite3_stmt *stmt, sqlite3 *db, int index,
                            std::optional<std::string> &val) {
  char const *res = (char const *)sqlite3_column_text(stmt, index);
  if (!res) {
    int ec = sqlite3_errcode(db);
    if (ec != SQLITE_OK) val.reset();
  }
  val = std::string{res,
                    (std::string::size_type)sqlite3_column_bytes(stmt, index)};
}

inline void get_col_val_aux(sqlite3_stmt *stmt, sqlite3 *db, int index,
                            std::optional<std::string_view> &val) {
  char const *res = (char const *)sqlite3_column_text(stmt, index);
  if (!res) {
    int ec = sqlite3_errcode(db);
    if (ec != SQLITE_OK) val.reset();
  }
  val = std::string_view{
      res, (std::string_view::size_type)sqlite3_column_bytes(stmt, index)};
}

/*
 * Helpers for binding values to sqlite3_stmt.
 */
inline void bind_to_stmt(sqlite3_stmt *stmt, int i) {}

inline int bind_val(sqlite3_stmt *stmt, int index, int val) {
  return sqlite3_bind_int(stmt, index, val);
}

inline int bind_val(sqlite3_stmt *stmt, int index, double val) {
  return sqlite3_bind_double(stmt, index, val);
}

inline int bind_val(sqlite3_stmt *stmt, int index, std::string const &val) {
  return sqlite3_bind_text(stmt, index, val.c_str(), val.size(), SQLITE_STATIC);
}

inline int bind_val(sqlite3_stmt *stmt, int index, std::string_view val) {
  return sqlite3_bind_text(stmt, index, val.data(), val.size(), SQLITE_STATIC);
}

inline int bind_val(sqlite3_stmt *stmt, int index, char const *val) {
  return sqlite3_bind_text(stmt, index, val, -1, SQLITE_STATIC);
}

inline int bind_val(sqlite3_stmt *stmt, int index, std::nullptr_t _) {
  return sqlite3_bind_null(stmt, index);
}

template <typename T, typename... Args>
void bind_to_stmt(sqlite3_stmt *stmt, int index, T &&val, Args &&... args) {
  int ec = 0;
  if (0 != (ec = bind_val(stmt, index, std::forward<T>(val)))) throw error(ec);
  bind_to_stmt(stmt, index + 1, std::forward<Args>(args)...);
}

/**
 * Helpers for converting value from sqlite3_value.
 */
template <typename T>
struct Type {};

inline int get(Type<int>, sqlite3_value **v, int const index) {
  return sqlite3_value_int(v[index]);
}

inline int64_t get(Type<int64_t>, sqlite3_value **v, int const index) {
  return sqlite3_value_int64(v[index]);
}

inline double get(Type<double>, sqlite3_value **v, int const index) {
  return sqlite3_value_double(v[index]);
}

inline std::string get(Type<std::string>, sqlite3_value **v, int const index) {
  return std::string((char const *)sqlite3_value_text(v[index]),
                     (size_t)sqlite3_value_bytes(v[index]));
}

inline std::string_view get(Type<std::string_view>, sqlite3_value **v,
                            int const index) {
  return std::string_view((char const *)sqlite3_value_text(v[index]),
                          (size_t)sqlite3_value_bytes(v[index]));
}

/**
 * Helpers for setting result of scalar functions.
 */
inline void result(int val, sqlite3_context *ctx) {
  sqlite3_result_int(ctx, val);
}

inline void result(int64_t val, sqlite3_context *ctx) {
  sqlite3_result_int64(ctx, val);
}

inline void result(double val, sqlite3_context *ctx) {
  sqlite3_result_double(ctx, val);
}

inline void result(std::string const &val, sqlite3_context *ctx) {
  sqlite3_result_text(ctx, val.data(), val.size(), SQLITE_TRANSIENT);
}

/**
 * Magic for typesafe invoking lambda/std::function from sqlite3
 * (registered via sqlite3_create_function).
 */
template <typename R, typename... Args, int... Is>
R invoke(std::function<R(Args...)> func, int argc, sqlite3_value **argv,
         indexes<Is...>) {
  // TODO Check argc
  // Expand argv per index
  return func(get(Type<Args>{}, argv, Is)...);
}

template <typename R, typename... Args>
R invoke(std::function<R(Args...)> func, int argc, sqlite3_value **argv) {
  return invoke(func, argc, argv, make_indexes_t<sizeof...(Args)>{});
}

template <typename R, typename... Args>
auto make_invoker(std::function<R(Args...)> &&func) {
  if constexpr (std::is_void_v<R>) {
    return [func](sqlite3_context *ctx, int argc, sqlite3_value **argv) {
      invoke(func, argc, argv);
    };
  } else {
    return [func](sqlite3_context *ctx, int argc, sqlite3_value **argv) {
      result(invoke(func, argc, argv), ctx);
    };
  }
}

/**
 * Function traits for supporting lambda.
 */

// For generic types that are functors, delegate to its 'operator()'
template <typename T>
struct function_traits : public function_traits<decltype(&T::operator())> {};

// for pointers to member function
template <typename C, typename R, typename... Args>
struct function_traits<R (C::*)(Args...)> {
  typedef std::function<R(Args...)> f_type;
  static const size_t arity = sizeof...(Args);
};

// for pointers to const member function
template <typename C, typename R, typename... Args>
struct function_traits<R (C::*)(Args...) const> {
  typedef std::function<R(Args...)> f_type;
  static const size_t arity = sizeof...(Args);
};

// for function pointers
template <typename R, typename... Args>
struct function_traits<R (*)(Args...)> {
  typedef std::function<R(Args...)> f_type;
  static const size_t arity = sizeof...(Args);
};

/**
 * Member function binder helpers (auto expand by passed function prototype)
 */
template <typename F, typename C, int... Is>
typename function_traits<F>::f_type bind_this(F f, C *this_, indexes<Is...>) {
  return std::bind(f, this_, placeholder_tmpl<Is>{}...);
}

template <typename F, typename C>
typename function_traits<F>::f_type bind_this(F f, C *this_) {
  using traits = function_traits<F>;
  return bind_this(f, this_, make_indexes_t<traits::arity>{});
}
}  // namespace detail
}  // namespace sqlite3cpp

namespace sqlite3cpp {

/**
 * row impl
 */
template <typename... Cols>
std::tuple<Cols...> row::to() const {
  std::tuple<Cols...> result;
  detail::enumerate(
      [this](int index, auto &&tuple_value) {
        detail::get_col_val_aux(m_stmt, m_db, index, tuple_value);
      },
      result);
  return result;
}

/**
 * cursor impl
 */
template <typename... Args>
cursor &cursor::execute(std::string const &sql, Args &&... args) {
  // TODO: Support rebind params
  sqlite3_stmt *stmt = 0;
  int ec = 0;

  if (0 != (ec = sqlite3_prepare_v2(m_db, sql.c_str(), sql.size(), &stmt, 0)))
    throw error(ec);

  m_stmt.reset(stmt);
  detail::bind_to_stmt(m_stmt.get(), 1, std::forward<Args>(args)...);
  step();
  return *this;
}

/**
 * database impl
 */

template <typename... Args>
cursor database::execute(std::string const &sql, Args &&... args) {
  cursor c = make_cursor();
  c.execute(sql, std::forward<Args>(args)...);
  return c;
}

template <typename FUNC>
void database::create_scalar(std::string const &name, FUNC func, int flags) {
  using traits = detail::function_traits<FUNC>;

  auto xfunc_ptr =
      std::make_unique<xfunc_t>(detail::make_invoker(typename traits::f_type(func)));

  int ec = 0;
  if (0 != (ec = sqlite3_create_function_v2(
                m_db.get(), name.c_str(), (int)traits::arity, flags,
                (void *)xfunc_ptr.release(), &database::forward, 0, 0, &dispose))) {
    throw error(ec);
  }
}

template <typename AG>
void database::create_aggregate(std::string const &name, int flags) {
  using detail::bind_this;
  using detail::make_invoker;
  using detail::result;
  using traits = detail::function_traits<decltype(&AG::step)>;

  aggregate_wrapper_t *wrapper = new aggregate_wrapper_t;
  AG *inst = new AG;
  wrapper->reset = [inst]() {
    inst->~AG();
    new (inst) AG();
  };
  wrapper->release = [inst]() { delete inst; };
  wrapper->step = make_invoker(bind_this(&AG::step, inst));
  wrapper->fin = [inst](sqlite3_context *ctx) {
    result(inst->finalize(), ctx);
  };

  int ec = 0;
  if (0 != (ec = sqlite3_create_function_v2(
                m_db.get(), name.c_str(), (int)traits::arity, flags,
                (void *)wrapper, 0, &step_ag, &final_ag, &dispose_ag))) {
    delete inst;
    delete wrapper;
    throw error(ec);
  }
}

inline void database::dispose(void *user_data) {
  auto *cb = (xfunc_t *)user_data;
  assert(cb != 0);

  delete cb;
}

}  // namespace sqlite3cpp
<commit_msg>Use string_view to wrap result value for sqlite.<commit_after>/*****************************************************************************
 * Copyright (c) 2019, Acer Yun-Tse Yang All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ******************************************************************************/
#include <cassert>
#include <type_traits>
#include <utility>

namespace sqlite3cpp {
namespace detail {

template <int>
struct placeholder_tmpl {};

template <int... Ints>
using indexes = std::integer_sequence<int, Ints...>;

template <int Max>
using make_indexes_t = std::make_integer_sequence<int, Max>;
}  // namespace detail
}  // namespace sqlite3cpp

// custome placeholders
namespace std {
template <int N>
struct is_placeholder<sqlite3cpp::detail::placeholder_tmpl<N>>
    : integral_constant<int, N + 1> {};
}  // namespace std

namespace sqlite3cpp {
namespace detail {

/**
 * tuple enumerate
 */
template <class F, class Tuple, int... I>
constexpr decltype(auto) enumerate_impl(F &&f, Tuple &&t,
                                        std::integer_sequence<int, I...>) {
  return (
      std::invoke(std::forward<F>(f), I, std::get<I>(std::forward<Tuple>(t))),
      ...);
}

template <class F, class Tuple>
constexpr decltype(auto) enumerate(F &&f, Tuple &&t) {
  return detail::enumerate_impl(
      std::forward<F>(f), std::forward<Tuple>(t),
      std::make_integer_sequence<
          int, std::tuple_size_v<std::remove_reference_t<Tuple>>>{});
}

/**
 * Helpers for retrieve column values.
 */
inline void get_col_val_aux(sqlite3_stmt *stmt, sqlite3 *, int index,
                            int &val) {
  val = sqlite3_column_int(stmt, index);
}

inline void get_col_val_aux(sqlite3_stmt *stmt, sqlite3 *, int index,
                            int64_t &val) {
  val = sqlite3_column_int64(stmt, index);
}

inline void get_col_val_aux(sqlite3_stmt *stmt, sqlite3 *, int index,
                            double &val) {
  val = sqlite3_column_double(stmt, index);
}

inline void get_col_val_aux(sqlite3_stmt *stmt, sqlite3 *db, int index,
                            std::string &val) {
  char const *res = (char const *)sqlite3_column_text(stmt, index);
  if (!res) {
    int ec = sqlite3_errcode(db);
    if (ec != SQLITE_OK) throw error(ec);
  }
  val.assign(res, sqlite3_column_bytes(stmt, index));
}

inline void get_col_val_aux(sqlite3_stmt *stmt, sqlite3 *db, int index,
                            std::string_view &val) {
  char const *res = (char const *)sqlite3_column_text(stmt, index);
  if (!res) {
    int ec = sqlite3_errcode(db);
    if (ec != SQLITE_OK) throw error(ec);
  }
  val = std::string_view{
      (char const *)sqlite3_column_text(stmt, index),
      (std::string_view::size_type)sqlite3_column_bytes(stmt, index)};
}

inline void get_col_val_aux(sqlite3_stmt *stmt, sqlite3 *db, int index,
                            std::optional<std::string> &val) {
  char const *res = (char const *)sqlite3_column_text(stmt, index);
  if (!res) {
    int ec = sqlite3_errcode(db);
    if (ec != SQLITE_OK) val.reset();
  }
  val = std::string{res,
                    (std::string::size_type)sqlite3_column_bytes(stmt, index)};
}

inline void get_col_val_aux(sqlite3_stmt *stmt, sqlite3 *db, int index,
                            std::optional<std::string_view> &val) {
  char const *res = (char const *)sqlite3_column_text(stmt, index);
  if (!res) {
    int ec = sqlite3_errcode(db);
    if (ec != SQLITE_OK) val.reset();
  }
  val = std::string_view{
      res, (std::string_view::size_type)sqlite3_column_bytes(stmt, index)};
}

/*
 * Helpers for binding values to sqlite3_stmt.
 */
inline void bind_to_stmt(sqlite3_stmt *stmt, int i) {}

inline int bind_val(sqlite3_stmt *stmt, int index, int val) {
  return sqlite3_bind_int(stmt, index, val);
}

inline int bind_val(sqlite3_stmt *stmt, int index, double val) {
  return sqlite3_bind_double(stmt, index, val);
}

inline int bind_val(sqlite3_stmt *stmt, int index, std::string const &val) {
  return sqlite3_bind_text(stmt, index, val.c_str(), val.size(), SQLITE_STATIC);
}

inline int bind_val(sqlite3_stmt *stmt, int index, std::string_view val) {
  return sqlite3_bind_text(stmt, index, val.data(), val.size(), SQLITE_STATIC);
}

inline int bind_val(sqlite3_stmt *stmt, int index, char const *val) {
  return sqlite3_bind_text(stmt, index, val, -1, SQLITE_STATIC);
}

inline int bind_val(sqlite3_stmt *stmt, int index, std::nullptr_t _) {
  return sqlite3_bind_null(stmt, index);
}

template <typename T, typename... Args>
void bind_to_stmt(sqlite3_stmt *stmt, int index, T &&val, Args &&... args) {
  int ec = 0;
  if (0 != (ec = bind_val(stmt, index, std::forward<T>(val)))) throw error(ec);
  bind_to_stmt(stmt, index + 1, std::forward<Args>(args)...);
}

/**
 * Helpers for converting value from sqlite3_value.
 */
template <typename T>
struct Type {};

inline int get(Type<int>, sqlite3_value **v, int const index) {
  return sqlite3_value_int(v[index]);
}

inline int64_t get(Type<int64_t>, sqlite3_value **v, int const index) {
  return sqlite3_value_int64(v[index]);
}

inline double get(Type<double>, sqlite3_value **v, int const index) {
  return sqlite3_value_double(v[index]);
}

inline std::string get(Type<std::string>, sqlite3_value **v, int const index) {
  return std::string((char const *)sqlite3_value_text(v[index]),
                     (size_t)sqlite3_value_bytes(v[index]));
}

inline std::string_view get(Type<std::string_view>, sqlite3_value **v,
                            int const index) {
  return std::string_view((char const *)sqlite3_value_text(v[index]),
                          (size_t)sqlite3_value_bytes(v[index]));
}
/**
 * Helpers for setting result of scalar functions.
 */
template <typename T>
constexpr bool can_convert_to_string_view_v =
    std::is_convertible_v<std::decay_t<T>, std::string_view>;

template <typename T,
          std::enable_if_t<can_convert_to_string_view_v<T>, bool> = true>
inline void result(T const &val, sqlite3_context *ctx) {
  std::string_view sv(val);
  sqlite3_result_text(ctx, sv.data(), sv.size(), SQLITE_TRANSIENT);
}

template <typename T,
          std::enable_if_t<!can_convert_to_string_view_v<T>, bool> = true>
inline void result(T val, sqlite3_context *ctx) {
  if constexpr (std::is_same_v<T, int>) {
    sqlite3_result_int(ctx, val);
  } else if (std::is_same_v<T, int64_t>) {
    sqlite3_result_int64(ctx, val);
  } else if (std::is_same_v<T, double>) {
    sqlite3_result_double(ctx, val);
  } else {
    assert(false);
  }
}

/**
 * Magic for typesafe invoking lambda/std::function from sqlite3
 * (registered via sqlite3_create_function).
 */
template <typename R, typename... Args, int... Is>
R invoke(std::function<R(Args...)> func, int argc, sqlite3_value **argv,
         indexes<Is...>) {
  // TODO Check argc
  // Expand argv per index
  return func(get(Type<Args>{}, argv, Is)...);
}

template <typename R, typename... Args>
R invoke(std::function<R(Args...)> func, int argc, sqlite3_value **argv) {
  return invoke(func, argc, argv, make_indexes_t<sizeof...(Args)>{});
}

template <typename R, typename... Args>
auto make_invoker(std::function<R(Args...)> &&func) {
  if constexpr (std::is_void_v<R>) {
    return [func](sqlite3_context *ctx, int argc, sqlite3_value **argv) {
      invoke(func, argc, argv);
    };
  } else {
    return [func](sqlite3_context *ctx, int argc, sqlite3_value **argv) {
      result(invoke(func, argc, argv), ctx);
    };
  }
}

/**
 * Function traits for supporting lambda.
 */

// For generic types that are functors, delegate to its 'operator()'
template <typename T>
struct function_traits : public function_traits<decltype(&T::operator())> {};

// for pointers to member function
template <typename C, typename R, typename... Args>
struct function_traits<R (C::*)(Args...)> {
  typedef std::function<R(Args...)> f_type;
  static const size_t arity = sizeof...(Args);
};

// for pointers to const member function
template <typename C, typename R, typename... Args>
struct function_traits<R (C::*)(Args...) const> {
  typedef std::function<R(Args...)> f_type;
  static const size_t arity = sizeof...(Args);
};

// for function pointers
template <typename R, typename... Args>
struct function_traits<R (*)(Args...)> {
  typedef std::function<R(Args...)> f_type;
  static const size_t arity = sizeof...(Args);
};

/**
 * Member function binder helpers (auto expand by passed function prototype)
 */
template <typename F, typename C, int... Is>
typename function_traits<F>::f_type bind_this(F f, C *this_, indexes<Is...>) {
  return std::bind(f, this_, placeholder_tmpl<Is>{}...);
}

template <typename F, typename C>
typename function_traits<F>::f_type bind_this(F f, C *this_) {
  using traits = function_traits<F>;
  return bind_this(f, this_, make_indexes_t<traits::arity>{});
}
}  // namespace detail
}  // namespace sqlite3cpp

namespace sqlite3cpp {

/**
 * row impl
 */
template <typename... Cols>
std::tuple<Cols...> row::to() const {
  std::tuple<Cols...> result;
  detail::enumerate(
      [this](int index, auto &&tuple_value) {
        detail::get_col_val_aux(m_stmt, m_db, index, tuple_value);
      },
      result);
  return result;
}

/**
 * cursor impl
 */
template <typename... Args>
cursor &cursor::execute(std::string const &sql, Args &&... args) {
  // TODO: Support rebind params
  sqlite3_stmt *stmt = 0;
  int ec = 0;

  if (0 != (ec = sqlite3_prepare_v2(m_db, sql.c_str(), sql.size(), &stmt, 0)))
    throw error(ec);

  m_stmt.reset(stmt);
  detail::bind_to_stmt(m_stmt.get(), 1, std::forward<Args>(args)...);
  step();
  return *this;
}

/**
 * database impl
 */

template <typename... Args>
cursor database::execute(std::string const &sql, Args &&... args) {
  cursor c = make_cursor();
  c.execute(sql, std::forward<Args>(args)...);
  return c;
}

template <typename FUNC>
void database::create_scalar(std::string const &name, FUNC func, int flags) {
  using traits = detail::function_traits<FUNC>;

  auto xfunc_ptr =
      std::make_unique<xfunc_t>(detail::make_invoker(typename traits::f_type(func)));

  int ec = 0;
  if (0 != (ec = sqlite3_create_function_v2(
                m_db.get(), name.c_str(), (int)traits::arity, flags,
                (void *)xfunc_ptr.release(), &database::forward, 0, 0, &dispose))) {
    throw error(ec);
  }
}

template <typename AG>
void database::create_aggregate(std::string const &name, int flags) {
  using detail::bind_this;
  using detail::make_invoker;
  using detail::result;
  using traits = detail::function_traits<decltype(&AG::step)>;

  aggregate_wrapper_t *wrapper = new aggregate_wrapper_t;
  AG *inst = new AG;
  wrapper->reset = [inst]() {
    inst->~AG();
    new (inst) AG();
  };
  wrapper->release = [inst]() { delete inst; };
  wrapper->step = make_invoker(bind_this(&AG::step, inst));
  wrapper->fin = [inst](sqlite3_context *ctx) {
    result(inst->finalize(), ctx);
  };

  int ec = 0;
  if (0 != (ec = sqlite3_create_function_v2(
                m_db.get(), name.c_str(), (int)traits::arity, flags,
                (void *)wrapper, 0, &step_ag, &final_ag, &dispose_ag))) {
    delete inst;
    delete wrapper;
    throw error(ec);
  }
}

inline void database::dispose(void *user_data) {
  auto *cb = (xfunc_t *)user_data;
  assert(cb != 0);

  delete cb;
}

}  // namespace sqlite3cpp
<|endoftext|>