<commit_before>// File:        BinMDataStd_NamedDataDriver.cxx
// Created:     Mon Jul  2 10:19:20 2007
// Author:      Sergey ZARITCHNY
//              <szy@popox.nnov.matra-dtv.fr>
// Copyright:   Open CasCade SA 2007

#include <BinMDataStd_NamedDataDriver.ixx>
#include <TCollection_ExtendedString.hxx>
#include <CDM_MessageDriver.hxx>
#include <TDataStd_NamedData.hxx>
#include <TColStd_HArray1OfReal.hxx>
#include <TColStd_HArray1OfInteger.hxx>
#include <TColStd_HArray1OfReal.hxx>
#include <TColStd_DataMapOfStringInteger.hxx>
#include <TDataStd_DataMapOfStringReal.hxx>
#include <TDataStd_DataMapOfStringString.hxx>
#include <TDataStd_DataMapOfStringByte.hxx>
#include <TDataStd_DataMapOfStringHArray1OfInteger.hxx>
#include <TDataStd_DataMapOfStringHArray1OfReal.hxx>
#include <TColStd_DataMapIteratorOfDataMapOfStringInteger.hxx>
#include <TDataStd_DataMapIteratorOfDataMapOfStringReal.hxx>
#include <TDataStd_DataMapIteratorOfDataMapOfStringString.hxx>
#include <TDataStd_DataMapIteratorOfDataMapOfStringByte.hxx>
#include <TDataStd_DataMapIteratorOfDataMapOfStringHArray1OfInteger.hxx>
#include <TDataStd_DataMapIteratorOfDataMapOfStringHArray1OfReal.hxx>

//=======================================================================
//function : BinMDataStd_NamedDataDriver
//purpose  : Constructor
//=======================================================================
BinMDataStd_NamedDataDriver::BinMDataStd_NamedDataDriver(const Handle(CDM_MessageDriver)& theMsgDriver)
     : BinMDF_ADriver (theMsgDriver, STANDARD_TYPE(TDataStd_NamedData)->Name())
{

}

//=======================================================================
//function : NewEmpty
//purpose  :
//=======================================================================
Handle(TDF_Attribute) BinMDataStd_NamedDataDriver::NewEmpty() const
{
  return new TDataStd_NamedData();
}

//=======================================================================
//function : Paste
//purpose  : persistent -> transient (retrieve)
//=======================================================================
Standard_Boolean BinMDataStd_NamedDataDriver::Paste(const BinObjMgt_Persistent&  theSource,
						      const Handle(TDF_Attribute)& theTarget,
						      BinObjMgt_RRelocationTable&  ) const
{

  Handle(TDataStd_NamedData) T = Handle(TDataStd_NamedData)::DownCast(theTarget);
  if(T.IsNull()) return Standard_False;
  Standard_Integer aLower, anUpper,i;
  if (! (theSource >> aLower >> anUpper))
    return Standard_False;
//  const Standard_Integer aLength = anUpper - aLower + 1;
  if (anUpper < aLower)  return Standard_False;
  if(anUpper | aLower) {
    TColStd_DataMapOfStringInteger anIntegers;
    for (i=aLower; i<=anUpper; i++) {
      TCollection_ExtendedString aKey;
      Standard_Integer aValue;
      if (! (theSource >> aKey >> aValue))
        return Standard_False;
      anIntegers.Bind(aKey, aValue);
    }
    T->ChangeIntegers(anIntegers);
  }

  if (! (theSource >> aLower >> anUpper))
    return Standard_False;
  if (anUpper < aLower)  return Standard_False;
  if(anUpper | aLower) {
    TDataStd_DataMapOfStringReal aReals;
    for (i=aLower; i<=anUpper; i++) {
      TCollection_ExtendedString aKey;
      Standard_Real aValue;
      if (! (theSource >> aKey >> aValue))
        return Standard_False;
      aReals.Bind(aKey, aValue);
    }
    T->ChangeReals(aReals);
  }

// strings
  if (! (theSource >> aLower >> anUpper))
    return Standard_False;
  if (anUpper < aLower)  return Standard_False;
  if(anUpper | aLower) {
    TDataStd_DataMapOfStringString aStrings;
    for (i=aLower; i<=anUpper; i++) {
      TCollection_ExtendedString aKey;
      TCollection_ExtendedString aValue;
      if (! (theSource >> aKey >> aValue))
        return Standard_False;
      aStrings.Bind(aKey, aValue);
    }
    T->ChangeStrings(aStrings);
  }

//Bytes
  if (! (theSource >> aLower >> anUpper))
    return Standard_False;
  if (anUpper < aLower)  return Standard_False;
  if(anUpper | aLower) {
    TDataStd_DataMapOfStringByte aBytes;
    for (i=aLower; i<=anUpper; i++) {
      TCollection_ExtendedString aKey;
      Standard_Byte aValue;
      if (! (theSource >> aKey >> aValue))
        return Standard_False;
      aBytes.Bind(aKey, (Standard_Byte)aValue);
    }
    T->ChangeBytes(aBytes);
  }

// arrays of integers
  if (! (theSource >> aLower >> anUpper))
    return Standard_False;
  if (anUpper < aLower)  return Standard_False;
  Standard_Boolean aResult = Standard_False;
  if(anUpper | aLower) {
    TDataStd_DataMapOfStringHArray1OfInteger anIntArrays;
    for (i=aLower; i<=anUpper; i++) {
      TCollection_ExtendedString aKey;
      if (! (theSource >> aKey))
        return Standard_False;
      Standard_Integer low, up;
      if (! (theSource >> low >> up))
        return Standard_False;
      if(up < low)
        return Standard_False;
      if(up | low) {
	Handle(TColStd_HArray1OfInteger) aTargetArray = new TColStd_HArray1OfInteger (low, up);
	if(!theSource.GetIntArray (&(aTargetArray->ChangeArray1())(low), up-low+1))
	  return Standard_False;

	Standard_Boolean Ok = anIntArrays.Bind(aKey, aTargetArray);
	aResult |= Ok;
      }
    }
    if (aResult)
      T->ChangeArraysOfIntegers(anIntArrays);
  }

// arrays of reals
  if (! (theSource >> aLower >> anUpper))
    return Standard_False;
  if (anUpper < aLower)  return Standard_False;
  aResult = Standard_False;
  if(anUpper | aLower) {
    TDataStd_DataMapOfStringHArray1OfReal aRealArrays;
    for (i=aLower; i<=anUpper; i++) {
      TCollection_ExtendedString aKey;
      if (! (theSource >> aKey))
        return Standard_False;
      Standard_Integer low, up;
      if (! (theSource >> low >> up))
        return Standard_False;
      if(up < low) Standard_False;
      if(low | up) {
        Handle(TColStd_HArray1OfReal) aTargetArray =
          new TColStd_HArray1OfReal(low, up);
        if(!theSource.GetRealArray (&(aTargetArray->ChangeArray1())(low), up-low+1))
          return Standard_False;
        Standard_Boolean Ok = aRealArrays.Bind(aKey, aTargetArray);
        aResult |= Ok;
      }
    }
    if(aResult)
      T->ChangeArraysOfReals(aRealArrays);
  }
  return Standard_True;
}

//=======================================================================
//function : Paste
//purpose  : transient -> persistent (store)
//=======================================================================
void BinMDataStd_NamedDataDriver::Paste(const Handle(TDF_Attribute)& theSource,
                                        BinObjMgt_Persistent&        theTarget,
                                        BinObjMgt_SRelocationTable&  ) const
{
  Handle(TDataStd_NamedData) S = Handle(TDataStd_NamedData)::DownCast (theSource);
  if(S.IsNull()) return;
//  Standard_Integer i=0;

  if(S->HasIntegers() && !S->GetIntegersContainer().IsEmpty()) {
    theTarget.PutInteger(1) << S->GetIntegersContainer().Extent(); //dim
    TColStd_DataMapIteratorOfDataMapOfStringInteger itr(S->GetIntegersContainer());
    for (; itr.More(); itr.Next()) {
      theTarget << itr.Key() << itr.Value(); // key - value;
    }
  } else {
    theTarget.PutInteger(0).PutInteger(0);
  }

  if(S->HasReals() && !S->GetRealsContainer().IsEmpty()) {
    theTarget.PutInteger(1) << S->GetRealsContainer().Extent();
    TDataStd_DataMapIteratorOfDataMapOfStringReal itr(S->GetRealsContainer());
    for (; itr.More(); itr.Next()) {
      theTarget << itr.Key() << itr.Value();
    }
  } else {
    theTarget.PutInteger(0).PutInteger(0);
  }

  if(S->HasStrings() && !S->GetStringsContainer().IsEmpty()) {
    theTarget.PutInteger(1) << S->GetStringsContainer().Extent();
    TDataStd_DataMapIteratorOfDataMapOfStringString itr(S->GetStringsContainer());
    for (; itr.More(); itr.Next()) {
      theTarget << itr.Key() << itr.Value();
    }
  } else {
    theTarget.PutInteger(0).PutInteger(0);
  }

  if(S->HasBytes() && !S->GetBytesContainer().IsEmpty()) {
    theTarget.PutInteger(1) << S->GetBytesContainer().Extent();
    TDataStd_DataMapIteratorOfDataMapOfStringByte itr(S->GetBytesContainer());
    for (; itr.More(); itr.Next()) {
      theTarget << itr.Key() << (Standard_Byte) itr.Value();
    }
  } else {
    theTarget.PutInteger(0).PutInteger(0);
  }

  if(S->HasArraysOfIntegers() && !S->GetArraysOfIntegersContainer().IsEmpty()) {
    theTarget.PutInteger(1) << S->GetArraysOfIntegersContainer().Extent();
    TDataStd_DataMapIteratorOfDataMapOfStringHArray1OfInteger
      itr(S->GetArraysOfIntegersContainer());
    for (; itr.More(); itr.Next()) {
      theTarget << itr.Key(); //key
      const TColStd_Array1OfInteger& anArr1 = itr.Value()->Array1();
      theTarget << anArr1.Lower() <<anArr1.Upper(); // value Arr1 dimensions
      Standard_Integer *aPtr = (Standard_Integer *) &anArr1(anArr1.Lower());
      theTarget.PutIntArray(aPtr, anArr1.Length());
    }
  } else {
    theTarget.PutInteger(0).PutInteger(0);
  }

  if(S->HasArraysOfReals() && !S->GetArraysOfRealsContainer().IsEmpty()) {
    theTarget.PutInteger(1) << S->GetArraysOfRealsContainer().Extent(); //dim
    TDataStd_DataMapIteratorOfDataMapOfStringHArray1OfReal
      itr(S->GetArraysOfRealsContainer());
    for (; itr.More(); itr.Next()) {
      theTarget << itr.Key();//key
      const TColStd_Array1OfReal& anArr1 = itr.Value()->Array1();
      theTarget << anArr1.Lower() <<anArr1.Upper(); // value Arr1 dimensions
      Standard_Real *aPtr = (Standard_Real *) &anArr1(anArr1.Lower());
      theTarget.PutRealArray(aPtr, anArr1.Length());
    }
  } else {
    theTarget.PutInteger(0).PutInteger(0);
  }
}
<commit_msg>[warning-fix][statement-has-no-effect] Added missing "return"<commit_after>// File:        BinMDataStd_NamedDataDriver.cxx
// Created:     Mon Jul  2 10:19:20 2007
// Author:      Sergey ZARITCHNY
//              <szy@popox.nnov.matra-dtv.fr>
// Copyright:   Open CasCade SA 2007

