<commit_before>#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <vector>
#include <regex>
#include <ctype.h>
#include "lexer.h"

LexerTarget::LexerTarget(std::string name) {
    filename = name;
    content = read_file(name);
    lineNum = 0;
    colNum = 0;
    sub_begin = sub_len = 0;
}

LexerTarget::~LexerTarget() {
}

std::string LexerTarget::lex() {

    std::string nonalpha = "{}[]()<>*+-/=.==\"";
    std::string two_char = "/+=-*";
    std::string token = "";
    int comment_depth;
    char last_char = 0;

    std::string ln = content.at(lineNum);

    int i;

    if(comment_depth) {
        return lexcomment();
    }

    while(isspace(ln[sub_begin])) {
        sub_begin += 1;
        if(ln.size() <= sub_begin) {
            sub_begin = 0;
            lineNum++;
            ln = content.at(lineNum);
        }
    }

    char c = ln[sub_begin];

    if(nonalpha.find(c,0) != std::string::npos) {
        if(ln.size() > sub_begin+1) {
            if(c == '/' && ln[sub_begin+1] == '*') {
                std::cout << "comment begin \n";
                sub_begin += 1;
                comment_depth++;
            }
        }
    }

    for(i = sub_begin; i < ln.size(); i++,sub_len++) {
        char c = ln[i];

    }

    for (auto st : content) {
        std::cout << "\n" << lineNum << ": " << st << "\n";
        sub_begin = 0;
        sub_len = 0;
        int count = 0;
        for(auto& c : st) {
            count++;

            switch(last_char) {
                case '/':
                    if(c == '*') {
                        //std::cout << "In a comment\n";
                        comment_depth++;
                        last_char = 0;
                    }
                    break;
                case '*':
                    if(c == '/') {
                        //std::cout << "Out a comment\n";
                        comment_depth--;
                        last_char = 0;
                        if(comment_depth == 0) {
                            token += st.substr(sub_begin,count);
                            std::cout << token << '\n';
                            token = "";
                            sub_begin = count;
                            sub_len = 0;
                        }
                    }
                    break;
                default:
                    if(last_char != 0)
                        std::cout << last_char << '\n';
                    break;
            }

            if(comment_depth) {
                if(c == '/' || c == '*') {
                    last_char = c;
                }
                continue;
            }

            if(nonalpha.find(c,0) != std::string::npos) {
                if(sub_len > 0) {
                    token = st.substr(sub_begin, sub_len);
                    std::cout << token << '\n';
                    token = "";
                    sub_begin += sub_len+2;
                    sub_len = 0;
                }

                if(two_char.find(c,0) != std::string::npos) {
                    std::cout << "foo " << c << "\n";
                    last_char = c;
                    sub_len = 1;
                }
                continue;
            }

            if(isspace(c)) {
                token = st.substr(sub_begin, sub_len);
                std::cout << token << '\n';
                token = "";
                sub_begin += sub_len+1;
                sub_len = 0;
                continue;
            }

            sub_len += 1;
        }

        if(comment_depth) {
            token += st.substr(sub_begin)+'\n';
        } else if(sub_len) {
            token = st.substr(sub_begin, sub_len);
            std::cout << token << '\n';
            token = "";
        }

        lineNum++;
    }
}

std::string LexerTarget::lexcomment() {

    return "";
}

std::string LexerTarget::lex(bool b) {
    std::string token = "";
    std::string tokentype = "";

    if(lineNum >= content.size()) {
        return "EOF";
    }

    std::string ln = content.at(lineNum);
    if(colNum >= ln.size()) {
        lineNum++;
        colNum = 0;
        if(lineNum >= content.size())
            return "EOF";
        ln = content.at(lineNum);
    }
lex:
    while(isspace(ln[colNum])) {
        colNum++;

        //std::cout << "there is a space!!!\n";
        if(colNum >= ln.size()) {
            if(lineNum+1 >= content.size()) {
                return "EOF";
            } else {
                lineNum++;
                ln = content.at(lineNum);
                colNum = 0;
            }
        }
    }

    std::regex longest_regex_match;
    int longest_match = 0;

    //std::cout << "colNum = " << colNum << '\n';
    std::cout << ln << '\n';
    for(int c = 0; c < colNum-1; c++) {
        std::cout << '-';
    }
    std::cout << "-^\n";

    std::smatch match_comment;
    std::string t = ln.substr(colNum);
    if(std::regex_search(t,match_comment,std::regex("//"))) {
        for(int i = 0; i < match_comment.size(); i++) {
            if(match_comment.position(i) != 0) continue;

            colNum = 0;
            lineNum++;
            if(lineNum >= content.size()) {
                return "EOF";
            }
            ln = content.at(lineNum);
            goto lex;
        }
    }

    for (int i = 0; i < num_regexes; i++) {
        std::smatch tmp;
        std::string remaining = ln.substr(colNum);
        bool matched = std::regex_search(remaining,tmp,regexes[i]);
        if(matched) {
            //std::cout << "Matched for regex: " << i << '\n';
            //std::cout << "Total # of matches: " << tmp.size() << '\n';
        for(int j = 0; j < tmp.size(); j++) {
            //make sure match starts at beginning of last token
            //std::cout << "Match for: " << tmp[j] << " at pos " << tmp.position(j) <<'\n';
            if(tmp.position(j) != 0) continue;

            if(tmp.length(j) > longest_match) {
                longest_match = tmp.length(j);
                longest_regex_match = regexes[i];
            }
        }
        }
    }

    token = ln.substr(colNum,longest_match);
    colNum += longest_match;

    std::cout << "token: " << token << "\n\n";
    return token;

}

std::vector<std::string> read_file(const std::string& filename) {
    std::ifstream in(filename);
    std::vector<std::string> content;
    std::string line;
    while(std::getline(in, line)) {
        content.push_back(line);
        std::cout << line << '\n';
    }

    return content;
}

int main() {
    LexerTarget target1 = LexerTarget("test.txt");
    //target1.lex();
    std::string tok;
    while(tok.compare("EOF") != 0) {
        tok = target1.lex(true);
    }
    return 0;
}
<commit_msg>Remove goto, replace with recursive call.<commit_after>#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <vector>
#include <regex>
#include <ctype.h>
#include "lexer.h"

LexerTarget::LexerTarget(std::string name) {
    filename = name;
    content = read_file(name);
    lineNum = 0;
    colNum = 0;
    sub_begin = sub_len = 0;
}

LexerTarget::~LexerTarget() {
}

std::string LexerTarget::lex() {

    std::string nonalpha = "{}[]()<>*+-/=.==\"";
    std::string two_char = "/+=-*";
    std::string token = "";
    int comment_depth;
    char last_char = 0;

    std::string ln = content.at(lineNum);

    int i;

    if(comment_depth) {
        return lexcomment();
    }

    while(isspace(ln[sub_begin])) {
        sub_begin += 1;
        if(ln.size() <= sub_begin) {
            sub_begin = 0;
            lineNum++;
            ln = content.at(lineNum);
        }
    }

    char c = ln[sub_begin];

    if(nonalpha.find(c,0) != std::string::npos) {
        if(ln.size() > sub_begin+1) {
            if(c == '/' && ln[sub_begin+1] == '*') {
                std::cout << "comment begin \n";
                sub_begin += 1;
                comment_depth++;
            }
        }
    }

    for(i = sub_begin; i < ln.size(); i++,sub_len++) {
        char c = ln[i];

    }

    for (auto st : content) {
        std::cout << "\n" << lineNum << ": " << st << "\n";
        sub_begin = 0;
        sub_len = 0;
        int count = 0;
        for(auto& c : st) {
            count++;

            switch(last_char) {
                case '/':
                    if(c == '*') {
                        //std::cout << "In a comment\n";
                        comment_depth++;
                        last_char = 0;
                    }
                    break;
                case '*':
                    if(c == '/') {
                        //std::cout << "Out a comment\n";
                        comment_depth--;
                        last_char = 0;
                        if(comment_depth == 0) {
                            token += st.substr(sub_begin,count);
                            std::cout << token << '\n';
                            token = "";
                            sub_begin = count;
                            sub_len = 0;
                        }
                    }
                    break;
                default:
                    if(last_char != 0)
                        std::cout << last_char << '\n';
                    break;
            }

            if(comment_depth) {
                if(c == '/' || c == '*') {
                    last_char = c;
                }
                continue;
            }

            if(nonalpha.find(c,0) != std::string::npos) {
                if(sub_len > 0) {
                    token = st.substr(sub_begin, sub_len);
                    std::cout << token << '\n';
                    token = "";
                    sub_begin += sub_len+2;
                    sub_len = 0;
                }

                if(two_char.find(c,0) != std::string::npos) {
                    std::cout << "foo " << c << "\n";
                    last_char = c;
                    sub_len = 1;
                }
                continue;
            }

            if(isspace(c)) {
                token = st.substr(sub_begin, sub_len);
                std::cout << token << '\n';
                token = "";
                sub_begin += sub_len+1;
                sub_len = 0;
                continue;
            }

            sub_len += 1;
        }

        if(comment_depth) {
            token += st.substr(sub_begin)+'\n';
        } else if(sub_len) {
            token = st.substr(sub_begin, sub_len);
            std::cout << token << '\n';
            token = "";
        }

        lineNum++;
    }
}

std::string LexerTarget::lexcomment() {

    return "";
}

std::string LexerTarget::lex(bool b) {
    std::string token = "";
    std::string tokentype = "";

    if(lineNum >= content.size()) {
        return "EOF";
    }

    std::string ln = content.at(lineNum);
    if(colNum >= ln.size()) {
        lineNum++;
        colNum = 0;
        if(lineNum >= content.size())
            return "EOF";
        ln = content.at(lineNum);
    }

    while(isspace(ln[colNum])) {
        colNum++;

        //std::cout << "there is a space!!!\n";
        if(colNum >= ln.size()) {
            if(lineNum+1 >= content.size()) {
                return "EOF";
            } else {
                lineNum++;
                ln = content.at(lineNum);
                colNum = 0;
            }
        }
    }

    std::regex longest_regex_match;
    int longest_match = 0;

    //std::cout << "colNum = " << colNum << '\n';
    std::cout << ln << '\n';
    for(int c = 0; c < colNum-1; c++) {
        std::cout << '-';
    }
    std::cout << "-^\n";

    std::smatch match_comment;
    std::string t = ln.substr(colNum);
    if(std::regex_search(t,match_comment,std::regex("//"))) {
        for(int i = 0; i < match_comment.size(); i++) {
            if(match_comment.position(i) != 0) continue;

            colNum = 0;
            lineNum++;
            if(lineNum >= content.size()) {
                return "EOF";
            }
            ln = content.at(lineNum);
            return lex(true);
        }
    }

    for (int i = 0; i < num_regexes; i++) {
        std::smatch tmp;
        std::string remaining = ln.substr(colNum);
        bool matched = std::regex_search(remaining,tmp,regexes[i]);
        if(matched) {
            //std::cout << "Matched for regex: " << i << '\n';
            //std::cout << "Total # of matches: " << tmp.size() << '\n';
        for(int j = 0; j < tmp.size(); j++) {
            //make sure match starts at beginning of last token
            //std::cout << "Match for: " << tmp[j] << " at pos " << tmp.position(j) <<'\n';
            if(tmp.position(j) != 0) continue;

            if(tmp.length(j) > longest_match) {
                longest_match = tmp.length(j);
                longest_regex_match = regexes[i];
            }
        }
        }
    }

    token = ln.substr(colNum,longest_match);
    colNum += longest_match;

    std::cout << "token: " << token << "\n\n";
    return token;

}

std::vector<std::string> read_file(const std::string& filename) {
    std::ifstream in(filename);
    std::vector<std::string> content;
    std::string line;
    while(std::getline(in, line)) {
        content.push_back(line);
        std::cout << line << '\n';
    }

    return content;
}

int main() {
    LexerTarget target1 = LexerTarget("test.txt");
    //target1.lex();
    std::string tok;
    while(tok.compare("EOF") != 0) {
        tok = target1.lex(true);
    }
    return 0;
}
<|endoftext|>


<commit_before>/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkTclUtil.cxx
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

Copyright (c) 1993-1998 Ken Martin, Will Schroeder, Bill Lorensen.

This software is copyrighted by Ken Martin, Will Schroeder and Bill Lorensen.
The following terms apply to all files associated with the software unless
explicitly disclaimed in individual files. This copyright specifically does
not apply to the related textbook "The Visualization Toolkit" ISBN
013199837-4 published by Prentice Hall which is covered by its own copyright.

The authors hereby grant permission to use, copy, and distribute this
software and its documentation for any purpose, provided that existing
copyright notices are retained in all copies and that this notice is included
verbatim in any distributions. Additionally, the authors grant permission to
modify this software and its documentation for any purpose, provided that
such modifications are not distributed without the explicit consent of the
authors and that existing copyright notices are retained in all copies. Some
of the algorithms implemented by this software are patented, observe all
applicable patent law.

IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY FOR
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF,
EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE IS PROVIDED ON AN
"AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.

=========================================================================*/

#include <iostream.h>
#include <stdlib.h>
#include "vtkObject.h"
#include "vtkTclUtil.h"
#include "vtkSetGet.h"

int vtkTclEval(char *str)
{
  int res;

  res = Tcl_GlobalEval(vtkGlobalTclInterp, str);

  if (res == TCL_ERROR)
    {
    vtkGenericWarningMacro("Error returned from vtk/tcl callback.\n" <<
			   vtkGlobalTclInterp->result << endl);
    }
  return res;
}

// The string result returned is volatile so you should copy it.
char *vtkTclGetResult()
{
  return vtkGlobalTclInterp->result;
}

int vtkRendererCommand(ClientData cd, Tcl_Interp *interp,
		       int argc, char *argv[]);
int vtkRenderWindowCommand(ClientData cd, Tcl_Interp *interp,
			      int argc, char *argv[]);

extern Tcl_HashTable vtkInstanceLookup;
extern Tcl_HashTable vtkPointerLookup;
extern Tcl_HashTable vtkCommandLookup;

static int num = 0;
static int vtkTclDebugOn = 0;
static int vtkInDelete = 0;

int vtkTclInDelete()
{
  return vtkInDelete;
}

// we do no error checking in this.  We assume that if we were called
// then tcl must have been able to find the command function and object
int vtkTclDeleteObjectFromHash(ClientData cd)
{
  char temps[80];
  Tcl_HashEntry *entry;
  char *temp;
  int (*command)(ClientData, Tcl_Interp *,int, char *[]);

  // lookup the objects name
  sprintf(temps,"%p",(void *)cd);
  entry = Tcl_FindHashEntry(&vtkPointerLookup,temps);
  temp = (char *)(Tcl_GetHashValue(entry));

  // now delete the three hash entries
  entry = Tcl_FindHashEntry(&vtkCommandLookup,temp);
  command =
    (int (*)(ClientData,Tcl_Interp *,int,char *[]))Tcl_GetHashValue(entry);
  Tcl_DeleteHashEntry(entry);
  entry = Tcl_FindHashEntry(&vtkPointerLookup,temps);
  Tcl_DeleteHashEntry(entry);
  entry = Tcl_FindHashEntry(&vtkInstanceLookup,temp);
  Tcl_DeleteHashEntry(entry);

  if (vtkTclDebugOn)
    {
    vtkGenericWarningMacro("vtkTcl Attempting to free object named " << temp);
    }
  // if it isn't a temp object (i.e. we created it) then delete it
  if (strncmp(temp,"vtkTemp",7))
    {
    // finally free the name we got from the hash table
    // it was created using strdup
    free (temp);
    return 1;
    }
  // finally free the name we got from the hash table
  // it was created using strdup
  free (temp);
  return 0;
}

// we do no error checking in this.  We assume that if we were called
// then tcl must have been able to find the command function and object
void vtkTclGenericDeleteObject(ClientData cd)
{
  char temps[80];
  Tcl_HashEntry *entry;
  int (*command)(ClientData, Tcl_Interp *,int, char *[]);
  char *args[2];
  char *temp;
  Tcl_Interp *i;

  /* set up the args */
  args[1] = "Delete";

  // lookup the objects name
  sprintf(temps,"%p",(void *)cd);
  entry = Tcl_FindHashEntry(&vtkPointerLookup,temps);
  temp = strdup((char *)(Tcl_GetHashValue(entry)));
  args[0] = temp;

  // get the command function and invoke the delete operation
  entry = Tcl_FindHashEntry(&vtkCommandLookup,temp);
  command = (int (*)(ClientData,Tcl_Interp *,int,char *[]))Tcl_GetHashValue(entry);

  // do we need to delete the c++ obj
  if (vtkTclDeleteObjectFromHash(cd))
    {
    i = Tcl_CreateInterp();
    vtkInDelete = 1;
    command(cd,i,2,args);
    vtkInDelete = 0;
    Tcl_DeleteInterp(i);
    }
  if (temp) free(temp);
}

int vtkCommand(ClientData cd, Tcl_Interp *interp, int argc, char *argv[])
{
  Tcl_HashEntry *entry;
  Tcl_HashSearch search;
  char * tmp;

  cd = 0; // shut up the compiler

  if (argc < 2) return TCL_OK;

  if (!strcmp(argv[1],"DeleteAllObjects"))
    {
    for (entry = Tcl_FirstHashEntry(&vtkPointerLookup,&search);
	 entry != NULL;
	 entry = Tcl_FirstHashEntry(&vtkPointerLookup,&search))
      {
      tmp = strdup((char *)Tcl_GetHashValue(entry));
      if (tmp) Tcl_DeleteCommand(interp,tmp);
	  if (tmp) free(tmp);
      }
    return TCL_OK;
    }
  if (!strcmp(argv[1],"DebugOn"))
    {
    vtkTclDebugOn = 1;
    return TCL_OK;
    }
  if (!strcmp(argv[1],"DebugOff"))
    {
    vtkTclDebugOn = 0;
    return TCL_OK;
    }
  if (!strcmp("ListMethods",argv[1]))
    {
    Tcl_AppendResult(interp,"Methods for vtkCommand:\n",NULL);
    Tcl_AppendResult(interp,"  DebugOn\n",NULL);
    Tcl_AppendResult(interp,"  DebugOff\n",NULL);
    Tcl_AppendResult(interp,"  DeleteAllObjects\n",NULL);
    return TCL_OK;
    }

  Tcl_AppendResult(interp,"invalid method for vtkCommand\n",NULL);
  return TCL_ERROR;
}

void vtkTclGetObjectFromPointer(Tcl_Interp *interp,void *temp,
			  int command(ClientData, Tcl_Interp *,int, char *[]))
{
  int is_new;
  char temps[80];
  char name[80];
  Tcl_HashEntry *entry;

  /* if it is NULL then return empty string */
  if (!temp)
    {
    interp->result[0] = '\0';
    return;
    }

  /* return a pointer to a vtk Object */
  /* first we must look up the pointer to see if it already exists */
  sprintf(temps,"%p",temp);
  if ((entry = Tcl_FindHashEntry(&vtkPointerLookup,temps)))
    {
    sprintf(interp->result,"%s",(char *)(Tcl_GetHashValue(entry)));
    return;
    }

  /* we must create a new name if it isn't NULL */
  sprintf(name,"vtkTemp%i",num);
  num++;

  entry = Tcl_CreateHashEntry(&vtkInstanceLookup,name,&is_new);
  Tcl_SetHashValue(entry,(ClientData)(temp));
  entry = Tcl_CreateHashEntry(&vtkPointerLookup,temps,&is_new);
  Tcl_SetHashValue(entry,(ClientData)(strdup(name)));
  Tcl_CreateCommand(interp,name,command,
		    (ClientData)(temp),
		    (Tcl_CmdDeleteProc *)vtkTclGenericDeleteObject);
  entry = Tcl_CreateHashEntry(&vtkCommandLookup,name,&is_new);
  Tcl_SetHashValue(entry,(ClientData)command);
  sprintf(interp->result,"%s",name);
}

void *vtkTclGetPointerFromObject(char *name,char *result_type,
				 Tcl_Interp *interp)
{
  Tcl_HashEntry *entry;
  ClientData temp;
  int (*command)(ClientData, Tcl_Interp *,int, char *[]);
  char *args[3];
  char temps[256];

  /* set up the args */
  args[0] = "DoTypecasting";
  args[1] = result_type;
  args[2] = NULL;

  // object names cannot start with a number
  if ((name[0] >= '0')&&(name[0] <= '9'))
    {
    return NULL;
    }
  if ((entry = Tcl_FindHashEntry(&vtkInstanceLookup,name)))
    {
    temp = (ClientData)Tcl_GetHashValue(entry);
    }
  else
    {
    sprintf(temps,"vtk bad argument, could not find object named %s\n", name);
    Tcl_AppendResult(interp,temps,NULL);
    return NULL;
    }

  /* now handle the typecasting, get the command proc */
  if ((entry = Tcl_FindHashEntry(&vtkCommandLookup,name)))
    {
    command = (int (*)(ClientData,Tcl_Interp *,int,char *[]))Tcl_GetHashValue(entry);
    }
  else
    {
    sprintf(temps,"vtk bad argument, could not find command process for %s.\n", name);
    Tcl_AppendResult(interp,temps,NULL);
    return NULL;
    }

  if (command(temp,(Tcl_Interp *)NULL,3,args) == TCL_OK)
    {
    return (void *)(args[2]);
    }
  else
    {
    sprintf(temps,"vtk bad argument, type conversion failed for object %s.\nCould not type convert %s to type %s.\n", name, name, result_type);
    Tcl_AppendResult(interp,temps,NULL);
    return NULL;
    }

}