#include <BinMDataStd_NamedDataDriver.ixx>
#include <TCollection_ExtendedString.hxx>
#include <CDM_MessageDriver.hxx>
#include <TDataStd_NamedData.hxx>
#include <TColStd_HArray1OfReal.hxx>
#include <TColStd_HArray1OfInteger.hxx>
#include <TColStd_HArray1OfReal.hxx>
#include <TColStd_DataMapOfStringInteger.hxx>
#include <TDataStd_DataMapOfStringReal.hxx>
#include <TDataStd_DataMapOfStringString.hxx>
#include <TDataStd_DataMapOfStringByte.hxx>
#include <TDataStd_DataMapOfStringHArray1OfInteger.hxx>
#include <TDataStd_DataMapOfStringHArray1OfReal.hxx>
#include <TColStd_DataMapIteratorOfDataMapOfStringInteger.hxx>
#include <TDataStd_DataMapIteratorOfDataMapOfStringReal.hxx>
#include <TDataStd_DataMapIteratorOfDataMapOfStringString.hxx>
#include <TDataStd_DataMapIteratorOfDataMapOfStringByte.hxx>
#include <TDataStd_DataMapIteratorOfDataMapOfStringHArray1OfInteger.hxx>
#include <TDataStd_DataMapIteratorOfDataMapOfStringHArray1OfReal.hxx>

//=======================================================================
//function : BinMDataStd_NamedDataDriver
//purpose  : Constructor
//=======================================================================
BinMDataStd_NamedDataDriver::BinMDataStd_NamedDataDriver(const Handle(CDM_MessageDriver)& theMsgDriver)
     : BinMDF_ADriver (theMsgDriver, STANDARD_TYPE(TDataStd_NamedData)->Name())
{

}

//=======================================================================
//function : NewEmpty
//purpose  :
//=======================================================================
Handle(TDF_Attribute) BinMDataStd_NamedDataDriver::NewEmpty() const
{
  return new TDataStd_NamedData();
}

//=======================================================================
//function : Paste
//purpose  : persistent -> transient (retrieve)
//=======================================================================
Standard_Boolean BinMDataStd_NamedDataDriver::Paste(const BinObjMgt_Persistent&  theSource,
						      const Handle(TDF_Attribute)& theTarget,
						      BinObjMgt_RRelocationTable&  ) const
{

  Handle(TDataStd_NamedData) T = Handle(TDataStd_NamedData)::DownCast(theTarget);
  if(T.IsNull()) return Standard_False;
  Standard_Integer aLower, anUpper,i;
  if (! (theSource >> aLower >> anUpper))
    return Standard_False;
//  const Standard_Integer aLength = anUpper - aLower + 1;
  if (anUpper < aLower)  return Standard_False;
  if(anUpper | aLower) {
    TColStd_DataMapOfStringInteger anIntegers;
    for (i=aLower; i<=anUpper; i++) {
      TCollection_ExtendedString aKey;
      Standard_Integer aValue;
      if (! (theSource >> aKey >> aValue))
        return Standard_False;
      anIntegers.Bind(aKey, aValue);
    }
    T->ChangeIntegers(anIntegers);
  }

  if (! (theSource >> aLower >> anUpper))
    return Standard_False;
  if (anUpper < aLower)  return Standard_False;
  if(anUpper | aLower) {
    TDataStd_DataMapOfStringReal aReals;
    for (i=aLower; i<=anUpper; i++) {
      TCollection_ExtendedString aKey;
      Standard_Real aValue;
      if (! (theSource >> aKey >> aValue))
        return Standard_False;
      aReals.Bind(aKey, aValue);
    }
    T->ChangeReals(aReals);
  }

// strings
  if (! (theSource >> aLower >> anUpper))
    return Standard_False;
  if (anUpper < aLower)  return Standard_False;
  if(anUpper | aLower) {
    TDataStd_DataMapOfStringString aStrings;
    for (i=aLower; i<=anUpper; i++) {
      TCollection_ExtendedString aKey;
      TCollection_ExtendedString aValue;
      if (! (theSource >> aKey >> aValue))
        return Standard_False;
      aStrings.Bind(aKey, aValue);
    }
    T->ChangeStrings(aStrings);
  }

//Bytes
  if (! (theSource >> aLower >> anUpper))
    return Standard_False;
  if (anUpper < aLower)  return Standard_False;
  if(anUpper | aLower) {
    TDataStd_DataMapOfStringByte aBytes;
    for (i=aLower; i<=anUpper; i++) {
      TCollection_ExtendedString aKey;
      Standard_Byte aValue;
      if (! (theSource >> aKey >> aValue))
        return Standard_False;
      aBytes.Bind(aKey, (Standard_Byte)aValue);
    }
    T->ChangeBytes(aBytes);
  }

// arrays of integers
  if (! (theSource >> aLower >> anUpper))
    return Standard_False;
  if (anUpper < aLower)  return Standard_False;
  Standard_Boolean aResult = Standard_False;
  if(anUpper | aLower) {
    TDataStd_DataMapOfStringHArray1OfInteger anIntArrays;
    for (i=aLower; i<=anUpper; i++) {
      TCollection_ExtendedString aKey;
      if (! (theSource >> aKey))
        return Standard_False;
      Standard_Integer low, up;
      if (! (theSource >> low >> up))
        return Standard_False;
      if(up < low)
        return Standard_False;
      if(up | low) {
	Handle(TColStd_HArray1OfInteger) aTargetArray = new TColStd_HArray1OfInteger (low, up);
	if(!theSource.GetIntArray (&(aTargetArray->ChangeArray1())(low), up-low+1))
	  return Standard_False;

	Standard_Boolean Ok = anIntArrays.Bind(aKey, aTargetArray);
	aResult |= Ok;
      }
    }
    if (aResult)
      T->ChangeArraysOfIntegers(anIntArrays);
  }

// arrays of reals
  if (! (theSource >> aLower >> anUpper))
    return Standard_False;
  if (anUpper < aLower)  return Standard_False;
  aResult = Standard_False;
  if(anUpper | aLower) {
    TDataStd_DataMapOfStringHArray1OfReal aRealArrays;
    for (i=aLower; i<=anUpper; i++) {
      TCollection_ExtendedString aKey;
      if (! (theSource >> aKey))
        return Standard_False;
      Standard_Integer low, up;
      if (! (theSource >> low >> up))
        return Standard_False;
      if(up < low)
		return Standard_False;
      if(low | up) {
        Handle(TColStd_HArray1OfReal) aTargetArray =
          new TColStd_HArray1OfReal(low, up);
        if(!theSource.GetRealArray (&(aTargetArray->ChangeArray1())(low), up-low+1))
          return Standard_False;
        Standard_Boolean Ok = aRealArrays.Bind(aKey, aTargetArray);
        aResult |= Ok;
      }
    }
    if(aResult)
      T->ChangeArraysOfReals(aRealArrays);
  }
  return Standard_True;
}

//=======================================================================
//function : Paste
//purpose  : transient -> persistent (store)
//=======================================================================
void BinMDataStd_NamedDataDriver::Paste(const Handle(TDF_Attribute)& theSource,
                                        BinObjMgt_Persistent&        theTarget,
                                        BinObjMgt_SRelocationTable&  ) const
{
  Handle(TDataStd_NamedData) S = Handle(TDataStd_NamedData)::DownCast (theSource);
  if(S.IsNull()) return;
//  Standard_Integer i=0;

  if(S->HasIntegers() && !S->GetIntegersContainer().IsEmpty()) {
    theTarget.PutInteger(1) << S->GetIntegersContainer().Extent(); //dim
    TColStd_DataMapIteratorOfDataMapOfStringInteger itr(S->GetIntegersContainer());
    for (; itr.More(); itr.Next()) {
      theTarget << itr.Key() << itr.Value(); // key - value;
    }
  } else {
    theTarget.PutInteger(0).PutInteger(0);
  }

  if(S->HasReals() && !S->GetRealsContainer().IsEmpty()) {
    theTarget.PutInteger(1) << S->GetRealsContainer().Extent();
    TDataStd_DataMapIteratorOfDataMapOfStringReal itr(S->GetRealsContainer());
    for (; itr.More(); itr.Next()) {
      theTarget << itr.Key() << itr.Value();
    }
  } else {
    theTarget.PutInteger(0).PutInteger(0);
  }

  if(S->HasStrings() && !S->GetStringsContainer().IsEmpty()) {
    theTarget.PutInteger(1) << S->GetStringsContainer().Extent();
    TDataStd_DataMapIteratorOfDataMapOfStringString itr(S->GetStringsContainer());
    for (; itr.More(); itr.Next()) {
      theTarget << itr.Key() << itr.Value();
    }
  } else {
    theTarget.PutInteger(0).PutInteger(0);
  }

  if(S->HasBytes() && !S->GetBytesContainer().IsEmpty()) {
    theTarget.PutInteger(1) << S->GetBytesContainer().Extent();
    TDataStd_DataMapIteratorOfDataMapOfStringByte itr(S->GetBytesContainer());
    for (; itr.More(); itr.Next()) {
      theTarget << itr.Key() << (Standard_Byte) itr.Value();
    }
  } else {
    theTarget.PutInteger(0).PutInteger(0);
  }

  if(S->HasArraysOfIntegers() && !S->GetArraysOfIntegersContainer().IsEmpty()) {
    theTarget.PutInteger(1) << S->GetArraysOfIntegersContainer().Extent();
    TDataStd_DataMapIteratorOfDataMapOfStringHArray1OfInteger
      itr(S->GetArraysOfIntegersContainer());
    for (; itr.More(); itr.Next()) {
      theTarget << itr.Key(); //key
      const TColStd_Array1OfInteger& anArr1 = itr.Value()->Array1();
      theTarget << anArr1.Lower() <<anArr1.Upper(); // value Arr1 dimensions
      Standard_Integer *aPtr = (Standard_Integer *) &anArr1(anArr1.Lower());
      theTarget.PutIntArray(aPtr, anArr1.Length());
    }
  } else {
    theTarget.PutInteger(0).PutInteger(0);
  }

  if(S->HasArraysOfReals() && !S->GetArraysOfRealsContainer().IsEmpty()) {
    theTarget.PutInteger(1) << S->GetArraysOfRealsContainer().Extent(); //dim
    TDataStd_DataMapIteratorOfDataMapOfStringHArray1OfReal
      itr(S->GetArraysOfRealsContainer());
    for (; itr.More(); itr.Next()) {
      theTarget << itr.Key();//key
      const TColStd_Array1OfReal& anArr1 = itr.Value()->Array1();
      theTarget << anArr1.Lower() <<anArr1.Upper(); // value Arr1 dimensions
      Standard_Real *aPtr = (Standard_Real *) &anArr1(anArr1.Lower());
      theTarget.PutRealArray(aPtr, anArr1.Length());
    }
  } else {
    theTarget.PutInteger(0).PutInteger(0);
  }
}
<|endoftext|>