void vtkTclVoidFunc(void *arg)
{
  int res;

  vtkTclVoidFuncArg *arg2;

  arg2 = (vtkTclVoidFuncArg *)arg;

  res = Tcl_GlobalEval(arg2->interp, arg2->command);

  if (res == TCL_ERROR)
    {
    vtkGenericWarningMacro("Error returned from vtk/tcl callback:\n" <<
			   Tcl_GetVar(arg2->interp,"errorInfo",0) <<
			   " at line number " << arg2->interp->errorLine);
    }
}

void vtkTclVoidFuncArgDelete(void *arg)
{
  vtkTclVoidFuncArg *arg2;

  arg2 = (vtkTclVoidFuncArg *)arg;

  // free the string and then structure
  delete [] arg2->command;
  delete arg2;
}

void vtkTclListInstances(Tcl_Interp *interp, ClientData arg)
{
  Tcl_HashSearch srch;
  Tcl_HashEntry *entry;
  int first = 1;

  // iteratively search hash table for command function
  entry = Tcl_FirstHashEntry(&vtkCommandLookup, &srch);
  if (!entry)
    {
    interp->result[0] = '\0';
    return;
    }
  while (entry)
    {
    if (Tcl_GetHashValue(entry) == arg)
      {
      if (first)
	{
	first = 0;
	Tcl_AppendResult(interp,Tcl_GetHashKey(&vtkCommandLookup,entry),NULL);
	}
      else
	{
	Tcl_AppendResult(interp, " ", Tcl_GetHashKey(&vtkCommandLookup,entry),
			 NULL);
	}
      }
    entry = Tcl_NextHashEntry(&srch);
    }
}

<commit_msg>ERR: better handling of error<commit_after>/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkTclUtil.cxx
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

Copyright (c) 1993-1998 Ken Martin, Will Schroeder, Bill Lorensen.

This software is copyrighted by Ken Martin, Will Schroeder and Bill Lorensen.
The following terms apply to all files associated with the software unless
explicitly disclaimed in individual files. This copyright specifically does
not apply to the related textbook "The Visualization Toolkit" ISBN
013199837-4 published by Prentice Hall which is covered by its own copyright.

The authors hereby grant permission to use, copy, and distribute this
software and its documentation for any purpose, provided that existing
copyright notices are retained in all copies and that this notice is included
verbatim in any distributions. Additionally, the authors grant permission to
modify this software and its documentation for any purpose, provided that
such modifications are not distributed without the explicit consent of the
authors and that existing copyright notices are retained in all copies. Some
of the algorithms implemented by this software are patented, observe all
applicable patent law.

IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY FOR
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF,
EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE IS PROVIDED ON AN
"AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.

=========================================================================*/

#include <iostream.h>
#include <stdlib.h>
#include "vtkObject.h"
#include "vtkTclUtil.h"
#include "vtkSetGet.h"

int vtkTclEval(char *str)
{
  int res;

  res = Tcl_GlobalEval(vtkGlobalTclInterp, str);

  if (res == TCL_ERROR)
    {
    vtkGenericWarningMacro("Error returned from vtk/tcl callback.\n" <<
			   vtkGlobalTclInterp->result << endl);
    }
  return res;
}

// The string result returned is volatile so you should copy it.
char *vtkTclGetResult()
{
  return vtkGlobalTclInterp->result;
}

int vtkRendererCommand(ClientData cd, Tcl_Interp *interp,
		       int argc, char *argv[]);
int vtkRenderWindowCommand(ClientData cd, Tcl_Interp *interp,
			      int argc, char *argv[]);

extern Tcl_HashTable vtkInstanceLookup;
extern Tcl_HashTable vtkPointerLookup;
extern Tcl_HashTable vtkCommandLookup;

static int num = 0;
static int vtkTclDebugOn = 0;
static int vtkInDelete = 0;

int vtkTclInDelete()
{
  return vtkInDelete;
}

// we do no error checking in this.  We assume that if we were called
// then tcl must have been able to find the command function and object
int vtkTclDeleteObjectFromHash(ClientData cd)
{
  char temps[80];
  Tcl_HashEntry *entry;
  char *temp;
  int (*command)(ClientData, Tcl_Interp *,int, char *[]);

  // lookup the objects name
  sprintf(temps,"%p",(void *)cd);
  entry = Tcl_FindHashEntry(&vtkPointerLookup,temps);
  temp = (char *)(Tcl_GetHashValue(entry));

  // now delete the three hash entries
  entry = Tcl_FindHashEntry(&vtkCommandLookup,temp);
  command =
    (int (*)(ClientData,Tcl_Interp *,int,char *[]))Tcl_GetHashValue(entry);
  Tcl_DeleteHashEntry(entry);
  entry = Tcl_FindHashEntry(&vtkPointerLookup,temps);
  Tcl_DeleteHashEntry(entry);
  entry = Tcl_FindHashEntry(&vtkInstanceLookup,temp);
  Tcl_DeleteHashEntry(entry);

  if (vtkTclDebugOn)
    {
    vtkGenericWarningMacro("vtkTcl Attempting to free object named " << temp);
    }
  // if it isn't a temp object (i.e. we created it) then delete it
  if (strncmp(temp,"vtkTemp",7))
    {
    // finally free the name we got from the hash table
    // it was created using strdup
    free (temp);
    return 1;
    }
  // finally free the name we got from the hash table
  // it was created using strdup
  free (temp);
  return 0;
}

// we do no error checking in this.  We assume that if we were called
// then tcl must have been able to find the command function and object
void vtkTclGenericDeleteObject(ClientData cd)
{
  char temps[80];
  Tcl_HashEntry *entry;
  int (*command)(ClientData, Tcl_Interp *,int, char *[]);
  char *args[2];
  char *temp;
  Tcl_Interp *i;

  /* set up the args */
  args[1] = "Delete";

  // lookup the objects name
  sprintf(temps,"%p",(void *)cd);
  entry = Tcl_FindHashEntry(&vtkPointerLookup,temps);
  temp = strdup((char *)(Tcl_GetHashValue(entry)));
  args[0] = temp;

  // get the command function and invoke the delete operation
  entry = Tcl_FindHashEntry(&vtkCommandLookup,temp);
  command = (int (*)(ClientData,Tcl_Interp *,int,char *[]))Tcl_GetHashValue(entry);

  // do we need to delete the c++ obj
  if (vtkTclDeleteObjectFromHash(cd))
    {
    i = Tcl_CreateInterp();
    vtkInDelete = 1;
    command(cd,i,2,args);
    vtkInDelete = 0;
    Tcl_DeleteInterp(i);
    }
  if (temp) free(temp);
}

int vtkCommand(ClientData cd, Tcl_Interp *interp, int argc, char *argv[])
{
  Tcl_HashEntry *entry;
  Tcl_HashSearch search;
  char * tmp;

  cd = 0; // shut up the compiler

  if (argc < 2) return TCL_OK;

  if (!strcmp(argv[1],"DeleteAllObjects"))
    {
    for (entry = Tcl_FirstHashEntry(&vtkPointerLookup,&search);
	 entry != NULL;
	 entry = Tcl_FirstHashEntry(&vtkPointerLookup,&search))
      {
      tmp = strdup((char *)Tcl_GetHashValue(entry));
      if (tmp) Tcl_DeleteCommand(interp,tmp);
	  if (tmp) free(tmp);
      }
    return TCL_OK;
    }
  if (!strcmp(argv[1],"DebugOn"))
    {
    vtkTclDebugOn = 1;
    return TCL_OK;
    }
  if (!strcmp(argv[1],"DebugOff"))
    {
    vtkTclDebugOn = 0;
    return TCL_OK;
    }
  if (!strcmp("ListMethods",argv[1]))
    {
    Tcl_AppendResult(interp,"Methods for vtkCommand:\n",NULL);
    Tcl_AppendResult(interp,"  DebugOn\n",NULL);
    Tcl_AppendResult(interp,"  DebugOff\n",NULL);
    Tcl_AppendResult(interp,"  DeleteAllObjects\n",NULL);
    return TCL_OK;
    }

  Tcl_AppendResult(interp,"invalid method for vtkCommand\n",NULL);
  return TCL_ERROR;
}

void vtkTclGetObjectFromPointer(Tcl_Interp *interp,void *temp,
			  int command(ClientData, Tcl_Interp *,int, char *[]))
{
  int is_new;
  char temps[80];
  char name[80];
  Tcl_HashEntry *entry;

  /* if it is NULL then return empty string */
  if (!temp)
    {
    interp->result[0] = '\0';
    return;
    }

  /* return a pointer to a vtk Object */
  /* first we must look up the pointer to see if it already exists */
  sprintf(temps,"%p",temp);
  if ((entry = Tcl_FindHashEntry(&vtkPointerLookup,temps)))
    {
    sprintf(interp->result,"%s",(char *)(Tcl_GetHashValue(entry)));
    return;
    }

  /* we must create a new name if it isn't NULL */
  sprintf(name,"vtkTemp%i",num);
  num++;

  entry = Tcl_CreateHashEntry(&vtkInstanceLookup,name,&is_new);
  Tcl_SetHashValue(entry,(ClientData)(temp));
  entry = Tcl_CreateHashEntry(&vtkPointerLookup,temps,&is_new);
  Tcl_SetHashValue(entry,(ClientData)(strdup(name)));
  Tcl_CreateCommand(interp,name,command,
		    (ClientData)(temp),
		    (Tcl_CmdDeleteProc *)vtkTclGenericDeleteObject);
  entry = Tcl_CreateHashEntry(&vtkCommandLookup,name,&is_new);
  Tcl_SetHashValue(entry,(ClientData)command);
  sprintf(interp->result,"%s",name);
}

void *vtkTclGetPointerFromObject(char *name,char *result_type,
				 Tcl_Interp *interp)
{
  Tcl_HashEntry *entry;
  ClientData temp;
  int (*command)(ClientData, Tcl_Interp *,int, char *[]);
  char *args[3];
  char temps[256];

  /* set up the args */
  args[0] = "DoTypecasting";
  args[1] = result_type;
  args[2] = NULL;

  // object names cannot start with a number
  if ((name[0] >= '0')&&(name[0] <= '9'))
    {
    return NULL;
    }
  if ((entry = Tcl_FindHashEntry(&vtkInstanceLookup,name)))
    {
    temp = (ClientData)Tcl_GetHashValue(entry);
    }
  else
    {
    sprintf(temps,"vtk bad argument, could not find object named %s\n", name);
    Tcl_AppendResult(interp,temps,NULL);
    return NULL;
    }

  /* now handle the typecasting, get the command proc */
  if ((entry = Tcl_FindHashEntry(&vtkCommandLookup,name)))
    {
    command = (int (*)(ClientData,Tcl_Interp *,int,char *[]))Tcl_GetHashValue(entry);
    }
  else
    {
    sprintf(temps,"vtk bad argument, could not find command process for %s.\n", name);
    Tcl_AppendResult(interp,temps,NULL);
    return NULL;
    }

  if (command(temp,(Tcl_Interp *)NULL,3,args) == TCL_OK)
    {
    return (void *)(args[2]);
    }
  else
    {
    sprintf(temps,"vtk bad argument, type conversion failed for object %s.\nCould not type convert %s to type %s.\n", name, name, result_type);
    Tcl_AppendResult(interp,temps,NULL);
    return NULL;
    }

}