<commit_before><commit_msg>WaE: unused private fields<commit_after><|endoftext|>


<commit_before><commit_msg>coverity#735321 Unchecked return value<commit_after><|endoftext|>


<commit_before>// Test doesn't build on windows, because OpenGL on windows is a nightmare.
#ifdef _WIN32
#include <stdio.h>
int main() {
    printf("Skipping test on Windows\n");
    return 0;
}
#else

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <cstring>
#include "../src/runtime/mini_opengl.h"
#include "Halide.h"

extern "C" void glGenTextures(GLsizei, GLuint *);
extern "C" void glTexParameteri(GLenum, GLenum, GLint);
extern "C" void glBindTexture(GLenum, GLuint);
extern "C" void glTexImage2D(GLenum, GLint, GLint, GLsizei, GLsizei, GLint, GLenum, GLenum, const GLvoid *);
extern "C" GLuint glCreateProgram();
extern "C" void glAttachShader(GLuint, GLuint);
extern "C" void glLinkProgram(GLuint);
extern "C" void glGetProgramiv(GLuint, GLenum, GLint *);
extern "C" void glGetProgramInfoLog(GLuint, GLsizei, GLsizei *, GLchar *);
extern "C" GLuint glCreateShader(GLenum);
extern "C" void glShaderSource(GLuint, GLsizei, const GLchar **, const GLint *);
extern "C" void glCompileShader(GLuint);
extern "C" void glGetShaderiv(GLuint, GLenum, GLint *);
extern "C" void glGetShaderInfoLog(GLuint, GLsizei, GLsizei *, GLchar *);
extern "C" void glEnable(GLenum);
extern "C" void glDisable(GLenum);
extern "C" void glGetIntegerv(GLenum, GLint *);
extern "C" void glGetBooleanv(GLenum, GLboolean *);
extern "C" GLenum glGetError();
extern "C" void glActiveTexture(GLenum);
extern "C" void glEnableVertexAttribArray(GLuint);
extern "C" void glDisableVertexAttribArray(GLuint);
extern "C" void glUseProgram(GLuint);
extern "C" void glGenBuffers(GLsizei, GLuint *);
extern "C" void glViewport(GLint, GLint, GLsizei, GLsizei);
extern "C" void glGenFramebuffers(GLsizei, GLuint *);
extern "C" void glBindBuffer(GLenum, GLuint);
extern "C" void glBindFramebuffer(GLenum, GLuint);
extern "C" void glGenVertexArrays(GLsizei, GLuint *);
extern "C" void glBindVertexArray(GLuint);
extern "C" void glGetVertexAttribiv(GLuint, GLenum, GLint *);

// Generates an arbitrary program.
class Program
{
    public:

    static GLuint handle()
    {
        const char *vertexShader = " \
                                    attribute vec4 Position;  \
                                    attribute vec2 TexCoordIn; \
                                    varying vec2 TexCoordOut; \
                                    void main(void) {  \
                                        gl_Position = Position; \
                                        TexCoordOut = TexCoordIn; \
                                    }";

        const char *fragmentShader = " \
                                      varying vec2 TexCoordOut; \
                                      uniform sampler2D Texture; \
                                      void main(void) { \
                                          gl_FragColor = texture2D(Texture, TexCoordOut); \
                                      }";

        GLuint handle = glCreateProgram();
        glAttachShader(handle, compileShader("vertex", vertexShader, GL_VERTEX_SHADER));
        glAttachShader(handle, compileShader("fragment", fragmentShader, GL_FRAGMENT_SHADER));
        glLinkProgram(handle);

        GLint linkSuccess;
        glGetProgramiv(handle, GL_LINK_STATUS, &linkSuccess);
        if (linkSuccess == GL_FALSE) {
            GLchar messages[256];
            glGetProgramInfoLog(handle, sizeof(messages), 0, messages);
            fprintf(stderr, "Error linking program: %s\n", messages);
            exit(1);
        }

        return handle;
    }

    private:

    static GLuint compileShader(const char *label, const char *shaderString, GLenum shaderType)
    {
        const GLuint handle = glCreateShader(shaderType);
        const int len = strlen(shaderString);
        glShaderSource(handle, 1, &shaderString, &len);
        glCompileShader(handle);
        GLint compileSuccess;
        glGetShaderiv(handle, GL_COMPILE_STATUS, &compileSuccess);
        if (compileSuccess == GL_FALSE) {
            GLchar messages[256];
            glGetShaderInfoLog(handle, sizeof(messages), 0, messages);
            fprintf(stderr, "Error compiling %s shader: %s\n", label, messages);
            exit(1);
        }
        return handle;
    }
};

// Encapsulates setting OpenGL's state to arbitrary values, and checking
// whether the state matches those values.
class KnownState
{
    private:

    void gl_enable(GLenum cap, bool state)
    {
	(state ? glEnable : glDisable)(cap);
    }

    GLuint gl_gen(void (*fn)(GLsizei, GLuint *))
    {
        GLuint val;
        (*fn)(1, &val);
        return val;
    }

    void check_value(const char *operation, const char *label, GLenum pname, GLint initial)
    {
	GLint val;
	glGetIntegerv(pname, &val);
	if (val != initial) {
	    fprintf(stderr, "%s did not restore %s: initial value was %d (%#x), current value is %d (%#x)\n", operation, label, initial, initial, val, val);
	    errors = true;
	}
    }

    void check_value(const char *operation, const char *label, GLenum pname, GLenum initial)
    {
        check_value(operation, label, pname, (GLint) initial);
    }

    void check_value(const char *operation, const char *label, GLenum pname, GLint initial[], int n=4)
    {
        GLint val[2048];
        glGetIntegerv(pname, val);
        for (int i=0; i<n; i++) {
            if (val[i] != initial[i]) {
                fprintf(stderr, "%s did not restore %s: initial value was", operation, label);
                for (int j=0; j<n; j++) fprintf(stderr, " %d", initial[j]);
                fprintf(stderr, ", current value is");
                for (int j=0; j<n; j++) fprintf(stderr, " %d", val[j]);
                fprintf(stderr, "\n");
                errors = true;
                return;
            }
        }
    }

    void check_value(const char *operation, const char *label, GLenum pname, bool initial)
    {
	GLboolean val;
	glGetBooleanv(pname, &val);
	if (val != initial) {
	    fprintf(stderr, "%s did not restore boolean %s: initial value was %s, current value is %s\n", operation, label, initial ? "true" : "false", val ? "true" : "false");
	    errors = true;
	}
    }

    void check_error(const char *label)
    {
	GLenum err = glGetError();
	if (err != GL_NO_ERROR) {
	    fprintf(stderr, "Error setting %s: OpenGL error %#x\n", label, err);
	    errors = true;
	}
    }

    GLenum initial_active_texture;
    GLint initial_viewport[4];
    GLuint initial_array_buffer_binding;
    GLuint initial_element_array_buffer_binding;
    GLuint initial_current_program;
    GLuint initial_framebuffer_binding;
    static const int ntextures = 10;
    GLuint initial_bound_textures[ntextures];
    bool initial_cull_face;
    bool initial_depth_test;

    static const int nvertex_attribs = 10;
    bool initial_vertex_attrib_array_enabled[nvertex_attribs];

#ifdef GL_VERTEX_ARRAY_BINDING
    GLuint initial_vertex_array_binding;
#endif

    public:

    bool errors;

    // This sets most values to generated or arbitrary values, which the
    // halide calls would be unlikely to accidentally use.  But for boolean
    // values, we want to be sure that halide is really restoring the
    // initial value, not just setting it to true or false.  So we need to
    // be able to try both.
    void setup(bool boolval)
    {
        glGenTextures(ntextures, initial_bound_textures);
        for (int i=0; i<ntextures; i++) {
            glActiveTexture(GL_TEXTURE0 + i);
            glBindTexture(GL_TEXTURE_2D, initial_bound_textures[i]);
        }
	glActiveTexture(initial_active_texture = GL_TEXTURE3);

        for (int i=0; i<nvertex_attribs; i++) {
            if ( (initial_vertex_attrib_array_enabled[i] = boolval ) )
                glEnableVertexAttribArray(i);
            else
                glDisableVertexAttribArray(i);
            char buf[256];
            sprintf(buf, "vertex attrib array %d state", i);
            check_error(buf);
        }

	glUseProgram(initial_current_program = Program::handle());
	glViewport(initial_viewport[0] = 111, initial_viewport[1] = 222, initial_viewport[2] = 333, initial_viewport[3] = 444);
	gl_enable(GL_CULL_FACE, initial_cull_face = boolval);
	gl_enable(GL_DEPTH_TEST, initial_depth_test = boolval);
	glBindBuffer(GL_ARRAY_BUFFER, initial_array_buffer_binding = gl_gen(glGenBuffers));
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, initial_element_array_buffer_binding = gl_gen(glGenBuffers));
	glBindFramebuffer(GL_FRAMEBUFFER, initial_framebuffer_binding = gl_gen(glGenFramebuffers));

#ifdef GL_VERTEX_ARRAY_BINDING
        glBindVertexArray(initial_vertex_array_binding = gl_gen(glGenVertexArrays));
#endif
        check_error("known state");
    }

    void check(const char *operation)
    {
	check_value(operation, "ActiveTexture", GL_ACTIVE_TEXTURE, initial_active_texture);
	check_value(operation, "current program", GL_CURRENT_PROGRAM, initial_current_program);
	check_value(operation, "framebuffer binding", GL_FRAMEBUFFER_BINDING, initial_framebuffer_binding);
	check_value(operation, "array buffer binding", GL_ARRAY_BUFFER_BINDING, initial_array_buffer_binding);
	check_value(operation, "element array buffer binding", GL_ELEMENT_ARRAY_BUFFER_BINDING, initial_element_array_buffer_binding);
	check_value(operation, "viewport", GL_VIEWPORT, initial_viewport);
	check_value(operation, "GL_CULL_FACE", GL_CULL_FACE, initial_cull_face);
	check_value(operation, "GL_DEPTH_TEST", GL_DEPTH_TEST, initial_cull_face);

#ifdef GL_VERTEX_ARRAY_BINDING
	check_value(operation, "vertex array binding", GL_VERTEX_ARRAY_BINDING, initial_vertex_array_binding);
#endif

        for (int i=0; i<ntextures; i++) {
            char buf[100];
            sprintf(buf, "bound texture (unit %d)", i);
            glActiveTexture(GL_TEXTURE0 + i);
            check_value(operation, buf, GL_TEXTURE_BINDING_2D, initial_bound_textures[i]);
        }

        for (int i=0; i < nvertex_attribs; i++) {
            int initial = initial_vertex_attrib_array_enabled[i];
            GLint val;
            glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &val);
            if (val != initial) {
                fprintf(stderr, "%s did not restore boolean VertexAttributeArrayEnabled(%d): initial value was %s, current value is %s\n", operation, i, initial ? "true" : "false", val ? "true" : "false");
                errors = true;
            }
        }
    }
};

using namespace Halide;

int main() {
    KnownState known_state;

    Image<uint8_t> input(255, 10, 3);
    Buffer out(UInt(8), 255, 10, 3);

    Var x, y, c;
    Func g;
    g(x, y, c) = input(x, y, c);
    g.bound(c, 0, 3);
    g.glsl(x, y, c);
    g.realize(out); // let Halide initialize OpenGL

    known_state.setup(true);
    g.realize(out);
    known_state.check("realize");

    known_state.setup(true);
    out.copy_to_host();
    known_state.check("copy_to_host");

    known_state.setup(false);
    g.realize(out);
    known_state.check("realize");

    known_state.setup(false);
    out.copy_to_host();
    known_state.check("copy_to_host");

    if (known_state.errors) {
	return 1;
    }

    printf("Success!\n");
    return 0;
}

#endif
<commit_msg>Fix the save_state test.<commit_after>// Test doesn't build on windows, because OpenGL on windows is a nightmare.
#ifdef _WIN32
#include <stdio.h>
int main() {
    printf("Skipping test on Windows\n");
    return 0;
}
#else

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <cstring>
#include "../src/runtime/mini_opengl.h"
#include "Halide.h"

extern "C" void glGenTextures(GLsizei, GLuint *);
extern "C" void glTexParameteri(GLenum, GLenum, GLint);
extern "C" void glBindTexture(GLenum, GLuint);
extern "C" void glTexImage2D(GLenum, GLint, GLint, GLsizei, GLsizei, GLint, GLenum, GLenum, const GLvoid *);
extern "C" GLuint glCreateProgram();
extern "C" void glAttachShader(GLuint, GLuint);
extern "C" void glLinkProgram(GLuint);
extern "C" void glGetProgramiv(GLuint, GLenum, GLint *);
extern "C" void glGetProgramInfoLog(GLuint, GLsizei, GLsizei *, GLchar *);
extern "C" GLuint glCreateShader(GLenum);
extern "C" void glShaderSource(GLuint, GLsizei, const GLchar **, const GLint *);
extern "C" void glCompileShader(GLuint);
extern "C" void glGetShaderiv(GLuint, GLenum, GLint *);
extern "C" void glGetShaderInfoLog(GLuint, GLsizei, GLsizei *, GLchar *);
extern "C" void glEnable(GLenum);
extern "C" void glDisable(GLenum);
extern "C" void glGetIntegerv(GLenum, GLint *);
extern "C" void glGetBooleanv(GLenum, GLboolean *);
extern "C" GLenum glGetError();
extern "C" void glActiveTexture(GLenum);
extern "C" void glEnableVertexAttribArray(GLuint);
extern "C" void glDisableVertexAttribArray(GLuint);
extern "C" void glUseProgram(GLuint);
extern "C" void glGenBuffers(GLsizei, GLuint *);
extern "C" void glViewport(GLint, GLint, GLsizei, GLsizei);
extern "C" void glGenFramebuffers(GLsizei, GLuint *);
extern "C" void glBindBuffer(GLenum, GLuint);
extern "C" void glBindFramebuffer(GLenum, GLuint);
extern "C" void glGenVertexArrays(GLsizei, GLuint *);
extern "C" void glBindVertexArray(GLuint);
extern "C" void glGetVertexAttribiv(GLuint, GLenum, GLint *);
extern "C" const GLubyte* glGetString(GLenum name);

// Generates an arbitrary program.
class Program
{
    public:

    static GLuint handle()
    {
        const char *vertexShader = " \
                                    attribute vec4 Position;  \
                                    attribute vec2 TexCoordIn; \
                                    varying vec2 TexCoordOut; \
                                    void main(void) {  \
                                        gl_Position = Position; \
                                        TexCoordOut = TexCoordIn; \
                                    }";

        const char *fragmentShader = " \
                                      varying vec2 TexCoordOut; \
                                      uniform sampler2D Texture; \
                                      void main(void) { \
                                          gl_FragColor = texture2D(Texture, TexCoordOut); \
                                      }";

        GLuint handle = glCreateProgram();
        glAttachShader(handle, compileShader("vertex", vertexShader, GL_VERTEX_SHADER));
        glAttachShader(handle, compileShader("fragment", fragmentShader, GL_FRAGMENT_SHADER));
        glLinkProgram(handle);

        GLint linkSuccess;
        glGetProgramiv(handle, GL_LINK_STATUS, &linkSuccess);
        if (linkSuccess == GL_FALSE) {
            GLchar messages[256];
            glGetProgramInfoLog(handle, sizeof(messages), 0, messages);
            fprintf(stderr, "Error linking program: %s\n", messages);
            exit(1);
        }

        return handle;
    }

    private:

    static GLuint compileShader(const char *label, const char *shaderString, GLenum shaderType)
    {
        const GLuint handle = glCreateShader(shaderType);
        const int len = strlen(shaderString);
        glShaderSource(handle, 1, &shaderString, &len);
        glCompileShader(handle);
        GLint compileSuccess;
        glGetShaderiv(handle, GL_COMPILE_STATUS, &compileSuccess);
        if (compileSuccess == GL_FALSE) {
            GLchar messages[256];
            glGetShaderInfoLog(handle, sizeof(messages), 0, messages);
            fprintf(stderr, "Error compiling %s shader: %s\n", label, messages);
            exit(1);
        }
        return handle;
    }
};

// Encapsulates setting OpenGL's state to arbitrary values, and checking
// whether the state matches those values.
class KnownState
{
    private:

    void gl_enable(GLenum cap, bool state)
    {
        (state ? glEnable : glDisable)(cap);
    }

    GLuint gl_gen(void (*fn)(GLsizei, GLuint *))
    {
        GLuint val;
        (*fn)(1, &val);
        return val;
    }

    void check_value(const char *operation, const char *label, GLenum pname, GLint initial)
    {
        GLint val;
        glGetIntegerv(pname, &val);
        if (val != initial) {
            fprintf(stderr, "%s did not restore %s: initial value was %d (%#x), current value is %d (%#x)\n", operation, label, initial, initial, val, val);
            errors = true;
        }
    }

    void check_value(const char *operation, const char *label, GLenum pname, GLenum initial)
    {
        check_value(operation, label, pname, (GLint) initial);
    }

    void check_value(const char *operation, const char *label, GLenum pname, GLint initial[], int n=4)
    {
        GLint val[2048];
        glGetIntegerv(pname, val);
        for (int i=0; i<n; i++) {
            if (val[i] != initial[i]) {
                fprintf(stderr, "%s did not restore %s: initial value was", operation, label);
                for (int j=0; j<n; j++) fprintf(stderr, " %d", initial[j]);
                fprintf(stderr, ", current value is");
                for (int j=0; j<n; j++) fprintf(stderr, " %d", val[j]);
                fprintf(stderr, "\n");
                errors = true;
                return;
            }
        }
    }

    void check_value(const char *operation, const char *label, GLenum pname, bool initial)
    {
        GLboolean val;
        glGetBooleanv(pname, &val);
        if (val != initial) {
            fprintf(stderr, "%s did not restore boolean %s: initial value was %s, current value is %s\n", operation, label, initial ? "true" : "false", val ? "true" : "false");
            errors = true;
        }
    }

    void check_error(const char *label)
    {
        GLenum err = glGetError();
        if (err != GL_NO_ERROR) {
            fprintf(stderr, "Error setting %s: OpenGL error %#x\n", label, err);
            errors = true;
        }
    }

    GLenum initial_active_texture;
    GLint initial_viewport[4];
    GLuint initial_array_buffer_binding;
    GLuint initial_element_array_buffer_binding;
    GLuint initial_current_program;
    GLuint initial_framebuffer_binding;
    static const int ntextures = 10;
    GLuint initial_bound_textures[ntextures];
    bool initial_cull_face;
    bool initial_depth_test;

    static const int nvertex_attribs = 10;
    bool initial_vertex_attrib_array_enabled[nvertex_attribs];

    // The next two functions are stolen from opengl.cpp
    // and are used to parse the major/minor version of OpenGL
    // to see if vertex array objects are supported
    const char *parse_int(const char *str, int *val) {
        int v = 0;
        size_t i = 0;
        while (str[i] >= '0' && str[i] <= '9') {
            v = 10 * v + (str[i] - '0');
            i++;
        }
        if (i > 0) {
            *val = v;
            return &str[i];
        }
        return NULL;
    }