void vtkTclVoidFunc(void *arg)
{
  int res;

  vtkTclVoidFuncArg *arg2;

  arg2 = (vtkTclVoidFuncArg *)arg;

  res = Tcl_GlobalEval(arg2->interp, arg2->command);

  if (res == TCL_ERROR)
    {
    if (Tcl_GetVar(arg2->interp,"errorInfo",0))
      {
      vtkGenericWarningMacro("Error returned from vtk/tcl callback:\n" <<
			     Tcl_GetVar(arg2->interp,"errorInfo",0) <<
			     " at line number " << arg2->interp->errorLine);
      }
    else
      {
      vtkGenericWarningMacro("Error returned from vtk/tcl callback:\n" <<
			     " at line number " << arg2->interp->errorLine);
      }
    }
}

void vtkTclVoidFuncArgDelete(void *arg)
{
  vtkTclVoidFuncArg *arg2;

  arg2 = (vtkTclVoidFuncArg *)arg;

  // free the string and then structure
  delete [] arg2->command;
  delete arg2;
}

void vtkTclListInstances(Tcl_Interp *interp, ClientData arg)
{
  Tcl_HashSearch srch;
  Tcl_HashEntry *entry;
  int first = 1;

  // iteratively search hash table for command function
  entry = Tcl_FirstHashEntry(&vtkCommandLookup, &srch);
  if (!entry)
    {
    interp->result[0] = '\0';
    return;
    }
  while (entry)
    {
    if (Tcl_GetHashValue(entry) == arg)
      {
      if (first)
	{
	first = 0;
	Tcl_AppendResult(interp,Tcl_GetHashKey(&vtkCommandLookup,entry),NULL);
	}
      else
	{
	Tcl_AppendResult(interp, " ", Tcl_GetHashKey(&vtkCommandLookup,entry),
			 NULL);
	}
      }
    entry = Tcl_NextHashEntry(&srch);
    }
}

<|endoftext|>


<commit_before><commit_msg>fix sftp unlink error<commit_after><|endoftext|>


<commit_before>//===----------------------------------------------------------------------===//
//
//                         PelotonDB
//
// nested_loop_join_executor.cpp
//
// Identification: src/backend/executor/nested_loop_join_executor.cpp
//
// Copyright (c) 2015, Carnegie Mellon University Database Group
//
//===----------------------------------------------------------------------===//

#include <vector>
#include <unordered_set>

#include "backend/common/types.h"
#include "backend/common/logger.h"
#include "backend/executor/logical_tile_factory.h"
#include "backend/executor/nested_loop_join_executor.h"
#include "backend/expression/abstract_expression.h"
#include "backend/expression/container_tuple.h"

namespace peloton {
namespace executor {

/**
 * @brief Constructor for nested loop join executor.
 * @param node Nested loop join node corresponding to this executor.
 */
NestedLoopJoinExecutor::NestedLoopJoinExecutor(
    const planner::AbstractPlan *node, ExecutorContext *executor_context)
    : AbstractJoinExecutor(node, executor_context) {}

/**
 * @brief Do some basic checks and create the schema for the output logical
 * tiles.
 * @return true on success, false otherwise.
 */
bool NestedLoopJoinExecutor::DInit() {
  auto status = AbstractJoinExecutor::DInit();
  if (status == false) {
    return status;
  }

  assert(right_result_tiles_.empty());
  right_child_done_ = false;
  right_result_itr_ = 0;

  assert(left_result_tiles_.empty());

  return true;
}

/**
 * @brief Creates logical tiles from the two input logical tiles after applying
 * join predicate.
 * @return true on success, false otherwise.
 */
bool NestedLoopJoinExecutor::DExecute() {
  LOG_INFO("********** Nested Loop %s Join executor :: 2 children \n", GetJoinTypeString());

  for(;;){  // Loop until we have non-empty result tile or exit

    LogicalTile* left_tile = nullptr;
    LogicalTile* right_tile = nullptr;

    bool advance_left_child = false;

    if(right_child_done_){  // If we have already retrieved all right child's results in buffer
      LOG_TRACE("Advance the right buffer iterator.");
      assert(!left_result_tiles_.empty());
      assert(!right_result_tiles_.empty());
      right_result_itr_++;
      if(right_result_itr_ >= right_result_tiles_.size()){
        advance_left_child = true;
        right_result_itr_ = 0;
      }
    }
    else { // Otherwise, we must attempt to execute the right child
      if(false == children_[1]->Execute()){
        // right child is finished, no more tiles
        LOG_TRACE("My right child is exhausted.");
        if(right_result_tiles_.empty()){
          assert(left_result_tiles_.empty());
          LOG_TRACE("Right child returns nothing totally. Exit.");
          return false;
        }
        right_child_done_ = true;
        right_result_itr_ = 0;
        advance_left_child = true;
      }
      else { // Buffer the right child's result
        LOG_TRACE("Retrieve a new tile from right child");
        right_result_tiles_.push_back(children_[1]->GetOutput());
        right_result_itr_ = right_result_tiles_.size() - 1;
      }
    }

    if(advance_left_child || left_result_tiles_.empty()){
      assert(0 == right_result_itr_);
      // Need to advance the left child
      if(false == children_[0]->Execute()){
        LOG_TRACE("Left child is exhausted. Returning false.");
        // Left child exhausted.
        // The whole executor is done.
        // Release cur left tile. Clear right child's result buffer and return.
        assert(right_result_tiles_.size() > 0);
        return false;
      }
      else{
        LOG_TRACE("Advance the left child.");
        // Insert left child's result to buffer
        left_result_tiles_.push_back(children_[0]->GetOutput());
      }
    }

    left_tile = left_result_tiles_.back();
    right_tile = right_result_tiles_[right_result_itr_];

    // Check the input logical tiles.
    assert(left_tile != nullptr);
    assert(right_tile != nullptr);

    // Construct output logical tile.
    std::unique_ptr<LogicalTile> output_tile(LogicalTileFactory::GetTile());

    auto left_tile_schema = left_tile->GetSchema();
    auto right_tile_schema = right_tile->GetSchema();

    for (auto &col : right_tile_schema) {
      col.position_list_idx += left_tile->GetPositionLists().size();
    }

    /* build the schema given the projection */
    auto output_tile_schema = BuildSchema(left_tile_schema, right_tile_schema);

    // Set the output logical tile schema
    output_tile->SetSchema(std::move(output_tile_schema));
    // Now, let's compute the position lists for the output tile

    // Cartesian product

    // Add everything from two logical tiles
    auto left_tile_position_lists = left_tile->GetPositionLists();
    auto right_tile_position_lists = right_tile->GetPositionLists();

    // Compute output tile column count
    size_t left_tile_column_count = left_tile_position_lists.size();
    size_t right_tile_column_count = right_tile_position_lists.size();
    size_t output_tile_column_count =
        left_tile_column_count + right_tile_column_count;

    assert(left_tile_column_count > 0);
    assert(right_tile_column_count > 0);

    // Construct position lists for output tile
    // TODO: We don't have to copy position lists for each column,
    // as there are likely duplications of them.
    // But must pay attention to the output schema (see how it is constructed!)
    std::vector<std::vector<oid_t>> position_lists;
    for (size_t column_itr = 0; column_itr < output_tile_column_count;
         column_itr++)
      position_lists.push_back(std::vector<oid_t>());

    LOG_TRACE("left col count: %lu, right col count: %lu", left_tile_column_count,
              right_tile_column_count);
    LOG_TRACE("left col count: %lu, right col count: %lu",
              left_tile->GetColumnCount(),
              right_tile->GetColumnCount());
    LOG_TRACE("left row count: %lu, right row count: %lu", left_tile_row_count,
              right_tile_row_count);

    unsigned int removed = 0;
    // Go over every pair of tuples in left and right logical tiles

    // Right Join, Outer Join
    // this set contains row id in right tile that none of the row in left tile matches
    // this set is initialized with all ids in right tile and
    // as the nested loop goes, id in the set are removed if a match is made,
    // After nested looping, ids left are rows with no matching.
    std::unordered_set<oid_t> no_match_rows;
    // only initialize if we are doing right or outer join
    if (join_type_ == JOIN_TYPE_RIGHT || join_type_ == JOIN_TYPE_OUTER) {
      no_match_rows.insert(right_tile->begin(), right_tile->end());
    }
    for(auto left_tile_row_itr : *left_tile){
      bool has_right_match = false;
      for(auto right_tile_row_itr : *right_tile){
        // TODO: OPTIMIZATION : Can split the control flow into two paths -
        // one for Cartesian product and one for join
        // Then, we can skip this branch atleast for the Cartesian product path.

        // Join predicate exists
        if (predicate_ != nullptr) {
          expression::ContainerTuple<executor::LogicalTile> left_tuple(
              left_tile, left_tile_row_itr);
          expression::ContainerTuple<executor::LogicalTile> right_tuple(
              right_tile, right_tile_row_itr);

          // Join predicate is false. Skip pair and continue.
          if (predicate_->Evaluate(&left_tuple, &right_tuple, executor_context_)
                  .IsFalse()) {
            removed++;
            continue;
          }
        }

        // Right Outer Join, Full Outer Join:
        // Remove a matched right row
        if (join_type_ == JOIN_TYPE_RIGHT || join_type_ == JOIN_TYPE_OUTER) {
          no_match_rows.erase(right_tile_row_itr);
        }
        // Left Outer Join, Full Outer Join:
        has_right_match = true;

        // Insert a tuple into the output logical tile
        // First, copy the elements in left logical tile's tuple
        for (size_t output_tile_column_itr = 0;
             output_tile_column_itr < left_tile_column_count;
             output_tile_column_itr++) {
          position_lists[output_tile_column_itr].push_back(
              left_tile_position_lists[output_tile_column_itr]
                                      [left_tile_row_itr]);
        }

        // Then, copy the elements in right logical tile's tuple
        for (size_t output_tile_column_itr = 0;
             output_tile_column_itr < right_tile_column_count;
             output_tile_column_itr++) {
          position_lists[left_tile_column_count + output_tile_column_itr]
              .push_back(right_tile_position_lists[output_tile_column_itr]
                                                  [right_tile_row_itr]);
        }
      } // inner loop of NLJ

      // Left Outer Join, Full Outer Join:
      if ((join_type_ == JOIN_TYPE_LEFT || join_type_ == JOIN_TYPE_OUTER) && !has_right_match) {
        LOG_INFO("Left or ful outer: Null row, left id %lu", left_tile_row_itr);
        // no right tuple matched, if we are doing left outer join or full outer join
        // we should also emit a tuple in which right parts are null
         for (size_t output_tile_column_itr = 0;
             output_tile_column_itr < left_tile_column_count;
             output_tile_column_itr++) {
          position_lists[output_tile_column_itr].push_back(
              left_tile_position_lists[output_tile_column_itr]
                                      [left_tile_row_itr]);
        }

        // Then, copy the elements in right logical tile's tuple
        for (size_t output_tile_column_itr = 0;
             output_tile_column_itr < right_tile_column_count;
             output_tile_column_itr++) {
          position_lists[left_tile_column_count + output_tile_column_itr]
              .push_back(NULL_OID);
        }
      }
    } // outer loop of NLJ

    // Right Outer Join, Full Outer Join:
    // For each row in right tile
    // it it has no match in left, we should emit a row whose left parts
    // are null
    if (join_type_ == JOIN_TYPE_RIGHT || join_type_ == JOIN_TYPE_OUTER) {
      for (auto left_null_row_itr : no_match_rows) {
        LOG_INFO("right or full outer: Null row, right id %lu", left_null_row_itr);
        for (size_t output_tile_column_itr = 0;
             output_tile_column_itr < left_tile_column_count;
             output_tile_column_itr++) {
          position_lists[output_tile_column_itr].push_back(NULL_OID);
        }

        for (size_t output_tile_column_itr = 0;
             output_tile_column_itr < right_tile_column_count;
             output_tile_column_itr++) {
          position_lists[left_tile_column_count + output_tile_column_itr]
              .push_back(right_tile_position_lists[output_tile_column_itr][left_null_row_itr]);
        }
      }
    }

    LOG_INFO("Predicate removed %d rows", removed);

    // Check if we have any matching tuples.
    if (position_lists[0].size() > 0) {
      output_tile->SetPositionListsAndVisibility(std::move(position_lists));
      SetOutput(output_tile.release());
      return true;
    }

    LOG_TRACE("This pair produces empty join result. Loop.");
  } // End large for-loop

}

NestedLoopJoinExecutor::~NestedLoopJoinExecutor(){

  for(auto tile : left_result_tiles_){
    delete tile;
    left_result_tiles_.clear();
  }

  for(auto tile : right_result_tiles_){
    delete tile;
    right_result_tiles_.clear();
  }

}

}  // namespace executor
}  // namespace peloton
<commit_msg>Minor changes<commit_after>//===----------------------------------------------------------------------===//
//
//                         PelotonDB
//
// nested_loop_join_executor.cpp
//
// Identification: src/backend/executor/nested_loop_join_executor.cpp
//
// Copyright (c) 2015, Carnegie Mellon University Database Group
//
//===----------------------------------------------------------------------===//