    const char *parse_opengl_version(const char *str, int *major, int *minor) {
        str = parse_int(str, major);
        if (str == NULL || *str != '.') {
            return NULL;
        }
        return parse_int(str + 1, minor);
    }

    GLuint initial_vertex_array_binding;

    public:

    // version of OpenGL
    int gl_major_version;
    int gl_minor_version;

    bool errors;

    // This sets most values to generated or arbitrary values, which the
    // halide calls would be unlikely to accidentally use.  But for boolean
    // values, we want to be sure that halide is really restoring the
    // initial value, not just setting it to true or false.  So we need to
    // be able to try both.
    void setup(bool boolval)
    {
        // parse the OpenGL version
        const char *version = (const char *)glGetString(GL_VERSION);
        parse_opengl_version(version, &gl_major_version, &gl_minor_version);

        glGenTextures(ntextures, initial_bound_textures);
        for (int i=0; i<ntextures; i++) {
            glActiveTexture(GL_TEXTURE0 + i);
            glBindTexture(GL_TEXTURE_2D, initial_bound_textures[i]);
        }
	    glActiveTexture(initial_active_texture = GL_TEXTURE3);

        for (int i=0; i<nvertex_attribs; i++) {
            if ( (initial_vertex_attrib_array_enabled[i] = boolval ) )
                glEnableVertexAttribArray(i);
            else
                glDisableVertexAttribArray(i);
            char buf[256];
            sprintf(buf, "vertex attrib array %d state", i);
            check_error(buf);
        }

        glUseProgram(initial_current_program = Program::handle());
        glViewport(initial_viewport[0] = 111, initial_viewport[1] = 222, initial_viewport[2] = 333, initial_viewport[3] = 444);
        gl_enable(GL_CULL_FACE, initial_cull_face = boolval);
        gl_enable(GL_DEPTH_TEST, initial_depth_test = boolval);
        glBindBuffer(GL_ARRAY_BUFFER, initial_array_buffer_binding = gl_gen(glGenBuffers));
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, initial_element_array_buffer_binding = gl_gen(glGenBuffers));
        glBindFramebuffer(GL_FRAMEBUFFER, initial_framebuffer_binding = gl_gen(glGenFramebuffers));

        // Vertex array objects are only used by Halide if the OpenGL version >=3
        if (gl_major_version >= 3) {
            glBindVertexArray(initial_vertex_array_binding = gl_gen(glGenVertexArrays));
            check_error("known state");
        }
    }

    void check(const char *operation)
    {
        check_value(operation, "ActiveTexture", GL_ACTIVE_TEXTURE, initial_active_texture);
        check_value(operation, "current program", GL_CURRENT_PROGRAM, initial_current_program);
        check_value(operation, "framebuffer binding", GL_FRAMEBUFFER_BINDING, initial_framebuffer_binding);
        check_value(operation, "array buffer binding", GL_ARRAY_BUFFER_BINDING, initial_array_buffer_binding);
        check_value(operation, "element array buffer binding", GL_ELEMENT_ARRAY_BUFFER_BINDING, initial_element_array_buffer_binding);
        check_value(operation, "viewport", GL_VIEWPORT, initial_viewport);
        check_value(operation, "GL_CULL_FACE", GL_CULL_FACE, initial_cull_face);
        check_value(operation, "GL_DEPTH_TEST", GL_DEPTH_TEST, initial_cull_face);

        // Vertex array objects are only used by Halide if the OpenGL version >=3
        if (gl_major_version >= 3) {
            check_value(operation, "vertex array binding", GL_VERTEX_ARRAY_BINDING, initial_vertex_array_binding);
        }

        for (int i=0; i<ntextures; i++) {
            char buf[100];
            sprintf(buf, "bound texture (unit %d)", i);
            glActiveTexture(GL_TEXTURE0 + i);
            check_value(operation, buf, GL_TEXTURE_BINDING_2D, initial_bound_textures[i]);
        }

        for (int i=0; i < nvertex_attribs; i++) {
            int initial = initial_vertex_attrib_array_enabled[i];
            GLint val;
            glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &val);
            if (val != initial) {
                fprintf(stderr, "%s did not restore boolean VertexAttributeArrayEnabled(%d): initial value was %s, current value is %s\n", operation, i, initial ? "true" : "false", val ? "true" : "false");
                errors = true;
            }
        }
    }
};

using namespace Halide;

int main() {
    KnownState known_state;

    Image<uint8_t> input(255, 10, 3);
    Buffer out(UInt(8), 255, 10, 3);

    Var x, y, c;
    Func g;
    g(x, y, c) = input(x, y, c);
    g.bound(c, 0, 3);
    g.glsl(x, y, c);
    g.realize(out); // let Halide initialize OpenGL

    known_state.setup(true);
    g.realize(out);
    known_state.check("realize");

    known_state.setup(true);
    out.copy_to_host();
    known_state.check("copy_to_host");

    known_state.setup(false);
    g.realize(out);
    known_state.check("realize");

    known_state.setup(false);
    out.copy_to_host();
    known_state.check("copy_to_host");

    if (known_state.errors) {
	return 1;
    }

    printf("Success!\n");
    return 0;
}

#endif
<|endoftext|>


<commit_before>#pragma once

#include <vector>

#include "acmacs-base/fmt.hh"

// ----------------------------------------------------------------------

namespace acmacs
{
    template <typename Key, typename Value> class flat_map_t
    {
      public:
        using key_type = Key;
        using value_type = Value;
        using entry_type = std::pair<Key, Value>;

        flat_map_t() = default;
        template <typename Iter> flat_map_t(Iter first, Iter last) : data_(first, last) {}
        flat_map_t(std::initializer_list<entry_type> init) : data_{init} {}

        auto begin() const { return data_.begin(); }
        auto end() const { return data_.end(); }
        auto empty() const { return data_.empty(); }
        auto size() const { return data_.size(); }
        const auto& operator[](size_t index) const { return data_[index]; }
        void sort_by_key() { std::sort(std::begin(data_), std::end(data_), [](const auto& e1, const auto& e2) { return e1.first < e2.first; }); }
        void sort_by_value() { std::sort(std::begin(data_), std::end(data_), [](const auto& e1, const auto& e2) { return e1.second < e2.second; }); }
        void sort_by_value_reverse() { std::sort(std::begin(data_), std::end(data_), [](const auto& e1, const auto& e2) { return e1.second > e2.second; }); }

        template <typename K> auto find(const K& key) const { return std::find_if(std::begin(data_), std::end(data_), [&key](const auto& en) { return en.first == key; }); }
        template <typename K> auto find(const K& key) { return std::find_if(std::begin(data_), std::end(data_), [&key](const auto& en) { return en.first == key; }); }
        template <typename K> bool exists(const K& key) const { return find(key) != std::end(data_); }

        Value& at(const Key& key)
        {
            if (const auto found = find(key); found != std::end(data_))
                return found->second;
            throw std::out_of_range{fmt::format("acmacs::flat_map_t::at(): no key: {}", key)};
        }

        const Value& at(const Key& key) const
        {
            if (const auto found = find(key); found != std::end(data_))
                return found->second;
            throw std::out_of_range{fmt::format("acmacs::flat_map_t::at(): no key: {}", key)};
        }

        auto& emplace(const Key& key, const Value& value) { return data_.emplace_back(key, value); }
        auto& emplace(Key&& key, Value&& value) { return data_.emplace_back(std::move(key), std::move(value)); }

        auto& emplace_or_replace(const Key& key, const Value& value)
        {
            if (auto found = find(key); found != std::end(data_)) {
                found->second = value;
                return *found;
            }
            else
                return emplace(key, value);
        }

        void reserve(size_t sz) { data_.reserve(sz); }

      private:
        std::vector<entry_type> data_;
    };

} // namespace acmacs

// ----------------------------------------------------------------------
/// Local Variables:
/// eval: (if (fboundp 'eu-rename-buffer) (eu-rename-buffer))
/// End:
<commit_msg>flat_map improvement<commit_after>#pragma once

#include <vector>

#include "acmacs-base/fmt.hh"

// ----------------------------------------------------------------------

namespace acmacs
{
    template <typename Key, typename Value> class flat_map_t
    {
      public:
        using key_type = Key;
        using value_type = Value;
        using entry_type = std::pair<Key, Value>;

        flat_map_t() = default;
        template <typename Iter> flat_map_t(Iter first, Iter last) : data_(first, last) {}
        flat_map_t(std::initializer_list<entry_type> init) : data_{init} {}

        auto begin() const { return data_.begin(); }
        auto end() const { return data_.end(); }
        auto empty() const { return data_.empty(); }
        auto size() const { return data_.size(); }
        const auto& operator[](size_t index) const { return data_[index]; }
        void sort_by_key() { std::sort(std::begin(data_), std::end(data_), [](const auto& e1, const auto& e2) { return e1.first < e2.first; }); }
        void sort_by_value() { std::sort(std::begin(data_), std::end(data_), [](const auto& e1, const auto& e2) { return e1.second < e2.second; }); }
        void sort_by_value_reverse() { std::sort(std::begin(data_), std::end(data_), [](const auto& e1, const auto& e2) { return e1.second > e2.second; }); }

        template <typename K> auto find(const K& key) const { return std::find_if(std::begin(data_), std::end(data_), [&key](const auto& en) { return en.first == key; }); }
        template <typename K> auto find(const K& key) { return std::find_if(std::begin(data_), std::end(data_), [&key](const auto& en) { return en.first == key; }); }
        template <typename K> bool exists(const K& key) const { return find(key) != std::end(data_); }

        Value& at(const Key& key)
        {
            if (const auto found = find(key); found != std::end(data_))
                return found->second;
            throw std::out_of_range{fmt::format("acmacs::flat_map_t::at(): no key: {}", key)};
        }

        const Value& at(const Key& key) const
        {
            if (const auto found = find(key); found != std::end(data_))
                return found->second;
            throw std::out_of_range{fmt::format("acmacs::flat_map_t::at(): no key: {}", key)};
        }

        const Value* at_ptr(const Key& key) const
        {
            if (const auto found = find(key); found != std::end(data_))
                return &found->second;
            else
                return nullptr;
        }

        auto& emplace(const Key& key, const Value& value) { return data_.emplace_back(key, value); }
        auto& emplace(Key&& key, Value&& value) { return data_.emplace_back(std::move(key), std::move(value)); }

        auto& emplace_or_replace(const Key& key, const Value& value)
        {
            if (auto found = find(key); found != std::end(data_)) {
                found->second = value;
                return *found;
            }
            else
                return emplace(key, value);
        }

        void reserve(size_t sz) { data_.reserve(sz); }

      private:
        std::vector<entry_type> data_;
    };

} // namespace acmacs