#include <vector>
#include <unordered_set>

#include "backend/common/types.h"
#include "backend/common/logger.h"
#include "backend/executor/logical_tile_factory.h"
#include "backend/executor/nested_loop_join_executor.h"
#include "backend/expression/abstract_expression.h"
#include "backend/expression/container_tuple.h"

namespace peloton {
namespace executor {

/**
 * @brief Constructor for nested loop join executor.
 * @param node Nested loop join node corresponding to this executor.
 */
NestedLoopJoinExecutor::NestedLoopJoinExecutor(
    const planner::AbstractPlan *node, ExecutorContext *executor_context)
    : AbstractJoinExecutor(node, executor_context) {
}

/**
 * @brief Do some basic checks and create the schema for the output logical
 * tiles.
 * @return true on success, false otherwise.
 */
bool NestedLoopJoinExecutor::DInit() {
  auto status = AbstractJoinExecutor::DInit();
  if (status == false) {
    return status;
  }

  assert(right_result_tiles_.empty());
  right_child_done_ = false;
  right_result_itr_ = 0;

  assert(left_result_tiles_.empty());

  return true;
}

/**
 * @brief Creates logical tiles from the two input logical tiles after applying
 * join predicate.
 * @return true on success, false otherwise.
 */
bool NestedLoopJoinExecutor::DExecute() {
  LOG_INFO("********** Nested Loop %s Join executor :: 2 children \n",
           GetJoinTypeString());

  for (;;) {  // Loop until we have non-empty result tile or exit

    LogicalTile* left_tile = nullptr;
    LogicalTile* right_tile = nullptr;

    bool advance_left_child = false;

    if (right_child_done_) {  // If we have already retrieved all right child's results in buffer
      LOG_TRACE("Advance the right buffer iterator.");
      assert(!left_result_tiles_.empty());
      assert(!right_result_tiles_.empty());
      right_result_itr_++;
      if (right_result_itr_ >= right_result_tiles_.size()) {
        advance_left_child = true;
        right_result_itr_ = 0;
      }
    } else {  // Otherwise, we must attempt to execute the right child
      if (false == children_[1]->Execute()) {
        // right child is finished, no more tiles
        LOG_TRACE("My right child is exhausted.");
        if (right_result_tiles_.empty()) {
          assert(left_result_tiles_.empty());
          LOG_TRACE("Right child returns nothing totally. Exit.");
          return false;
        }
        right_child_done_ = true;
        right_result_itr_ = 0;
        advance_left_child = true;
      } else {  // Buffer the right child's result
        LOG_TRACE("Retrieve a new tile from right child");
        right_result_tiles_.push_back(children_[1]->GetOutput());
        right_result_itr_ = right_result_tiles_.size() - 1;
      }
    }

    if (advance_left_child || left_result_tiles_.empty()) {
      assert(0 == right_result_itr_);
      // Need to advance the left child
      if (false == children_[0]->Execute()) {
        LOG_TRACE("Left child is exhausted. Returning false.");
        // Left child exhausted.
        // The whole executor is done.
        // Release cur left tile. Clear right child's result buffer and return.
        assert(right_result_tiles_.size() > 0);
        return false;
      } else {
        LOG_TRACE("Advance the left child.");
        // Insert left child's result to buffer
        left_result_tiles_.push_back(children_[0]->GetOutput());
      }
    }

    left_tile = left_result_tiles_.back();
    right_tile = right_result_tiles_[right_result_itr_];

    // Check the input logical tiles.
    assert(left_tile != nullptr);
    assert(right_tile != nullptr);

    // Construct output logical tile.
    std::unique_ptr<LogicalTile> output_tile(LogicalTileFactory::GetTile());

    auto left_tile_schema = left_tile->GetSchema();
    auto right_tile_schema = right_tile->GetSchema();

    for (auto &col : right_tile_schema) {
      col.position_list_idx += left_tile->GetPositionLists().size();
    }

    /* build the schema given the projection */
    auto output_tile_schema = BuildSchema(left_tile_schema, right_tile_schema);

    // Set the output logical tile schema
    output_tile->SetSchema(std::move(output_tile_schema));
    // Now, let's compute the position lists for the output tile

    // Cartesian product

    // Add everything from two logical tiles
    auto left_tile_position_lists = left_tile->GetPositionLists();
    auto right_tile_position_lists = right_tile->GetPositionLists();

    // Compute output tile column count
    size_t left_tile_column_count = left_tile_position_lists.size();
    size_t right_tile_column_count = right_tile_position_lists.size();
    size_t output_tile_column_count = left_tile_column_count
        + right_tile_column_count;

    assert(left_tile_column_count > 0);
    assert(right_tile_column_count > 0);

    // Construct position lists for output tile
    // TODO: We don't have to copy position lists for each column,
    // as there are likely duplications of them.
    // But must pay attention to the output schema (see how it is constructed!)
    std::vector<std::vector<oid_t>> position_lists;
    for (size_t column_itr = 0; column_itr < output_tile_column_count;
        column_itr++)
      position_lists.push_back(std::vector<oid_t>());

    LOG_TRACE("left col count: %lu, right col count: %lu",
              left_tile_column_count, right_tile_column_count);
    LOG_TRACE("left col count: %lu, right col count: %lu",
              left_tile->GetColumnCount(), right_tile->GetColumnCount());
    LOG_TRACE("left row count: %lu, right row count: %lu", left_tile_row_count,
              right_tile_row_count);

    unsigned int removed = 0;

    // Go over every pair of tuples in left and right logical tiles

    // Right Join, Outer Join
    // this set contains row id in right tile that none of the row in left tile matches
    // this set is initialized with all ids in right tile and
    // as the nested loop goes, id in the set are removed if a match is made,
    // After nested looping, ids left are rows with no matching.
    std::unordered_set<oid_t> no_match_rows;

    // only initialize if we are doing right or outer join
    if (join_type_ == JOIN_TYPE_RIGHT || join_type_ == JOIN_TYPE_OUTER) {
      no_match_rows.insert(right_tile->begin(), right_tile->end());
    }

    for (auto left_tile_row_itr : *left_tile) {
      bool has_right_match = false;

      for (auto right_tile_row_itr : *right_tile) {
        // TODO: OPTIMIZATION : Can split the control flow into two paths -
        // one for Cartesian product and one for join
        // Then, we can skip this branch atleast for the Cartesian product path.

        // Join predicate exists
        if (predicate_ != nullptr) {
          expression::ContainerTuple<executor::LogicalTile> left_tuple(
              left_tile, left_tile_row_itr);
          expression::ContainerTuple<executor::LogicalTile> right_tuple(
              right_tile, right_tile_row_itr);

          // Join predicate is false. Skip pair and continue.
          if (predicate_->Evaluate(&left_tuple, &right_tuple, executor_context_)
              .IsFalse()) {
            removed++;
            continue;
          }
        }

        // Right Outer Join, Full Outer Join:
        // Remove a matched right row
        if (join_type_ == JOIN_TYPE_RIGHT || join_type_ == JOIN_TYPE_OUTER) {
          no_match_rows.erase(right_tile_row_itr);
        }

        // Left Outer Join, Full Outer Join:
        has_right_match = true;

        // Insert a tuple into the output logical tile
        // First, copy the elements in left logical tile's tuple
        for (size_t output_tile_column_itr = 0;
            output_tile_column_itr < left_tile_column_count;
            output_tile_column_itr++) {
          position_lists[output_tile_column_itr].push_back(
              left_tile_position_lists[output_tile_column_itr][left_tile_row_itr]);
        }

        // Then, copy the elements in right logical tile's tuple
        for (size_t output_tile_column_itr = 0;
            output_tile_column_itr < right_tile_column_count;
            output_tile_column_itr++) {
          position_lists[left_tile_column_count + output_tile_column_itr]
              .push_back(
              right_tile_position_lists[output_tile_column_itr][right_tile_row_itr]);
        }
      }  // inner loop of NLJ

      // Left Outer Join, Full Outer Join:
      if ((join_type_ == JOIN_TYPE_LEFT || join_type_ == JOIN_TYPE_OUTER)
          && !has_right_match) {
        LOG_INFO("Left or ful outer: Null row, left id %lu", left_tile_row_itr);
        // no right tuple matched, if we are doing left outer join or full outer join
        // we should also emit a tuple in which right parts are null
        for (size_t output_tile_column_itr = 0;
            output_tile_column_itr < left_tile_column_count;
            output_tile_column_itr++) {
          position_lists[output_tile_column_itr].push_back(
              left_tile_position_lists[output_tile_column_itr][left_tile_row_itr]);
        }

        // Then, copy the elements in right logical tile's tuple
        for (size_t output_tile_column_itr = 0;
            output_tile_column_itr < right_tile_column_count;
            output_tile_column_itr++) {
          position_lists[left_tile_column_count + output_tile_column_itr]
              .push_back(NULL_OID);
        }
      }
    }  // outer loop of NLJ

    // Right Outer Join, Full Outer Join:
    // For each row in right tile
    // it it has no match in left, we should emit a row whose left parts
    // are null
    if (join_type_ == JOIN_TYPE_RIGHT || join_type_ == JOIN_TYPE_OUTER) {
      for (auto left_null_row_itr : no_match_rows) {
        LOG_INFO("right or full outer: Null row, right id %lu",
                 left_null_row_itr);
        for (size_t output_tile_column_itr = 0;
            output_tile_column_itr < left_tile_column_count;
            output_tile_column_itr++) {
          position_lists[output_tile_column_itr].push_back(NULL_OID);
        }

        for (size_t output_tile_column_itr = 0;
            output_tile_column_itr < right_tile_column_count;
            output_tile_column_itr++) {
          position_lists[left_tile_column_count + output_tile_column_itr]
              .push_back(
              right_tile_position_lists[output_tile_column_itr][left_null_row_itr]);
        }
      }
    }

    LOG_INFO("Predicate removed %d rows", removed);

    // Check if we have any matching tuples.
    if (position_lists[0].size() > 0) {
      output_tile->SetPositionListsAndVisibility(std::move(position_lists));
      SetOutput(output_tile.release());
      return true;
    }

    LOG_TRACE("This pair produces empty join result. Loop.");
  }  // End large for-loop

}

NestedLoopJoinExecutor::~NestedLoopJoinExecutor() {

  for (auto tile : left_result_tiles_) {
    delete tile;
    left_result_tiles_.clear();
  }

  for (auto tile : right_result_tiles_) {
    delete tile;
    right_result_tiles_.clear();
  }

}

}  // namespace executor
}  // namespace peloton
<|endoftext|>


<commit_before>#include "it.h"
#include "debug.h"
#include "threads.h"
#include "rewriter.h"
#define STACK_SIZE 1024

struct re2jit::native
{
    re2::Prog *_prog;

    rejit_entry_t entry() const
    {
        return _prog->start();
    }

    int run(struct rejit_threadset_t *nfa) const
    {
        ssize_t stack[STACK_SIZE];
        ssize_t stkid;

        ssize_t restore[STACK_SIZE];
        ssize_t rstid;

        while (rejit_thread_dispatch(nfa)) {
            struct rejit_thread_t *t = nfa->running;

            stkid = rstid = 0;
            stack[stkid++] = t->entry;

            while (stkid--) {
                ssize_t i = stack[stkid];

                if (i < 0) {
                    t->groups[-i] = restore[--rstid];
                    continue;
                }

                if (nfa->visited[i / 8] & (1 << (i % 8))) {
                    continue;
                }

                nfa->visited[i / 8] |= 1 << (i % 8);

                RE2JIT_WITH_INST(op, _prog, i,
                    switch (op.opcode) {
                        case re2jit::opcode::kUnicodeLetter:
                        case re2jit::opcode::kUnicodeNumber: {
                            rejit_uni_char_t c;

                            int len = rejit_read_utf8((const uint8_t *) nfa->input, nfa->length, &c);
                            if (len == -1)
                                // not a valid utf-8 character
                                break;

                            // TODO check the class of `c`
                            rejit_thread_wait(nfa, op.out, len);
                        }

                        default:
                            re2jit::debug::write("re2jit::vm: unknown extcode %hu\n", op.opcode);
                            break;
                    },

                    switch (op->opcode()) {
                        case re2::kInstAltMatch:
                        case re2::kInstAlt:
                            stack[stkid++] = op->out1();
                            stack[stkid++] = op->out();
                            break;

                        case re2::kInstByteRange: {
                            if (!nfa->length) {
                                break;
                            }

                            uint8_t c = nfa->input[0];

                            if (op->foldcase() && 'A' <= c && c <= 'Z') {
                                c += 'a' - 'A';
                            }

                            if (c < op->lo() || c > op->hi()) {
                                break;
                            }

                            rejit_thread_wait(nfa, op->out(), 1);
                            break;
                        }

                        case re2::kInstCapture:
                            if ((size_t) op->cap() < nfa->groups) {
                                restore[rstid++] = t->groups[op->cap()];
                                stack[stkid++] = -op->cap();

                                t->groups[op->cap()] = nfa->offset;
                            }

                            stack[stkid++] = op->out();
                            break;

                        case re2::kInstEmptyWidth:
                            if (!(op->empty() & ~(nfa->empty))) {
                                stack[stkid++] = op->out();
                            }
                            break;

                        case re2::kInstNop:
                            stack[stkid++] = op->out();
                            break;

                        case re2::kInstMatch:
                            if (rejit_thread_match(nfa)) {
                                // this match is preferred to whatever is still on stack.
                                stkid = 0;
                            }

                            break;

                        case re2::kInstFail:
                            break;

                        default:
                            re2jit::debug::write("re2jit::vm: unknown opcode %d\n", op->opcode());
                            return 0;
                    }
                );
            }
        }

        return 1;
    }
};
<commit_msg>Add a missing `break`.<commit_after>#include "it.h"
#include "debug.h"
#include "threads.h"
#include "rewriter.h"
#define STACK_SIZE 1024

struct re2jit::native
{
    re2::Prog *_prog;

    rejit_entry_t entry() const
    {
        return _prog->start();
    }

    int run(struct rejit_threadset_t *nfa) const
    {
        ssize_t stack[STACK_SIZE];
        ssize_t stkid;

        ssize_t restore[STACK_SIZE];
        ssize_t rstid;

        while (rejit_thread_dispatch(nfa)) {
            struct rejit_thread_t *t = nfa->running;

            stkid = rstid = 0;
            stack[stkid++] = t->entry;

            while (stkid--) {
                ssize_t i = stack[stkid];

                if (i < 0) {
                    t->groups[-i] = restore[--rstid];
                    continue;
                }

                if (nfa->visited[i / 8] & (1 << (i % 8))) {
                    continue;
                }

                nfa->visited[i / 8] |= 1 << (i % 8);

                RE2JIT_WITH_INST(op, _prog, i,
                    switch (op.opcode) {
                        case re2jit::opcode::kUnicodeLetter:
                        case re2jit::opcode::kUnicodeNumber: {
                            rejit_uni_char_t c;

                            int len = rejit_read_utf8((const uint8_t *) nfa->input, nfa->length, &c);
                            if (len == -1)
                                // not a valid utf-8 character
                                break;

                            // TODO check the class of `c`
                            rejit_thread_wait(nfa, op.out, len);
                            break;
                        }

                        default:
                            re2jit::debug::write("re2jit::vm: unknown extcode %hu\n", op.opcode);
                            break;
                    },

                    switch (op->opcode()) {
                        case re2::kInstAltMatch:
                        case re2::kInstAlt:
                            stack[stkid++] = op->out1();
                            stack[stkid++] = op->out();
                            break;

                        case re2::kInstByteRange: {
                            if (!nfa->length) {
                                break;
                            }

                            uint8_t c = nfa->input[0];

                            if (op->foldcase() && 'A' <= c && c <= 'Z') {
                                c += 'a' - 'A';
                            }

                            if (c < op->lo() || c > op->hi()) {
                                break;
                            }

                            rejit_thread_wait(nfa, op->out(), 1);
                            break;
                        }

                        case re2::kInstCapture:
                            if ((size_t) op->cap() < nfa->groups) {
                                restore[rstid++] = t->groups[op->cap()];
                                stack[stkid++] = -op->cap();

                                t->groups[op->cap()] = nfa->offset;
                            }

                            stack[stkid++] = op->out();
                            break;

                        case re2::kInstEmptyWidth:
                            if (!(op->empty() & ~(nfa->empty))) {
                                stack[stkid++] = op->out();
                            }
                            break;

                        case re2::kInstNop:
                            stack[stkid++] = op->out();
                            break;

                        case re2::kInstMatch:
                            if (rejit_thread_match(nfa)) {
                                // this match is preferred to whatever is still on stack.
                                stkid = 0;
                            }

                            break;

                        case re2::kInstFail:
                            break;

                        default:
                            re2jit::debug::write("re2jit::vm: unknown opcode %d\n", op->opcode());
                            return 0;
                    }
                );
            }
        }

        return 1;
    }
};
<|endoftext|>


<commit_before>/**
 * Copyright © 2016 IBM Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include <algorithm>
#include <cerrno>
#include <cstdlib>
#include <experimental/filesystem>
#include <fstream>
#include <memory>
#include <thread>
#include "sysfs.hpp"

using namespace std::string_literals;
namespace fs = std::experimental::filesystem;

namespace sysfs {

static constexpr auto retryableErrors = {
    /*
     * Retry on bus or device errors or timeouts in case
     * they are transient.
     */
    EIO,
    ETIMEDOUT,

    /*
     * Retry CRC errors.
     */
    EBADMSG,

    /*
     * Some hwmon drivers do this when they aren't ready
     * instead of blocking.  Retry.
     */
    EAGAIN,
    /*
     * We'll see this when for example i2c devices are
     * unplugged but the driver is still bound.  Retry
     * rather than exit on the off chance the device is
     * plugged back in and the driver doesn't do a
     * remove/probe.  If a remove does occur, we'll
     * eventually get ENOENT.
     */
    ENXIO,
};

static const auto emptyString = ""s;
static constexpr auto ofRoot = "/sys/firmware/devicetree/base";

std::string findPhandleMatch(
        const std::string& iochanneldir,
        const std::string& phandledir)
{
    // TODO: At the moment this method only supports device trees
    // with iio-hwmon nodes with a single sensor.  Typically
    // device trees are defined with all the iio sensors in a
    // single iio-hwmon node so it would be nice to add support
    // for lists of phandles (with variable sized entries) via
    // libfdt or something like that, so that users are not
    // forced into implementing unusual looking device trees
    // with multiple iio-hwmon nodes - one for each sensor.

    fs::path ioChannelsPath{iochanneldir};
    ioChannelsPath /= "io-channels";

    if (!fs::exists(ioChannelsPath))
    {
        return emptyString;
    }

    uint32_t ioChannelsValue;
    std::ifstream ioChannelsFile(ioChannelsPath);

    ioChannelsFile.read(
            reinterpret_cast<char*>(&ioChannelsValue),
            sizeof(ioChannelsValue));

    for (const auto& ofInst : fs::recursive_directory_iterator(phandledir))
    {
        auto path = ofInst.path();
        if ("phandle" != path.filename())
        {
            continue;
        }
        std::ifstream pHandleFile(path);
        uint32_t pHandleValue;

        pHandleFile.read(
                reinterpret_cast<char*>(&pHandleValue),
                sizeof(pHandleValue));

        if (ioChannelsValue == pHandleValue)
        {
            return path;
        }
    }

    return emptyString;
}