// ----------------------------------------------------------------------
/// Local Variables:
/// eval: (if (fboundp 'eu-rename-buffer) (eu-rename-buffer))
/// End:
<|endoftext|>


<commit_before>#include <iostream>
#include <fstream>
#include <string>
using namespace std::string_literals;

#include "acmacs-base/argc-argv.hh"
#include "acmacs-base/read-file.hh"
#include "acmacs-base/quicklook.hh"
#include "acmacs-chart-2/factory-import.hh"
#include "acmacs-map-draw/draw.hh"
#include "acmacs-map-draw/settings.hh"
#include "acmacs-map-draw/mod-applicator.hh"
#include "acmacs-map-draw/setup-dbs.hh"

// ----------------------------------------------------------------------

static int draw(const argc_argv& args);

int main(int argc, char* const argv[])
{
    int exit_code = 1;
    try {
        argc_argv args(argc, argv, {
                {"--apply", "", "json array to use as \"apply\", e.g. [\"all_grey\",\"egg\",\"clades\",\"labels\"]"},
                {"--apply-from", "", "read json array to use as \"apply\" from file (or stdin if \"-\""},
                {"--clade", false},
                {"--point-scale", 1.0},
                {"--rotate-degrees", 0.0, "counter clockwise"},
                {"-r", 0.0, "rotate in degrees, counter clockwise"},
                {"--flip-ew", false},
                {"--flip-ns", false},
                {"--settings", argc_argv::strings{}, "load settings from file"},
                {"-s", argc_argv::strings{}, "load settings from file"},
                {"--init-settings", "", "initialize (overwrite) settings file"},
                {"--save", "", "save resulting chart with modified projection and plot spec"},
                {"-h", false},
                {"--help", false},
                {"--help-mods", false},
                {"--help-select", false},
                {"--previous", ""},
                {"--open", false},
                {"--ql", false},
                {"--projection", 0L},
                {"-p", 0L, "projection"},
                {"--db-dir", ""},
                {"--time", false, "report time of loading chart"},
                {"--verbose", false},
                {"-v", false},
        });
        if (args["--help-mods"])
            std::cerr << settings_help_mods();
        else if (args["--help-select"])
            std::cerr << settings_help_select();
        else if (args["-h"] || args["--help"] || args.number_of_arguments() < 1 || args.number_of_arguments() > 2)
            std::cerr << "Usage: " << args.program() << " [options] <chart.ace> [<map.pdf>]\n" << args.usage_options() << '\n';
        else
            exit_code = draw(args);
    }
    catch (std::exception& err) {
        std::cerr << "ERROR: " << err.what() << '\n';
        exit_code = 2;
    }
    return exit_code;
}

// ----------------------------------------------------------------------

int draw(const argc_argv& args)
{
    const bool verbose = args["-v"] || args["--verbose"];
    size_t projection_no = 0;
    if (const auto pr1 = args["--projection"]; pr1.present())
        projection_no = pr1;
    else if (const auto pr2 = args["-p"]; pr2.present())
        projection_no = pr2;

    setup_dbs(args["--db-dir"].str(), verbose);

    ChartDraw chart_draw(std::make_shared<acmacs::chart::ChartModify>(acmacs::chart::import_from_file(args[0], acmacs::chart::Verify::None, do_report_time(args["--time"]))),
                         projection_no);
    if (args["--previous"])
        chart_draw.previous_chart(acmacs::chart::import_from_file(args["--previous"], acmacs::chart::Verify::None, do_report_time(args["--time"])));

    auto settings = settings_default();
    if (args["--init-settings"]) {
        auto write_settings = [&settings](std::ostream& out) { out << rjson::pretty(settings) << '\n'; };
        if (args["--init-settings"] == "-") {
            write_settings(std::cout);
        }
        else {
            std::ofstream out(args["--init-settings"].str());
            write_settings(out);
        }
    }

    settings.update(settings_builtin_mods());

    bool settings_loaded = false;
    auto load_settings = [&](argc_argv::strings aFilenames) {
        for (auto fn : aFilenames) {
            if (verbose)
                std::cerr << "DEBUG: reading settings from " << fn << '\n';
            settings.update(rjson::parse_file(fn, rjson::remove_comments::no));
            settings_loaded = true;
        }
    };
    load_settings(args["-s"]);
    load_settings(args["--settings"]);
    // std::cerr << "DEBUG: loaded settings\n" << settings.to_json_pp() << '\n';

    if (args["--apply"] || args["--apply-from"]) {
        const auto new_apply = args["--apply"] ? rjson::parse_string(static_cast<std::string_view>(args["--apply"])) : rjson::parse_string(acmacs::file::ifstream(args["--apply-from"]).read());
        if (settings_loaded) {
            if (const auto& old_apply_const = settings["apply"]; !old_apply_const.is_null()) {
                auto& old_apply = const_cast<rjson::value&>(old_apply_const);
                rjson::for_each(new_apply, [&old_apply](const rjson::value& element) { old_apply.append(element); });
            }
            else
                settings["apply"] = new_apply;
        }
        else {
            settings["apply"] = new_apply;
        }
    }
    else if (args["--clade"]) {
        settings["apply"] = rjson::array{"size_reset", "all_grey", "egg", "clades", "vaccines", "title"};
    }

    if (args["--point-scale"].present()) {
        static_cast<rjson::value&>(settings["apply"])
            .append(rjson::object{{"N", "point_scale"}, {"scale", static_cast<double>(args["--point-scale"])}, {"outline_scale", 1.0}});
    }

    if (args["--flip-ew"].present()) {
        static_cast<rjson::value&>(settings["apply"]).append(rjson::object{{"N", "flip"}, {"direction", "ew"}});
    }
    if (args["--flip-ns"].present()) {
        static_cast<rjson::value&>(settings["apply"]).append(rjson::object{{"N", "flip"}, {"direction", "ns"}});
    }
    if (auto r_opt = args["-r"]; r_opt.present()) {
        static_cast<rjson::value&>(settings["apply"]).append(rjson::object{{"N", "rotate"}, {"degrees", static_cast<double>(r_opt)}});
    }
    if (args["--rotate-degrees"].present()) {
        static_cast<rjson::value&>(settings["apply"])
            .append(rjson::object{{"N", "rotate"}, {"degrees", static_cast<double>(args["--rotate-degrees"])}});
    }

    try {
        apply_mods(chart_draw, settings["apply"], settings);
    }
    catch (std::exception& err) {
        throw std::runtime_error{"Cannot apply " + rjson::to_string(settings["apply"]) + ": " + err.what() + "\n settings:\n" + rjson::pretty(settings, rjson::emacs_indent::no) + '\n'};
    }

    chart_draw.calculate_viewport();

    if (args.number_of_arguments() < 2) {
        acmacs::file::temp output(static_cast<std::string>(fs::path(args[0]).stem()) + "--p" + std::to_string(projection_no), ".pdf");
        chart_draw.draw(output, 800, report_time::yes);
        acmacs::quicklook(output, 2);
    }
    else if (args[1] == "/dev/null" || args[1] == "/") { // do not generate pdf
    }
    else {
        chart_draw.draw(std::string(args[1]), 800, report_time::yes);
        if (args["--open"])
            acmacs::open(std::string(args[1]), 2);
        else if (args["--ql"])
            acmacs::quicklook(std::string(args[1]), 2);
    }

    if (const std::string save_settings(args["--init-settings"]); !save_settings.empty())
        acmacs::file::write(save_settings, rjson::pretty(settings));

    if (const std::string save(args["--save"]); !save.empty()) {
        chart_draw.save(save, args.program());
    }

    return 0;

} // draw

// ----------------------------------------------------------------------
/// Local Variables:
/// eval: (if (fboundp 'eu-rename-buffer) (eu-rename-buffer))
/// End:
<commit_msg>minor fix<commit_after>#include <iostream>
#include <fstream>
#include <string>
using namespace std::string_literals;

#include "acmacs-base/argc-argv.hh"
#include "acmacs-base/read-file.hh"
#include "acmacs-base/quicklook.hh"
#include "acmacs-chart-2/factory-import.hh"
#include "acmacs-map-draw/draw.hh"
#include "acmacs-map-draw/settings.hh"
#include "acmacs-map-draw/mod-applicator.hh"
#include "acmacs-map-draw/setup-dbs.hh"

// ----------------------------------------------------------------------

static int draw(const argc_argv& args);

int main(int argc, char* const argv[])
{
    int exit_code = 1;
    try {
        argc_argv args(argc, argv, {
                {"--apply", "", "json array to use as \"apply\", e.g. [\"all_grey\",\"egg\",\"clades\",\"labels\"]"},
                {"--apply-from", "", "read json array to use as \"apply\" from file (or stdin if \"-\""},
                {"--clade", false},
                {"--point-scale", 1.0},
                {"--rotate-degrees", 0.0, "counter clockwise"},
                {"-r", 0.0, "rotate in degrees, counter clockwise"},
                {"--flip-ew", false},
                {"--flip-ns", false},
                {"--settings", argc_argv::strings{}, "load settings from file"},
                {"-s", argc_argv::strings{}, "load settings from file"},
                {"--init-settings", "", "initialize (overwrite) settings file"},
                {"--save", "", "save resulting chart with modified projection and plot spec"},
                {"-h", false},
                {"--help", false},
                {"--help-mods", false},
                {"--help-select", false},
                {"--previous", ""},
                {"--open", false},
                {"--ql", false},
                {"--projection", 0L},
                {"-p", 0L, "projection"},
                {"--db-dir", ""},
                {"--time", false, "report time of loading chart"},
                {"--verbose", false},
                {"-v", false},
        });
        if (args["--help-mods"])
            std::cerr << settings_help_mods();
        else if (args["--help-select"])
            std::cerr << settings_help_select();
        else if (args["-h"] || args["--help"] || args.number_of_arguments() < 1 || args.number_of_arguments() > 2)
            std::cerr << "Usage: " << args.program() << " [options] <chart.ace> [<map.pdf>]\n" << args.usage_options() << '\n';
        else
            exit_code = draw(args);
    }
    catch (std::exception& err) {
        std::cerr << "ERROR: " << err.what() << '\n';
        exit_code = 2;
    }
    return exit_code;
}

// ----------------------------------------------------------------------

int draw(const argc_argv& args)
{
    const bool verbose = args["-v"] || args["--verbose"];
    size_t projection_no = 0;
    if (const auto pr1 = args["--projection"]; pr1.present())
        projection_no = pr1;
    else if (const auto pr2 = args["-p"]; pr2.present())
        projection_no = pr2;

    setup_dbs(args["--db-dir"].str(), verbose);

    ChartDraw chart_draw(std::make_shared<acmacs::chart::ChartModify>(acmacs::chart::import_from_file(args[0], acmacs::chart::Verify::None, do_report_time(args["--time"]))),
                         projection_no);
    if (args["--previous"])
        chart_draw.previous_chart(acmacs::chart::import_from_file(args["--previous"], acmacs::chart::Verify::None, do_report_time(args["--time"])));

    auto settings = settings_default();
    if (args["--init-settings"]) {
        auto write_settings = [&settings](std::ostream& out) { out << rjson::pretty(settings) << '\n'; };
        if (args["--init-settings"] == "-") {
            write_settings(std::cout);
        }
        else {
            std::ofstream out(args["--init-settings"].str());
            write_settings(out);
        }
    }

    settings.update(settings_builtin_mods());

    bool settings_loaded = false;
    auto load_settings = [&](argc_argv::strings aFilenames) {
        for (auto fn : aFilenames) {
            if (verbose)
                std::cerr << "DEBUG: reading settings from " << fn << '\n';
            settings.update(rjson::parse_file(fn, rjson::remove_comments::no));
            settings_loaded = true;
        }
    };
    load_settings(args["-s"]);
    load_settings(args["--settings"]);
    // std::cerr << "DEBUG: loaded settings\n" << settings.to_json_pp() << '\n';

    if (args["--apply"] || args["--apply-from"]) {
        const auto new_apply = args["--apply"] ? rjson::parse_string(static_cast<std::string_view>(args["--apply"])) : rjson::parse_string(acmacs::file::ifstream(args["--apply-from"]).read());
        if (settings_loaded) {
            if (const auto& old_apply_const = settings["apply"]; !old_apply_const.is_null()) {
                auto& old_apply = const_cast<rjson::value&>(old_apply_const);
                rjson::for_each(new_apply, [&old_apply](const rjson::value& element) { old_apply.append(element); });
            }
            else
                settings["apply"] = new_apply;
        }
        else {
            settings["apply"] = new_apply;
        }
    }
    else if (args["--clade"]) {
        settings["apply"] = rjson::array{"size_reset", "all_grey", "egg", "clades", "vaccines", "title"};
    }

    if (args["--point-scale"].present()) {
        static_cast<rjson::value&>(settings["apply"])
            .append(rjson::object{{"N", "point_scale"}, {"scale", static_cast<double>(args["--point-scale"])}, {"outline_scale", 1.0}});
    }

    if (args["--flip-ew"].present()) {
        static_cast<rjson::value&>(settings["apply"]).append(rjson::object{{"N", "flip"}, {"direction", "ew"}});
    }
    if (args["--flip-ns"].present()) {
        static_cast<rjson::value&>(settings["apply"]).append(rjson::object{{"N", "flip"}, {"direction", "ns"}});
    }
    if (auto r_opt = args["-r"]; r_opt.present()) {
        static_cast<rjson::value&>(settings["apply"]).append(rjson::object{{"N", "rotate"}, {"degrees", static_cast<double>(r_opt)}});
    }
    if (args["--rotate-degrees"].present()) {
        static_cast<rjson::value&>(settings["apply"])
            .append(rjson::object{{"N", "rotate"}, {"degrees", static_cast<double>(args["--rotate-degrees"])}});
    }

    try {
        apply_mods(chart_draw, settings["apply"], settings);
    }
    catch (std::exception& err) {
        // throw std::runtime_error{"Cannot apply " + rjson::to_string(settings["apply"]) + ": " + err.what() + "\n settings:\n" + rjson::pretty(settings, rjson::emacs_indent::no) + '\n'};
        throw std::runtime_error{"Cannot apply " + rjson::to_string(settings["apply"]).substr(0, 200) + ": " + err.what()};
    }

    chart_draw.calculate_viewport();

    if (args.number_of_arguments() < 2) {
        acmacs::file::temp output(static_cast<std::string>(fs::path(args[0]).stem()) + "--p" + std::to_string(projection_no), ".pdf");
        chart_draw.draw(output, 800, report_time::yes);
        acmacs::quicklook(output, 2);
    }
    else if (args[1] == "/dev/null" || args[1] == "/") { // do not generate pdf
    }
    else {
        chart_draw.draw(std::string(args[1]), 800, report_time::yes);
        if (args["--open"])
            acmacs::open(std::string(args[1]), 2);
        else if (args["--ql"])
            acmacs::quicklook(std::string(args[1]), 2);
    }

    if (const std::string save_settings(args["--init-settings"]); !save_settings.empty())
        acmacs::file::write(save_settings, rjson::pretty(settings));

    if (const std::string save(args["--save"]); !save.empty()) {
        chart_draw.save(save, args.program());
    }

    return 0;

} // draw

// ----------------------------------------------------------------------
/// Local Variables:
/// eval: (if (fboundp 'eu-rename-buffer) (eu-rename-buffer))
/// End:
<|endoftext|>


<commit_before>#pragma once

#include "acmacs-base/rjson-v3.hh"
#include "acmacs-base/to-json.hh"

// ----------------------------------------------------------------------

namespace acmacs::settings::inline v2
{
    class error : public std::runtime_error
    {
        using std::runtime_error::runtime_error;
    };

    class Settings
    {
      public:
        enum class toplevel_only { no, yes };

        Settings() = default;
        Settings(const std::vector<std::string_view>& filenames) { load(filenames); }
        virtual ~Settings() = default;

        // read settings from files, upon reading each file apply "init" in it (if found)
        void load(std::string_view filename);
        void load(const std::vector<std::string_view>& filenames);
        void reload();          // reset environament, re-load previously loaded files, apply "init" in loaded files

        // substitute vars in name, find name in environment or in data_ or in built-in and apply it
        // if name starts with ? do nothing
        // if name not found, throw
        virtual void apply(std::string_view name /* = "main" */);

        void setenv(std::string_view key, rjson::v3::value&& value) { environment_.add(key, std::move(value)); }
        void setenv(std::string_view key, std::string_view value) { setenv(key, rjson::v3::parse_string(value)); }
        void setenv_toplevel(std::string_view key, std::string_view value) { environment_.add_to_toplevel(key, value); } // rjson::v3::parse_string(fmt::format("\"{}\"", value))); }
        void setenv_toplevel(std::string_view key, bool value) { environment_.add_to_toplevel(key, value); } // rjson::v3::parse_string(fmt::format("{}", value))); }

        const rjson::v3::value& getenv_single_substitution(std::string_view key, toplevel_only a_toplevel_only = toplevel_only::no) const
        {
            return environment_.get(environment_.substitute_to_value(key).to<std::string_view>(), a_toplevel_only);
        }

        // returns const_null if not found
        const rjson::v3::value& getenv(std::string_view key, toplevel_only a_toplevel_only = toplevel_only::no) const;
        const rjson::v3::value& substitute(const rjson::v3::value& source) const;

        template <typename T> std::decay_t<T> getenv(std::string_view key, T&& a_default, toplevel_only a_toplevel_only = toplevel_only::no) const
        {
            if (const auto& val = getenv(key, a_toplevel_only); !val.is_null())
                return val.to<std::decay_t<T>>();
            else
                return std::move(a_default);
        }

        template <typename T> void getenv_copy_if_present(std::string_view key, T& target, toplevel_only a_toplevel_only = toplevel_only::no) const
        {
            static_assert(!std::is_same_v<std::decay_t<T>, std::string_view>);
            if (const auto& val = getenv(key, a_toplevel_only); !val.is_null())
                target = val.to<std::decay_t<T>>();
        }

        template <typename T1, typename T2> void getenv_extract_copy_if_present(std::string_view key, T2& target, toplevel_only a_toplevel_only = toplevel_only::no) const
        {
            static_assert(!std::is_same_v<std::decay_t<T2>, std::string_view>);
            if (const auto& val = getenv(key, a_toplevel_only); !val.is_null())
                target = T2{val.to<std::decay_t<T1>>()};
        }

        void printenv() const { environment_.print(); }

        template <typename... Args> void getenv_to_json(to_json::object& obj, std::string_view key, Args&&... keys)
        {
            if (const auto& val = getenv(key); !val.is_null())
                obj << to_json::key_val(key, to_json::raw(rjson::v3::format(val)));
            if constexpr (sizeof...(keys) > 0)
                getenv_to_json(obj, keys...);
        }

        template <typename ... Args> std::string getenv_to_json(Args&& ... keys)
        {
            to_json::object obj;
            getenv_to_json(obj, keys...);
            return obj.compact(to_json::json::embed_space::no);
        }

      protected:
        template <typename... Key> const rjson::v3::value& get(Key&&... keys) const
        {
            return loaded_data_.get(keys ...);
        }

        void apply(const rjson::v3::value& entry);
        virtual bool apply_built_in(std::string_view name); // returns true if built-in command with that name found and applied

        // substitute vars in name, find name in the top of data_ and apply it
        // do nothing if name starts with ? or if it is not found in top of data_
        virtual void apply_top(std::string_view name);

      private:
        class LoadedDataFiles
        {
          public:
            LoadedDataFiles() = default;

            void load(std::string_view filename);
            void reload(Settings& settings);
            const rjson::v3::value& get_top(std::string_view name) const { return file_data_.front()[name]; }

            template <typename... Key> const rjson::v3::value& get(Key&&... keys) const
            {
                for (const auto& per_file : file_data_) {
                    if (const auto& val = per_file.get(std::forward<Key>(keys)...); !val.is_null())
                        return val;
                }
                return rjson::v3::const_null;
            }

            void report() const
            {
                for (const auto& per_file : file_data_)
                    AD_DEBUG("{}", per_file);
            }

          private:
            std::vector<std::string> filenames_;
            std::vector<rjson::v3::value_read> file_data_;
        };

        class Environment
        {
          public:
            Environment() { push(); }