std::string findCalloutPath(const std::string& instancePath)
{
    // Follow the hwmon instance (/sys/class/hwmon/hwmon<N>)
    // /sys/devices symlink.
    fs::path devPath{instancePath};
    devPath /= "device";

    try
    {
        devPath = fs::canonical(devPath);
    }
    catch (const std::system_error& e)
    {
        return emptyString;
    }

    // See if the device is backed by the iio-hwmon driver.
    fs::path p{devPath};
    p /= "driver";
    p = fs::canonical(p);

    if (p.filename() != "iio_hwmon")
    {
        // Not backed by iio-hwmon.  The device pointed to
        // is the callout device.
        return devPath;
    }

    // Find the DT path to the iio-hwmon platform device.
    fs::path ofDevPath{devPath};
    ofDevPath /= "of_node";

    try
    {
        ofDevPath = fs::canonical(ofDevPath);
    }
    catch (const std::system_error& e)
    {
        return emptyString;
    }

    // Search /sys/bus/iio/devices for the phandle in io-channels.
    // If a match is found, use the corresponding /sys/devices
    // iio device as the callout device.
    static constexpr auto iioDevices = "/sys/bus/iio/devices";
    for (const auto& iioDev: fs::recursive_directory_iterator(iioDevices))
    {
        p = iioDev.path();
        p /= "of_node";

        try
        {
            p = fs::canonical(p);
        }
        catch (const std::system_error& e)
        {
            continue;
        }

        auto match = findPhandleMatch(ofDevPath, p);
        auto n = match.rfind('/');
        if (n != std::string::npos)
        {
            // This is the iio device referred to by io-channels.
            // Remove iio:device<N>.
            try
            {
                return fs::canonical(iioDev).parent_path();
            }
            catch (const std::system_error& e)
            {
                return emptyString;
            }
        }
    }

    return emptyString;
}

std::string findHwmon(const std::string& ofNode)
{
    static constexpr auto hwmonRoot = "/sys/class/hwmon";

    fs::path fullOfPath{ofRoot};
    fullOfPath /= ofNode;

    for (const auto& hwmonInst : fs::directory_iterator(hwmonRoot))
    {
        auto path = hwmonInst.path();
        path /= "of_node";

        try
        {
            path = fs::canonical(path);
        }
        catch (const std::system_error& e)
        {
            // realpath may encounter ENOENT (Hwmon
            // instances have a nasty habit of
            // going away without warning).
            continue;
        }

        if (path == fullOfPath)
        {
            return hwmonInst.path();
        }

        // Try to find HWMON instance via phandle values.
        // Used for IIO device drivers.
        auto matchpath = findPhandleMatch(path, fullOfPath);
        if (!matchpath.empty())
        {
            return hwmonInst.path();
        }
    }

    return emptyString;
}

namespace hwmonio
{

HwmonIO::HwmonIO(const std::string& path) : p(path)
{

}

uint32_t HwmonIO::read(
        const std::string& type,
        const std::string& id,
        const std::string& sensor,
        size_t retries,
        std::chrono::milliseconds delay) const
{
    uint32_t val;
    std::ifstream ifs;
    auto fullPath = sysfs::make_sysfs_path(
            p, type, id, sensor);

    ifs.exceptions(
            std::ifstream::failbit |
                std::ifstream::badbit |
                std::ifstream::eofbit);

    while (true)
    {
        try
        {
            if (!ifs.is_open())
                ifs.open(fullPath);
            ifs.clear();
            ifs.seekg(0);
            ifs >> val;
        }
        catch (const std::exception& e)
        {
            auto rc = errno;

            if (!rc)
            {
                throw;
            }

            if (rc == ENOENT)
            {
                // If the directory disappeared then this application should
                // gracefully exit.  There are race conditions between the
                // unloading of a hwmon driver and the stopping of this service
                // by systemd.  To prevent this application from falsely failing
                // in these scenarios, it will simply exit if the directory or
                // file can not be found.  It is up to the user(s) of this
                // provided hwmon object to log the appropriate errors if the
                // object disappears when it should not.
                exit(0);
            }

            if (0 == std::count(
                        retryableErrors.begin(),
                        retryableErrors.end(),
                        rc) ||
                    !retries)
            {
                // Not a retryable error or out of retries.

                // Work around GCC bugs 53984 and 66145 for callers by
                // explicitly raising system_error here.
                throw std::system_error(rc, std::generic_category());
            }

            --retries;
            std::this_thread::sleep_for(delay);
            continue;
        }
        break;
    }

    return val;
}

void HwmonIO::write(
        uint32_t val,
        const std::string& type,
        const std::string& id,
        const std::string& sensor,
        size_t retries,
        std::chrono::milliseconds delay) const

{
    std::ofstream ofs;
    auto fullPath = sysfs::make_sysfs_path(
            p, type, id, sensor);

    ofs.exceptions(
            std::ofstream::failbit |
                std::ofstream::badbit |
                std::ofstream::eofbit);

    // See comments in the read method for an explanation of the odd exception
    // handling behavior here.

    while (true)
    {
        try
        {
            if (!ofs.is_open())
                ofs.open(fullPath);
            ofs.clear();
            ofs.seekp(0);
            ofs << val;
            ofs.flush();
        }
        catch (const std::exception& e)
        {
            auto rc = errno;

            if (!rc)
            {
                throw;
            }

            if (rc == ENOENT)
            {
                exit(0);
            }

            if (0 == std::count(
                        retryableErrors.begin(),
                        retryableErrors.end(),
                        rc) ||
                    !retries)
            {
                // Not a retryable error or out of retries.

                // Work around GCC bugs 53984 and 66145 for callers by
                // explicitly raising system_error here.
                throw std::system_error(rc, std::generic_category());
            }

            --retries;
            std::this_thread::sleep_for(delay);
            continue;
        }
        break;
    }
}

std::string HwmonIO::path() const
{
    return p;
}

} // namespace hwmonio
}
// vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
<commit_msg>Retry on ENODEV errnos<commit_after>/**
 * Copyright © 2016 IBM Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include <algorithm>
#include <cerrno>
#include <cstdlib>
#include <experimental/filesystem>
#include <fstream>
#include <memory>
#include <thread>
#include "sysfs.hpp"

using namespace std::string_literals;
namespace fs = std::experimental::filesystem;

namespace sysfs {

static constexpr auto retryableErrors = {
    /*
     * Retry on bus or device errors or timeouts in case
     * they are transient.
     */
    EIO,
    ETIMEDOUT,

    /*
     * Retry CRC errors.
     */
    EBADMSG,

    /*
     * Some hwmon drivers do this when they aren't ready
     * instead of blocking.  Retry.
     */
    EAGAIN,
    /*
     * We'll see this when for example i2c devices are
     * unplugged but the driver is still bound.  Retry
     * rather than exit on the off chance the device is
     * plugged back in and the driver doesn't do a
     * remove/probe.  If a remove does occur, we'll
     * eventually get ENOENT.
     */
    ENXIO,

    /*
     * We can see this from some drivers when we try to do
     * a read in the middle of them being unbound.  The
     * unbinding should complete before the retries are up
     * and kill this process.
     */
    ENODEV,
};

static const auto emptyString = ""s;
static constexpr auto ofRoot = "/sys/firmware/devicetree/base";

std::string findPhandleMatch(
        const std::string& iochanneldir,
        const std::string& phandledir)
{
    // TODO: At the moment this method only supports device trees
    // with iio-hwmon nodes with a single sensor.  Typically
    // device trees are defined with all the iio sensors in a
    // single iio-hwmon node so it would be nice to add support
    // for lists of phandles (with variable sized entries) via
    // libfdt or something like that, so that users are not
    // forced into implementing unusual looking device trees
    // with multiple iio-hwmon nodes - one for each sensor.

    fs::path ioChannelsPath{iochanneldir};
    ioChannelsPath /= "io-channels";

    if (!fs::exists(ioChannelsPath))
    {
        return emptyString;
    }

    uint32_t ioChannelsValue;
    std::ifstream ioChannelsFile(ioChannelsPath);

    ioChannelsFile.read(
            reinterpret_cast<char*>(&ioChannelsValue),
            sizeof(ioChannelsValue));

    for (const auto& ofInst : fs::recursive_directory_iterator(phandledir))
    {
        auto path = ofInst.path();
        if ("phandle" != path.filename())
        {
            continue;
        }
        std::ifstream pHandleFile(path);
        uint32_t pHandleValue;

        pHandleFile.read(
                reinterpret_cast<char*>(&pHandleValue),
                sizeof(pHandleValue));

        if (ioChannelsValue == pHandleValue)
        {
            return path;
        }
    }

    return emptyString;
}

std::string findCalloutPath(const std::string& instancePath)
{
    // Follow the hwmon instance (/sys/class/hwmon/hwmon<N>)
    // /sys/devices symlink.
    fs::path devPath{instancePath};
    devPath /= "device";

    try
    {
        devPath = fs::canonical(devPath);
    }
    catch (const std::system_error& e)
    {
        return emptyString;
    }

    // See if the device is backed by the iio-hwmon driver.
    fs::path p{devPath};
    p /= "driver";
    p = fs::canonical(p);

    if (p.filename() != "iio_hwmon")
    {
        // Not backed by iio-hwmon.  The device pointed to
        // is the callout device.
        return devPath;
    }

    // Find the DT path to the iio-hwmon platform device.
    fs::path ofDevPath{devPath};
    ofDevPath /= "of_node";

    try
    {
        ofDevPath = fs::canonical(ofDevPath);
    }
    catch (const std::system_error& e)
    {
        return emptyString;
    }

    // Search /sys/bus/iio/devices for the phandle in io-channels.
    // If a match is found, use the corresponding /sys/devices
    // iio device as the callout device.
    static constexpr auto iioDevices = "/sys/bus/iio/devices";
    for (const auto& iioDev: fs::recursive_directory_iterator(iioDevices))
    {
        p = iioDev.path();
        p /= "of_node";

        try
        {
            p = fs::canonical(p);
        }
        catch (const std::system_error& e)
        {
            continue;
        }

        auto match = findPhandleMatch(ofDevPath, p);
        auto n = match.rfind('/');
        if (n != std::string::npos)
        {
            // This is the iio device referred to by io-channels.
            // Remove iio:device<N>.
            try
            {
                return fs::canonical(iioDev).parent_path();
            }
            catch (const std::system_error& e)
            {
                return emptyString;
            }
        }
    }

    return emptyString;
}