            const rjson::v3::value& get(std::string_view key, toplevel_only a_toplevel_only) const;
            void push() { env_data_.emplace_back(); }
            void pop() { env_data_.erase(std::prev(std::end(env_data_))); }
            size_t size() const { return env_data_.size(); }
            void add(std::string_view key, const rjson::v3::value& val) { env_data_.rbegin()->emplace_or_replace(std::string{key}, val); }
            void add(std::string_view key, rjson::v3::value&& val) { env_data_.rbegin()->emplace_or_replace(key, std::move(val)); }
            void add_to_toplevel(std::string_view key, rjson::v3::value&& val) { env_data_.begin()->emplace_or_replace(std::string{key}, std::move(val)); }
            void add_to_toplevel(std::string_view key, std::string_view value)  { env_data_.begin()->emplace_or_replace(std::string{key}, rjson::v3::detail::string{rjson::v3::detail::string::with_content, value}); }
            void add_to_toplevel(std::string_view key, bool value) { env_data_.begin()->emplace_or_replace(std::string{key}, rjson::v3::detail::boolean{value}); }
            void print() const;
            void print_key_value() const;

            std::string substitute_to_string(std::string_view source) const noexcept;
            const rjson::v3::value& substitute_to_value(std::string_view source) const noexcept;

            // abort if source is empty
            // const rjson::v3::value* if substitution requested for the whole source
            // std::string if no substitution requested (returns source) or part of the source substituted
            using substitute_result_t = std::variant<const rjson::v3::value*, std::string>;
            substitute_result_t substitute(std::string_view source) const;

          private:
            std::vector<acmacs::small_map_with_unique_keys_t<std::string, rjson::v3::value>> env_data_;

        };

        LoadedDataFiles loaded_data_;
        mutable Environment environment_;
        mutable bool warn_if_set_used_{false};

        void push_and_apply(const rjson::v3::detail::object& entry);
        void apply_if();
        bool eval_condition(const rjson::v3::value& condition) const;
        bool eval_and(const rjson::v3::value& condition) const;
        bool eval_or(const rjson::v3::value& condition) const;
        bool eval_not(const rjson::v3::value& condition) const { return !eval_condition(condition); }
        bool eval_empty(const rjson::v3::value& condition, bool true_if_empty) const;
        bool eval_equal(const rjson::v3::value& condition) const;
        bool eval_not_equal(const rjson::v3::value& condition) const { return !eval_equal(condition); }

        friend class Subenvironment;

    }; // class Settings

} // namespace acmacs::settings::inline v2

// ----------------------------------------------------------------------
/// Local Variables:
/// eval: (if (fboundp 'eu-rename-buffer) (eu-rename-buffer))
/// End:
<commit_msg>settings::v2::getenv_toplevel<commit_after>#pragma once

#include "acmacs-base/rjson-v3.hh"
#include "acmacs-base/to-json.hh"

// ----------------------------------------------------------------------

namespace acmacs::settings::inline v2
{
    class error : public std::runtime_error
    {
        using std::runtime_error::runtime_error;
    };

    namespace detail
    {
        using env_data_t = acmacs::small_map_with_unique_keys_t<std::string, rjson::v3::value>;
    }

    class Settings
    {
      public:
        enum class toplevel_only { no, yes };

        Settings() = default;
        Settings(const std::vector<std::string_view>& filenames) { load(filenames); }
        virtual ~Settings() = default;

        // read settings from files, upon reading each file apply "init" in it (if found)
        void load(std::string_view filename);
        void load(const std::vector<std::string_view>& filenames);
        void reload();          // reset environament, re-load previously loaded files, apply "init" in loaded files

        // substitute vars in name, find name in environment or in data_ or in built-in and apply it
        // if name starts with ? do nothing
        // if name not found, throw
        virtual void apply(std::string_view name /* = "main" */);

        void setenv(std::string_view key, rjson::v3::value&& value) { environment_.add(key, std::move(value)); }
        void setenv(std::string_view key, std::string_view value) { setenv(key, rjson::v3::parse_string(value)); }
        void setenv_toplevel(std::string_view key, std::string_view value) { environment_.add_to_toplevel(key, value); } // rjson::v3::parse_string(fmt::format("\"{}\"", value))); }
        void setenv_toplevel(std::string_view key, bool value) { environment_.add_to_toplevel(key, value); } // rjson::v3::parse_string(fmt::format("{}", value))); }

        const rjson::v3::value& getenv_single_substitution(std::string_view key, toplevel_only a_toplevel_only = toplevel_only::no) const
        {
            return environment_.get(environment_.substitute_to_value(key).to<std::string_view>(), a_toplevel_only);
        }

        // returns const_null if not found
        const rjson::v3::value& getenv(std::string_view key, toplevel_only a_toplevel_only = toplevel_only::no) const;
        const rjson::v3::value& substitute(const rjson::v3::value& source) const;
        const detail::env_data_t& getenv_toplevel() const { return environment_.toplevel(); }

        template <typename T> std::decay_t<T> getenv(std::string_view key, T&& a_default, toplevel_only a_toplevel_only = toplevel_only::no) const
        {
            if (const auto& val = getenv(key, a_toplevel_only); !val.is_null())
                return val.to<std::decay_t<T>>();
            else
                return std::move(a_default);
        }

        template <typename T> void getenv_copy_if_present(std::string_view key, T& target, toplevel_only a_toplevel_only = toplevel_only::no) const
        {
            static_assert(!std::is_same_v<std::decay_t<T>, std::string_view>);
            if (const auto& val = getenv(key, a_toplevel_only); !val.is_null())
                target = val.to<std::decay_t<T>>();
        }

        template <typename T1, typename T2> void getenv_extract_copy_if_present(std::string_view key, T2& target, toplevel_only a_toplevel_only = toplevel_only::no) const
        {
            static_assert(!std::is_same_v<std::decay_t<T2>, std::string_view>);
            if (const auto& val = getenv(key, a_toplevel_only); !val.is_null())
                target = T2{val.to<std::decay_t<T1>>()};
        }

        void printenv() const { environment_.print(); }

        template <typename... Args> void getenv_to_json(to_json::object& obj, std::string_view key, Args&&... keys)
        {
            if (const auto& val = getenv(key); !val.is_null())
                obj << to_json::key_val(key, to_json::raw(rjson::v3::format(val)));
            if constexpr (sizeof...(keys) > 0)
                getenv_to_json(obj, keys...);
        }

        template <typename ... Args> std::string getenv_to_json(Args&& ... keys)
        {
            to_json::object obj;
            getenv_to_json(obj, keys...);
            return obj.compact(to_json::json::embed_space::no);
        }

      protected:
        template <typename... Key> const rjson::v3::value& get(Key&&... keys) const
        {
            return loaded_data_.get(keys ...);
        }

        void apply(const rjson::v3::value& entry);
        virtual bool apply_built_in(std::string_view name); // returns true if built-in command with that name found and applied

        // substitute vars in name, find name in the top of data_ and apply it
        // do nothing if name starts with ? or if it is not found in top of data_
        virtual void apply_top(std::string_view name);

      private:
        class LoadedDataFiles
        {
          public:
            LoadedDataFiles() = default;

            void load(std::string_view filename);
            void reload(Settings& settings);
            const rjson::v3::value& get_top(std::string_view name) const { return file_data_.front()[name]; }

            template <typename... Key> const rjson::v3::value& get(Key&&... keys) const
            {
                for (const auto& per_file : file_data_) {
                    if (const auto& val = per_file.get(std::forward<Key>(keys)...); !val.is_null())
                        return val;
                }
                return rjson::v3::const_null;
            }

            void report() const
            {
                for (const auto& per_file : file_data_)
                    AD_DEBUG("{}", per_file);
            }

          private:
            std::vector<std::string> filenames_;
            std::vector<rjson::v3::value_read> file_data_;
        };

        class Environment
        {
          public:
            Environment() { push(); }

            const rjson::v3::value& get(std::string_view key, toplevel_only a_toplevel_only) const;
            void push() { env_data_.emplace_back(); }
            void pop() { env_data_.erase(std::prev(std::end(env_data_))); }
            size_t size() const { return env_data_.size(); }
            void add(std::string_view key, const rjson::v3::value& val) { env_data_.rbegin()->emplace_or_replace(std::string{key}, val); }
            void add(std::string_view key, rjson::v3::value&& val) { env_data_.rbegin()->emplace_or_replace(key, std::move(val)); }
            void add_to_toplevel(std::string_view key, rjson::v3::value&& val) { env_data_.begin()->emplace_or_replace(std::string{key}, std::move(val)); }
            void add_to_toplevel(std::string_view key, std::string_view value)  { env_data_.begin()->emplace_or_replace(std::string{key}, rjson::v3::detail::string{rjson::v3::detail::string::with_content, value}); }
            void add_to_toplevel(std::string_view key, bool value) { env_data_.begin()->emplace_or_replace(std::string{key}, rjson::v3::detail::boolean{value}); }
            void print() const;
            void print_key_value() const;

            std::string substitute_to_string(std::string_view source) const noexcept;
            const rjson::v3::value& substitute_to_value(std::string_view source) const noexcept;
            const detail::env_data_t& toplevel() const { return env_data_.back(); }

            // abort if source is empty
            // const rjson::v3::value* if substitution requested for the whole source
            // std::string if no substitution requested (returns source) or part of the source substituted
            using substitute_result_t = std::variant<const rjson::v3::value*, std::string>;
            substitute_result_t substitute(std::string_view source) const;

          private:
            std::vector<detail::env_data_t> env_data_;

        };

        LoadedDataFiles loaded_data_;
        mutable Environment environment_;
        mutable bool warn_if_set_used_{false};

        void push_and_apply(const rjson::v3::detail::object& entry);
        void apply_if();
        bool eval_condition(const rjson::v3::value& condition) const;
        bool eval_and(const rjson::v3::value& condition) const;
        bool eval_or(const rjson::v3::value& condition) const;
        bool eval_not(const rjson::v3::value& condition) const { return !eval_condition(condition); }
        bool eval_empty(const rjson::v3::value& condition, bool true_if_empty) const;
        bool eval_equal(const rjson::v3::value& condition) const;
        bool eval_not_equal(const rjson::v3::value& condition) const { return !eval_equal(condition); }

        friend class Subenvironment;

    }; // class Settings

} // namespace acmacs::settings::inline v2

// ----------------------------------------------------------------------
/// Local Variables:
/// eval: (if (fboundp 'eu-rename-buffer) (eu-rename-buffer))
/// End:
<|endoftext|>