std::string findHwmon(const std::string& ofNode)
{
    static constexpr auto hwmonRoot = "/sys/class/hwmon";

    fs::path fullOfPath{ofRoot};
    fullOfPath /= ofNode;

    for (const auto& hwmonInst : fs::directory_iterator(hwmonRoot))
    {
        auto path = hwmonInst.path();
        path /= "of_node";

        try
        {
            path = fs::canonical(path);
        }
        catch (const std::system_error& e)
        {
            // realpath may encounter ENOENT (Hwmon
            // instances have a nasty habit of
            // going away without warning).
            continue;
        }

        if (path == fullOfPath)
        {
            return hwmonInst.path();
        }

        // Try to find HWMON instance via phandle values.
        // Used for IIO device drivers.
        auto matchpath = findPhandleMatch(path, fullOfPath);
        if (!matchpath.empty())
        {
            return hwmonInst.path();
        }
    }

    return emptyString;
}

namespace hwmonio
{

HwmonIO::HwmonIO(const std::string& path) : p(path)
{

}

uint32_t HwmonIO::read(
        const std::string& type,
        const std::string& id,
        const std::string& sensor,
        size_t retries,
        std::chrono::milliseconds delay) const
{
    uint32_t val;
    std::ifstream ifs;
    auto fullPath = sysfs::make_sysfs_path(
            p, type, id, sensor);

    ifs.exceptions(
            std::ifstream::failbit |
                std::ifstream::badbit |
                std::ifstream::eofbit);

    while (true)
    {
        try
        {
            if (!ifs.is_open())
                ifs.open(fullPath);
            ifs.clear();
            ifs.seekg(0);
            ifs >> val;
        }
        catch (const std::exception& e)
        {
            auto rc = errno;

            if (!rc)
            {
                throw;
            }

            if (rc == ENOENT)
            {
                // If the directory disappeared then this application should
                // gracefully exit.  There are race conditions between the
                // unloading of a hwmon driver and the stopping of this service
                // by systemd.  To prevent this application from falsely failing
                // in these scenarios, it will simply exit if the directory or
                // file can not be found.  It is up to the user(s) of this
                // provided hwmon object to log the appropriate errors if the
                // object disappears when it should not.
                exit(0);
            }

            if (0 == std::count(
                        retryableErrors.begin(),
                        retryableErrors.end(),
                        rc) ||
                    !retries)
            {
                // Not a retryable error or out of retries.

                // Work around GCC bugs 53984 and 66145 for callers by
                // explicitly raising system_error here.
                throw std::system_error(rc, std::generic_category());
            }

            --retries;
            std::this_thread::sleep_for(delay);
            continue;
        }
        break;
    }

    return val;
}

void HwmonIO::write(
        uint32_t val,
        const std::string& type,
        const std::string& id,
        const std::string& sensor,
        size_t retries,
        std::chrono::milliseconds delay) const

{
    std::ofstream ofs;
    auto fullPath = sysfs::make_sysfs_path(
            p, type, id, sensor);

    ofs.exceptions(
            std::ofstream::failbit |
                std::ofstream::badbit |
                std::ofstream::eofbit);

    // See comments in the read method for an explanation of the odd exception
    // handling behavior here.

    while (true)
    {
        try
        {
            if (!ofs.is_open())
                ofs.open(fullPath);
            ofs.clear();
            ofs.seekp(0);
            ofs << val;
            ofs.flush();
        }
        catch (const std::exception& e)
        {
            auto rc = errno;

            if (!rc)
            {
                throw;
            }

            if (rc == ENOENT)
            {
                exit(0);
            }

            if (0 == std::count(
                        retryableErrors.begin(),
                        retryableErrors.end(),
                        rc) ||
                    !retries)
            {
                // Not a retryable error or out of retries.

                // Work around GCC bugs 53984 and 66145 for callers by
                // explicitly raising system_error here.
                throw std::system_error(rc, std::generic_category());
            }

            --retries;
            std::this_thread::sleep_for(delay);
            continue;
        }
        break;
    }
}

std::string HwmonIO::path() const
{
    return p;
}

} // namespace hwmonio
}
// vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
<|endoftext|>


<commit_before>// This file is part of the dune-gdt project:
//   http://users.dune-project.org/projects/dune-gdt
// Copyright holders: Felix Albrecht
// License: BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
//
// Contributors: Kirsten Weber

#ifndef DUNE_GDT_BASEFUNCTIONSET_INTERFACE_HH
#define DUNE_GDT_BASEFUNCTIONSET_INTERFACE_HH

#include <vector>

#include <dune/common/bartonnackmanifcheck.hh>
#include <dune/common/fvector.hh>
#include <dune/common/fmatrix.hh>

namespace Dune {
namespace GDT {

/**
 *  \brief Interface for matrix valued basis functions.
 *
 *  \note   see specialization for rangeDimCols = 1 for vector and scalar valued and basis functions.
 */
template< class Traits, class DomainFieldImp, int domainDim, class RangeFieldImp, int rangeDimRows, int rangeDim = 1 >
class BaseFunctionSetInterface;

/**
 *  \brief Interface for scalar valued basis functions.
 */
template< class Traits, class DomainFieldImp, int domainDim, class RangeFieldImp, int rangeDim >
class BaseFunctionSetInterface< Traits, DomainFieldImp, domainDim, RangeFieldImp, rangeDim, 1 >
{
public:
  typedef typename Traits::derived_type derived_type;
  typedef typename Traits::BackendType  BackendType;
  typedef typename Traits::EntityType   EntityType;

  typedef DomainFieldImp                                  DomainFieldType;
  static const unsigned int                               dimDomain = domainDim;
  typedef Dune::FieldVector< DomainFieldType, dimDomain > DomainType;
  typedef RangeFieldImp                                 RangeFieldType;
  static const unsigned int                             dimRange = rangeDim;
  static const unsigned int                             dimRangeCols = 1;
  typedef Dune::FieldVector< RangeFieldType, dimRange > RangeType;
  typedef Dune::FieldMatrix< RangeFieldType, dimRange, dimDomain > JacobianRangeType;

  const EntityType& entity() const
  {
    CHECK_INTERFACE_IMPLEMENTATION(asImp().entity());
    return asImp().entity();
  }

  const BackendType& backend() const
  {
    CHECK_INTERFACE_IMPLEMENTATION(asImp().backend());
    return asImp().backend();
  }

  size_t size() const
  {
    CHECK_INTERFACE_IMPLEMENTATION(asImp().size());
    return asImp().size();
  }

  size_t order() const
  {
    CHECK_INTERFACE_IMPLEMENTATION(asImp().order());
    return asImp().order();
  }

  void evaluate(const DomainType& x, std::vector< RangeType >& ret) const
  {
    CHECK_AND_CALL_INTERFACE_IMPLEMENTATION(asImp().evaluate(x, ret));
  }

  std::vector< RangeType > evaluate(const DomainType& xx) const
  {
    std::vector< RangeType > ret(size(), RangeType(0));
    evaluate(xx, ret);
    return ret;
  }

  void jacobian(const DomainType& x, std::vector< JacobianRangeType >& ret) const
  {
    CHECK_AND_CALL_INTERFACE_IMPLEMENTATION(asImp().jacobian(x, ret));
  }

  std::vector< JacobianRangeType > jacobian(const DomainType& xx) const
  {
    std::vector< JacobianRangeType > ret(size(), JacobianRangeType(0));
    jacobian(xx, ret);
    return ret;
  }

  derived_type& asImp()
  {
    return static_cast< derived_type& >(*this);
  }

  const derived_type& asImp() const
  {
    return static_cast< const derived_type& >(*this);
  }
}; // class BaseFunctionSetInterface

} // namespace GDT
} // namespace Dune

#endif // DUNE_GDT_BASEFUNCTIONSET_INTERFACE_HH
<commit_msg>[basefunctionset.interface] is now derived from LocalfunctionSetInterface<commit_after>// This file is part of the dune-gdt project:
//   http://users.dune-project.org/projects/dune-gdt
// Copyright holders: Felix Albrecht
// License: BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
//
// Contributors: Kirsten Weber

#ifndef DUNE_GDT_BASEFUNCTIONSET_INTERFACE_HH
#define DUNE_GDT_BASEFUNCTIONSET_INTERFACE_HH

#include <vector>

#include <dune/common/bartonnackmanifcheck.hh>
#include <dune/common/fvector.hh>
#include <dune/common/fmatrix.hh>

#include <dune/stuff/functions/interfaces.hh>

namespace Dune {
namespace GDT {

/**
 *  \brief Interface for matrix valued basis functions.
 *
 *  \note   see specialization for rangeDimCols = 1 for vector and scalar valued basis functions.
 */
template< class Traits, class DomainFieldImp, int domainDim, class RangeFieldImp, int rangeDimRows, int rangeDimCols = 1 >
class BaseFunctionSetInterface;

/**
 *  \brief Interface for scalar and vector valued basis functions.
 */
template< class Traits, class DomainFieldImp, int domainDim, class RangeFieldImp, int rangeDim >
class BaseFunctionSetInterface< Traits, DomainFieldImp, domainDim, RangeFieldImp, rangeDim, 1 >
  : public Stuff::LocalfunctionSetInterface< typename Traits::EntityType, DomainFieldImp, domainDim, RangeFieldImp, rangeDim >
{
  typedef Stuff::LocalfunctionSetInterface
      < typename Traits::EntityType, DomainFieldImp, domainDim, RangeFieldImp, rangeDim >
    BaseType;
public:
  typedef typename Traits::derived_type derived_type;
  typedef typename Traits::BackendType  BackendType;
  typedef typename Traits::EntityType   EntityType;

  typedef DomainFieldImp                                  DomainFieldType;
  static const unsigned int                               dimDomain = domainDim;
  typedef Dune::FieldVector< DomainFieldType, dimDomain > DomainType;
  typedef RangeFieldImp                                 RangeFieldType;
  static const unsigned int                             dimRange = rangeDim;
  static const unsigned int                             dimRangeRows = rangeDim;
  static const unsigned int                             dimRangeCols = 1;
  typedef Dune::FieldVector< RangeFieldType, dimRange > RangeType;
  typedef Dune::FieldMatrix< RangeFieldType, dimRange, dimDomain > JacobianRangeType;

  const BackendType& backend() const
  {
    CHECK_INTERFACE_IMPLEMENTATION(asImp().backend());
    return asImp().backend();
  }

  derived_type& asImp()
  {
    return static_cast< derived_type& >(*this);
  }

  const derived_type& asImp() const
  {
    return static_cast< const derived_type& >(*this);
  }
}; // class BaseFunctionSetInterface

} // namespace GDT
} // namespace Dune

#endif // DUNE_GDT_BASEFUNCTIONSET_INTERFACE_HH
<|endoftext|>