<commit_before>#include <common/buffer.h>
#include <common/endian.h>

#include <xcodec/xcodec.h>
#include <xcodec/xcodec_cache.h>
#include <xcodec/xcodec_decoder.h>
#include <xcodec/xcodec_encoder.h>
#include <xcodec/xcodec_hash.h>

XCodecDecoder::XCodecDecoder(XCodec *codec, XCodecEncoder *encoder)
: log_("/xcodec/decoder"),
  cache_(codec->cache_),
  window_(),
  encoder_(encoder),
  queued_(),
  asked_()
{ }

XCodecDecoder::~XCodecDecoder()
{ }

/*
 * Decode an XCodec-encoded stream.  Returns false if there was an
 * inconsistency, error or unrecoverable condition in the stream.
 * Returns true if we were able to process the stream entirely or
 * expect to be able to finish processing it once more data arrives.
 * The input buffer is cleared of anything we can parse right now.
 *
 * Since some events later in the stream (i.e. ASK or LEARN) may need
 * to be processed before some earlier in the stream (i.e. REF), we
 * parse the stream into a list of actions to take, performing them
 * as we go if possible, otherwise queueing them to occur until the
 * action that is blocking the stream has been satisfied or the stream
 * has been closed.
 *
 * XXX For now we will ASK in every stream where an unknown hash has
 * occurred and expect a LEARN in all of them.  In the future, it is
 * desirable to optimize this.  Especially once we start putting an
 * instance UUID in the HELLO message and can tell which streams
 * share an originator.
 */
bool
XCodecDecoder::decode(Buffer *output, Buffer *input)
{
	for (;;) {
		/*
		 * If there is queued data and no more outstanding hashes,
		 * handle the queued data.
		 */
		if (!queued_.empty() && asked_.empty()) {
			DEBUG(log_) << "Handling queued data.";
			/* XXX input->prepend(queued_);  */

			Buffer tmp(queued_);
			queued_.clear();
			tmp.append(input);
			input->clear();
			input->append(tmp);
		}

		if (input->empty())
			break;

		unsigned off;
		if (!input->find(XCODEC_MAGIC, &off)) {
			if (queued_.empty()) {
				output->append(input);
			} else {
				queued_.append(input);
			}
			input->clear();
			break;
		}

		if (off != 0) {
			if (queued_.empty()) {
				output->append(input, off);
				input->skip(off);
			} else {
				queued_.append(input, off);
				input->skip(off);
			}
		}

		/*
		 * Need the following byte at least.
		 */
		if (input->length() == 1)
			break;

		uint8_t op;
		input->copyout(&op, sizeof XCODEC_MAGIC, sizeof op);
		switch (op) {
		case XCODEC_OP_HELLO:
			if (input->length() < sizeof XCODEC_MAGIC + sizeof op + sizeof (uint8_t))
				return (true);
			else {
				if (!queued_.empty()) {
					ERROR(log_) << "Got <HELLO> with data queued.";
					return (false);
				}

				uint8_t len;
				input->copyout(&len, sizeof XCODEC_MAGIC + sizeof op, sizeof len);
				if (input->length() < sizeof XCODEC_MAGIC + sizeof op + sizeof len + len)
					return (true);
				switch (len) {
				case 0:
					break;
				default:
					ERROR(log_) << "Unsupported <HELLO> length: " << (unsigned)len;
					return (false);
				}
				input->skip(sizeof XCODEC_MAGIC + sizeof op + sizeof len + len);
			}
			break;
		case XCODEC_OP_ESCAPE:
			if (queued_.empty()) {
				output->append(XCODEC_MAGIC);
			} else {
				queued_.append(XCODEC_MAGIC);
				queued_.append(XCODEC_OP_ESCAPE);
			}
			input->skip(sizeof XCODEC_MAGIC + sizeof op);
			break;
		case XCODEC_OP_EXTRACT:
			if (input->length() < sizeof XCODEC_MAGIC + sizeof op + XCODEC_SEGMENT_LENGTH)
				return (true);
			else {
				input->skip(sizeof XCODEC_MAGIC + sizeof op);

				BufferSegment *seg;
				input->copyout(&seg, XCODEC_SEGMENT_LENGTH);
				input->skip(XCODEC_SEGMENT_LENGTH);

				uint64_t hash = XCodecHash<XCODEC_SEGMENT_LENGTH>::hash(seg->data());
				BufferSegment *oseg = cache_->lookup(hash);
				if (oseg != NULL) {
					if (oseg->match(seg)) {
						seg->unref();
						seg = oseg;
					} else {
						ERROR(log_) << "Collision in <EXTRACT>.";
						seg->unref();
						return (false);
					}
				} else {
					cache_->enter(hash, seg);
				}

				/* Just in case.  */
				asked_.erase(hash);

				if (queued_.empty()) {
					window_.declare(hash, seg);
					output->append(seg);
				} else {
					queued_.append(XCODEC_MAGIC);
					queued_.append(XCODEC_OP_EXTRACT);
					queued_.append(seg);
				}
				seg->unref();
			}
			break;
		case XCODEC_OP_REF:
			if (input->length() < sizeof XCODEC_MAGIC + sizeof op + sizeof (uint64_t))
				return (true);
			else {
				uint64_t behash;
				input->moveout((uint8_t *)&behash, sizeof XCODEC_MAGIC + sizeof op, sizeof behash);
				uint64_t hash = BigEndian::decode(behash);

				BufferSegment *oseg = cache_->lookup(hash);
				if (oseg == NULL) {
					if (encoder_ == NULL) {
						ERROR(log_) << "Unknown hash in <REF>: " << hash;
						return (false);
					}

					if (asked_.find(hash) == asked_.end()) {
						DEBUG(log_) << "Sending <ASK>, waiting for <LEARN>.";
						encoder_->encode_ask(hash);
						asked_.insert(hash);
					} else {
						DEBUG(log_) << "Already sent <ASK>, waiting for <LEARN>.";
					}

					queued_.append(XCODEC_MAGIC);
					queued_.append(XCODEC_OP_REF);
					queued_.append((const uint8_t *)&behash, sizeof behash);
				} else {
					if (queued_.empty()) {
						window_.declare(hash, oseg);
						output->append(oseg);
					} else {
						queued_.append(XCODEC_MAGIC);
						queued_.append(XCODEC_OP_REF);
						queued_.append((const uint8_t *)&behash, sizeof behash);
					}
					oseg->unref();
				}
			}
			break;
		case XCODEC_OP_BACKREF:
			if (input->length() < sizeof XCODEC_MAGIC + sizeof op + sizeof (uint8_t))
				return (true);
			else {
				if (queued_.empty()) {
					uint8_t idx;
					input->moveout(&idx, sizeof XCODEC_MAGIC + sizeof op, sizeof idx);

					BufferSegment *oseg = window_.dereference(idx);
					if (oseg == NULL) {
						ERROR(log_) << "Index not present in <BACKREF> window: " << (unsigned)idx;
						return (false);
					}

					output->append(oseg);
					oseg->unref();
				} else {
					input->moveout(&queued_, sizeof XCODEC_MAGIC + sizeof op + sizeof (uint8_t));
				}
			}
			break;
		case XCODEC_OP_LEARN:
			if (input->length() < sizeof XCODEC_MAGIC + sizeof op + XCODEC_SEGMENT_LENGTH)
				return (true);
			else {
				BufferSegment *seg;
				input->copyout(&seg, XCODEC_SEGMENT_LENGTH);
				input->skip(XCODEC_SEGMENT_LENGTH);

				uint64_t hash = XCodecHash<XCODEC_SEGMENT_LENGTH>::hash(seg->data());
				BufferSegment *oseg = cache_->lookup(hash);
				if (oseg != NULL) {
					if (!oseg->match(seg)) {
						oseg->unref();
						ERROR(log_) << "Collision in <LEARN>.";
						seg->unref();
						return (false);
					}
					oseg->unref();
					DEBUG(log_) << "Redundant <LEARN>.";
				} else {
					DEBUG(log_) << "Successful <LEARN>.";
					cache_->enter(hash, seg);
				}
				asked_.erase(hash);
				seg->unref();
			}
			break;
		case XCODEC_OP_ASK:
			if (input->length() < sizeof XCODEC_MAGIC + sizeof op + sizeof (uint64_t))
				return (true);
			else {
				if (encoder_ == NULL) {
					ERROR(log_) << "Cannot handle <ASK> without associated encoder.";
					return (false);
				}

				uint64_t behash;
				input->moveout((uint8_t *)&behash, sizeof XCODEC_MAGIC + sizeof op, sizeof behash);
				uint64_t hash = BigEndian::decode(behash);

				BufferSegment *oseg = cache_->lookup(hash);
				if (oseg == NULL) {
					ERROR(log_) << "Unknown hash in <ASK>: " << hash;
					return (false);
				}

				DEBUG(log_) << "Responding to <ASK> with <LEARN>.";
				encoder_->encode_learn(oseg);
				oseg->unref();
			}
			break;
		default:
			ERROR(log_) << "Unsupported XCodec opcode " << (unsigned)op << ".";
			return (false);
		}
	}

	ASSERT(input->empty());

	return (true);
}
<commit_msg>Skip magic and op in LEARN.<commit_after>#include <common/buffer.h>
#include <common/endian.h>

#include <xcodec/xcodec.h>
#include <xcodec/xcodec_cache.h>
#include <xcodec/xcodec_decoder.h>
#include <xcodec/xcodec_encoder.h>
#include <xcodec/xcodec_hash.h>

XCodecDecoder::XCodecDecoder(XCodec *codec, XCodecEncoder *encoder)
: log_("/xcodec/decoder"),
  cache_(codec->cache_),
  window_(),
  encoder_(encoder),
  queued_(),
  asked_()
{ }

XCodecDecoder::~XCodecDecoder()
{ }

/*
 * Decode an XCodec-encoded stream.  Returns false if there was an
 * inconsistency, error or unrecoverable condition in the stream.
 * Returns true if we were able to process the stream entirely or
 * expect to be able to finish processing it once more data arrives.
 * The input buffer is cleared of anything we can parse right now.
 *
 * Since some events later in the stream (i.e. ASK or LEARN) may need
 * to be processed before some earlier in the stream (i.e. REF), we
 * parse the stream into a list of actions to take, performing them
 * as we go if possible, otherwise queueing them to occur until the
 * action that is blocking the stream has been satisfied or the stream
 * has been closed.
 *
 * XXX For now we will ASK in every stream where an unknown hash has
 * occurred and expect a LEARN in all of them.  In the future, it is
 * desirable to optimize this.  Especially once we start putting an
 * instance UUID in the HELLO message and can tell which streams
 * share an originator.
 */
bool
XCodecDecoder::decode(Buffer *output, Buffer *input)
{
	for (;;) {
		/*
		 * If there is queued data and no more outstanding hashes,
		 * handle the queued data.
		 */
		if (!queued_.empty() && asked_.empty()) {
			DEBUG(log_) << "Handling queued data.";
			/* XXX input->prepend(queued_);  */

			Buffer tmp(queued_);
			queued_.clear();
			tmp.append(input);
			input->clear();
			input->append(tmp);
		}

		if (input->empty())
			break;

		unsigned off;
		if (!input->find(XCODEC_MAGIC, &off)) {
			if (queued_.empty()) {
				output->append(input);
			} else {
				queued_.append(input);
			}
			input->clear();
			break;
		}

		if (off != 0) {
			if (queued_.empty()) {
				output->append(input, off);
				input->skip(off);
			} else {
				queued_.append(input, off);
				input->skip(off);
			}
		}

		/*
		 * Need the following byte at least.
		 */
		if (input->length() == 1)
			break;

		uint8_t op;
		input->copyout(&op, sizeof XCODEC_MAGIC, sizeof op);
		switch (op) {
		case XCODEC_OP_HELLO:
			if (input->length() < sizeof XCODEC_MAGIC + sizeof op + sizeof (uint8_t))
				return (true);
			else {
				if (!queued_.empty()) {
					ERROR(log_) << "Got <HELLO> with data queued.";
					return (false);
				}

				uint8_t len;
				input->copyout(&len, sizeof XCODEC_MAGIC + sizeof op, sizeof len);
				if (input->length() < sizeof XCODEC_MAGIC + sizeof op + sizeof len + len)
					return (true);
				switch (len) {
				case 0:
					break;
				default:
					ERROR(log_) << "Unsupported <HELLO> length: " << (unsigned)len;
					return (false);
				}
				input->skip(sizeof XCODEC_MAGIC + sizeof op + sizeof len + len);
			}
			break;
		case XCODEC_OP_ESCAPE:
			if (queued_.empty()) {
				output->append(XCODEC_MAGIC);
			} else {
				queued_.append(XCODEC_MAGIC);
				queued_.append(XCODEC_OP_ESCAPE);
			}
			input->skip(sizeof XCODEC_MAGIC + sizeof op);
			break;
		case XCODEC_OP_EXTRACT:
			if (input->length() < sizeof XCODEC_MAGIC + sizeof op + XCODEC_SEGMENT_LENGTH)
				return (true);
			else {
				input->skip(sizeof XCODEC_MAGIC + sizeof op);

				BufferSegment *seg;
				input->copyout(&seg, XCODEC_SEGMENT_LENGTH);
				input->skip(XCODEC_SEGMENT_LENGTH);

				uint64_t hash = XCodecHash<XCODEC_SEGMENT_LENGTH>::hash(seg->data());
				BufferSegment *oseg = cache_->lookup(hash);
				if (oseg != NULL) {
					if (oseg->match(seg)) {
						seg->unref();
						seg = oseg;
					} else {
						ERROR(log_) << "Collision in <EXTRACT>.";
						seg->unref();
						return (false);
					}
				} else {
					cache_->enter(hash, seg);
				}

				/* Just in case.  */
				asked_.erase(hash);

				if (queued_.empty()) {
					window_.declare(hash, seg);
					output->append(seg);
				} else {
					queued_.append(XCODEC_MAGIC);
					queued_.append(XCODEC_OP_EXTRACT);
					queued_.append(seg);
				}
				seg->unref();
			}
			break;
		case XCODEC_OP_REF:
			if (input->length() < sizeof XCODEC_MAGIC + sizeof op + sizeof (uint64_t))
				return (true);
			else {
				uint64_t behash;
				input->moveout((uint8_t *)&behash, sizeof XCODEC_MAGIC + sizeof op, sizeof behash);
				uint64_t hash = BigEndian::decode(behash);

				BufferSegment *oseg = cache_->lookup(hash);
				if (oseg == NULL) {
					if (encoder_ == NULL) {
						ERROR(log_) << "Unknown hash in <REF>: " << hash;
						return (false);
					}

					if (asked_.find(hash) == asked_.end()) {
						DEBUG(log_) << "Sending <ASK>, waiting for <LEARN>.";
						encoder_->encode_ask(hash);
						asked_.insert(hash);
					} else {
						DEBUG(log_) << "Already sent <ASK>, waiting for <LEARN>.";
					}

					queued_.append(XCODEC_MAGIC);
					queued_.append(XCODEC_OP_REF);
					queued_.append((const uint8_t *)&behash, sizeof behash);
				} else {
					if (queued_.empty()) {
						window_.declare(hash, oseg);
						output->append(oseg);
					} else {
						queued_.append(XCODEC_MAGIC);
						queued_.append(XCODEC_OP_REF);
						queued_.append((const uint8_t *)&behash, sizeof behash);
					}
					oseg->unref();
				}
			}
			break;
		case XCODEC_OP_BACKREF:
			if (input->length() < sizeof XCODEC_MAGIC + sizeof op + sizeof (uint8_t))
				return (true);
			else {
				if (queued_.empty()) {
					uint8_t idx;
					input->moveout(&idx, sizeof XCODEC_MAGIC + sizeof op, sizeof idx);

					BufferSegment *oseg = window_.dereference(idx);
					if (oseg == NULL) {
						ERROR(log_) << "Index not present in <BACKREF> window: " << (unsigned)idx;
						return (false);
					}

					output->append(oseg);
					oseg->unref();
				} else {
					input->moveout(&queued_, sizeof XCODEC_MAGIC + sizeof op + sizeof (uint8_t));
				}
			}
			break;
		case XCODEC_OP_LEARN:
			if (input->length() < sizeof XCODEC_MAGIC + sizeof op + XCODEC_SEGMENT_LENGTH)
				return (true);
			else {
				input->skip(sizeof XCODEC_MAGIC + sizeof op);

				BufferSegment *seg;
				input->copyout(&seg, XCODEC_SEGMENT_LENGTH);
				input->skip(XCODEC_SEGMENT_LENGTH);

				uint64_t hash = XCodecHash<XCODEC_SEGMENT_LENGTH>::hash(seg->data());
				BufferSegment *oseg = cache_->lookup(hash);
				if (oseg != NULL) {
					if (!oseg->match(seg)) {
						oseg->unref();
						ERROR(log_) << "Collision in <LEARN>.";
						seg->unref();
						return (false);
					}
					oseg->unref();
					DEBUG(log_) << "Redundant <LEARN>.";
				} else {
					DEBUG(log_) << "Successful <LEARN>.";
					cache_->enter(hash, seg);
				}
				asked_.erase(hash);
				seg->unref();
			}
			break;
		case XCODEC_OP_ASK:
			if (input->length() < sizeof XCODEC_MAGIC + sizeof op + sizeof (uint64_t))
				return (true);
			else {
				if (encoder_ == NULL) {
					ERROR(log_) << "Cannot handle <ASK> without associated encoder.";
					return (false);
				}

				uint64_t behash;
				input->moveout((uint8_t *)&behash, sizeof XCODEC_MAGIC + sizeof op, sizeof behash);
				uint64_t hash = BigEndian::decode(behash);

				BufferSegment *oseg = cache_->lookup(hash);
				if (oseg == NULL) {
					ERROR(log_) << "Unknown hash in <ASK>: " << hash;
					return (false);
				}

				DEBUG(log_) << "Responding to <ASK> with <LEARN>.";
				encoder_->encode_learn(oseg);
				oseg->unref();
			}
			break;
		default:
			ERROR(log_) << "Unsupported XCodec opcode " << (unsigned)op << ".";
			return (false);
		}
	}

	ASSERT(input->empty());

	return (true);
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2001 Stephen Williams (steve@icarus.com)
 *
 *    This source code is free software; you can redistribute it
 *    and/or modify it in source code form under the terms of the GNU
 *    General Public License as published by the Free Software
 *    Foundation; either version 2 of the License, or (at your option)
 *    any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with this program; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 */
#if !defined(WINNT)
#ident "$Id: vpi_modules.cc,v 1.9 2001/10/14 18:42:46 steve Exp $"
#endif

# include  "config.h"
# include  "vpi_priv.h"
# include  "ivl_dlfcn.h"
# include  "vpithunk.h"
# include  <stdio.h>
# include  <string.h>

typedef void (*vlog_startup_routines_t)(void);
typedef int (*vpi_register_sim_t)(p_vpi_thunk tp);

const char* vpip_module_path[64] = {
      MODULE_DIR,
      0
};

unsigned vpip_module_path_cnt = 1;

void vpip_load_module(const char*name)
{
#ifdef __MINGW32__
      const char sep = '\\';
#else
      const char sep = '/';
#endif

      ivl_dll_t dll = 0;

      if (strchr(name, sep)) {
	      /* If the name has at least one directory character in
		 it, then assume it is a complete name, including any
		 possble .vpi suffix. */
	    dll = ivl_dlopen(name);

	    if (dll == 0) {
		  char buf[4096];
		  sprintf(buf, "%s.vpi", name);
		  dll = ivl_dlopen(buf);

		  if (dll == 0) {
			fprintf(stderr, "%s: Unable to link module\n", name);
			return;
		  }
	    }

      } else {
	    for (unsigned idx = 0
		       ; (dll == 0) && (idx < vpip_module_path_cnt)
		       ;  idx += 1) {
		  char buf[4096];
		  sprintf(buf, "%s%c%s.vpi", vpip_module_path[idx], sep, name);

		  dll = ivl_dlopen(buf);
	    }

	    if (dll == 0) {
		  fprintf(stderr, "%s: Unable to find a "
			  "%s.vpi module\n", name, name);
		  return;
	    }

      }

      void *regsub = ivl_dlsym(dll, LU "vpi_register_sim" TU);
      vpi_register_sim_t simreg = (vpi_register_sim_t)regsub;
      if (regsub == 0) {
	    fprintf(stderr, "%s: Unable to locate vpi_register_sim", name);
	    ivl_dlclose(dll);
	    return;
      }

      extern vpi_thunk vvpt;
      if (((simreg)(&vvpt)) == 0) {
	fprintf(stderr, "%s: vpi_register_sim returned zero", name);
	ivl_dlclose(dll);
	return;
      }

      void*table = ivl_dlsym(dll, LU "vlog_startup_routines" TU);
      if (table == 0) {
	    fprintf(stderr, "%s: no vlog_startup_routines\n", name);
	    ivl_dlclose(dll);
	    return;
      }

      vlog_startup_routines_t*routines = (vlog_startup_routines_t*)table;
      for (unsigned tmp = 0 ;  routines[tmp] ;  tmp += 1)
	    (routines[tmp])();

}

/*
 * $Log: vpi_modules.cc,v $
 * Revision 1.9  2001/10/14 18:42:46  steve
 *  Try appending .vpi to module names with directories.
 *
 * Revision 1.8  2001/07/30 02:44:05  steve
 *  Cleanup defines and types for mingw compile.
 *
 * Revision 1.7  2001/07/28 03:29:42  steve
 *  If module name has a /, skip the path search.
 *
 * Revision 1.6  2001/07/26 03:13:51  steve
 *  Make the -M flag add module search paths.
 *
 * Revision 1.5  2001/06/12 03:53:11  steve
 *  Change the VPI call process so that loaded .vpi modules
 *  use a function table instead of implicit binding.
 *
 * Revision 1.4  2001/05/22 02:14:47  steve
 *  Update the mingw build to not require cygwin files.
 *
 * Revision 1.3  2001/03/23 02:40:22  steve
 *  Add the :module header statement.
 *
 * Revision 1.2  2001/03/22 05:39:34  steve
 *  Test print that interferes with output.
 *
 * Revision 1.1  2001/03/16 01:44:34  steve
 *  Add structures for VPI support, and all the %vpi_call
 *  instruction. Get linking of VPI modules to work.
 *
 */

<commit_msg> Better linker error messages.<commit_after>/*
 * Copyright (c) 2001 Stephen Williams (steve@icarus.com)
 *
 *    This source code is free software; you can redistribute it
 *    and/or modify it in source code form under the terms of the GNU
 *    General Public License as published by the Free Software
 *    Foundation; either version 2 of the License, or (at your option)
 *    any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with this program; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 */
#if !defined(WINNT)
#ident "$Id: vpi_modules.cc,v 1.10 2002/03/05 05:31:52 steve Exp $"
#endif

# include  "config.h"
# include  "vpi_priv.h"
# include  "ivl_dlfcn.h"
# include  "vpithunk.h"
# include  <stdio.h>
# include  <string.h>
# include  <sys/types.h>
# include  <sys/stat.h>

typedef void (*vlog_startup_routines_t)(void);
typedef int (*vpi_register_sim_t)(p_vpi_thunk tp);

const char* vpip_module_path[64] = {
      MODULE_DIR,
      0
};

unsigned vpip_module_path_cnt = 1;

void vpip_load_module(const char*name)
{
      struct stat sb;
      int rc;
      char buf[4096];

#ifdef __MINGW32__
      const char sep = '\\';
#else
      const char sep = '/';
#endif

      ivl_dll_t dll = 0;
      buf[0] = 0;                     /* terminate the string */
      if (strchr(name, sep)) {
	      /* If the name has at least one directory character in
		 it, then assume it is a complete name, maybe including any
		 possble .vpi suffix. */
	    rc = stat(name, &sb);

	    if (rc != 0) {            /* did we find a file? */
	          /* no, try with a .vpi suffix too */
		  sprintf(buf, "%s.vpi", name);
		  rc = stat(buf, &sb);

		  if (rc != 0) {
			fprintf(stderr, "%s: Unable to find module file `%s' "
				"or `%s.vpi'.\n", name,name,buf);
			return;
		  }
	    } else {
	      strcpy(buf,name);   /* yes copy the name into the buffer */
	    }

      } else {
	    rc = -1;
	    for (unsigned idx = 0
		       ; (rc != 0) && (idx < vpip_module_path_cnt)
		       ;  idx += 1) {
		  sprintf(buf, "%s%c%s.vpi", vpip_module_path[idx], sep, name);

		  rc = stat(buf,&sb);
	    }

	    if (rc != 0) {
		  fprintf(stderr, "%s: Unable to find a "
			  "`%s.vpi' module on the search path.\n",
			  name, name);
		  return;
	    }

      }

      /* must have found some file that could possibly be a vpi module
       * try to open it as a shared object.
       */
      dll = ivl_dlopen(buf);
      if(dll==0) {
	/* hmm, this failed, let the user know what has really gone wrong */
	fprintf(stderr,"%s:`%s' failed to open using dlopen() because:\n"
		"    %s.\n",name,buf,dlerror());

	return;
      }

      void *regsub = ivl_dlsym(dll, LU "vpi_register_sim" TU);
      vpi_register_sim_t simreg = (vpi_register_sim_t)regsub;
      if (regsub == 0) {
	    fprintf(stderr, "%s: Unable to locate vpi_register_sim", name);
	    ivl_dlclose(dll);
	    return;
      }

      extern vpi_thunk vvpt;
      if (((simreg)(&vvpt)) == 0) {
	fprintf(stderr, "%s: vpi_register_sim returned zero", name);
	ivl_dlclose(dll);
	return;
      }

      void*table = ivl_dlsym(dll, LU "vlog_startup_routines" TU);
      if (table == 0) {
	    fprintf(stderr, "%s: no vlog_startup_routines\n", name);
	    ivl_dlclose(dll);
	    return;
      }

      vlog_startup_routines_t*routines = (vlog_startup_routines_t*)table;
      for (unsigned tmp = 0 ;  routines[tmp] ;  tmp += 1)
	    (routines[tmp])();

}

/*
 * $Log: vpi_modules.cc,v $
 * Revision 1.10  2002/03/05 05:31:52  steve
 *  Better linker error messages.
 *
 * Revision 1.9  2001/10/14 18:42:46  steve
 *  Try appending .vpi to module names with directories.
 *
 * Revision 1.8  2001/07/30 02:44:05  steve
 *  Cleanup defines and types for mingw compile.
 *
 * Revision 1.7  2001/07/28 03:29:42  steve
 *  If module name has a /, skip the path search.
 *
 * Revision 1.6  2001/07/26 03:13:51  steve
 *  Make the -M flag add module search paths.
 *
 * Revision 1.5  2001/06/12 03:53:11  steve
 *  Change the VPI call process so that loaded .vpi modules
 *  use a function table instead of implicit binding.
 *
 * Revision 1.4  2001/05/22 02:14:47  steve
 *  Update the mingw build to not require cygwin files.
 *
 * Revision 1.3  2001/03/23 02:40:22  steve
 *  Add the :module header statement.
 *
 * Revision 1.2  2001/03/22 05:39:34  steve
 *  Test print that interferes with output.
 *
 * Revision 1.1  2001/03/16 01:44:34  steve
 *  Add structures for VPI support, and all the %vpi_call
 *  instruction. Get linking of VPI modules to work.
 *
 */

<|endoftext|>


<commit_before>/**
 * @file Attribute.hpp
 * @author Denis Kotov
 * @date 06 Nov 2017
 * @brief Contains Attribute class.
 * It is an attribute class based on icc::Event class
 * @copyright Denis Kotov, MIT License. Open source: https://github.com/redradist/Inter-Component-Communication.git
 */

#ifndef FORECAST_ATTRIBUTE_HPP
#define FORECAST_ATTRIBUTE_HPP

#include "Event.hpp"

namespace icc {

/**
 * Attribute class that is used for storing some value and
 * notify when it is changed
 * @tparam _Field
 */
template<typename _FieldType,
         typename _Field = typename std::remove_reference<
                             typename std::remove_cv<_FieldType>::type>::type>
class Attribute
    : public Event<void(const _Field &)> {
  static_assert(std::is_copy_constructible<_Field>::value,
                "_Field is not copy constructable !!");
 public:
  /**
   * Method is used to set attribute value
   * @param _field Value to set
   */
  void setAttribute(const _Field &_field) {
    if (!isEqual(_field)) {
      field_ = _field;
      this->operator()(field_);
    }
  }

  /**
   * Method is used to move attribute value
   * @param _field Value to move
   */
  void setAttribute(_Field && _field) {
    if (!isEqual(_field)) {
      field_ = std::move(_field);
      this->operator()(field_);
    }
  }

  /**
   * Method is used to get attribute value
   * @return Attribute value
   */
  _Field getAttribute() {
    return field_;
  }

  /**
   * Helper method is used for simplification of set attribute
   * @param _field Value of attribute to set
   * @return
   */
  Attribute & operator=(const _Field &_field) {
    setAttribute(_field);
    return *this;
  }

  /**
   * Helper method is used for simplification of
   * move new value into attribute
   * @param _field Value of attribute to set
   * @return
   */
  Attribute & operator=(_Field && _field) {
    setAttribute(std::move(_field));
    return *this;
  }

  /**
   *
   * @return
   */
  operator _Field() const {
    return field_;
  }

 protected:
  /**
   * Helper method is used to check that fields are equal
   * @param _field
   * @return
   */
  bool isEqual(const _Field &_field) const {
    return field_ == _field;
  }

 private:
  _Field field_;
};

}

#endif //FORECAST_ATTRIBUTE_HPP
<commit_msg>Refactored names in Attribute class<commit_after>/**
 * @file Attribute.hpp
 * @author Denis Kotov
 * @date 06 Nov 2017
 * @brief Contains Attribute class.
 * It is an attribute class based on icc::Event class
 * @copyright Denis Kotov, MIT License. Open source: https://github.com/redradist/Inter-Component-Communication.git
 */

#ifndef FORECAST_ATTRIBUTE_HPP
#define FORECAST_ATTRIBUTE_HPP

#include "Event.hpp"

namespace icc {

/**
 * Attribute class that is used for storing some value and
 * notify when it is changed
 * @tparam _Field
 */
template<typename _FieldType,
         typename _Field = typename std::remove_reference<
                             typename std::remove_cv<_FieldType>::type>::type>
class Attribute
    : public Event<void(const _Field &)> {
  static_assert(std::is_copy_constructible<_Field>::value,
                "_Field is not copy constructable !!");
 public:
  /**
   * Method is used to set attribute value
   * @param _field Value to set
   */
  void setValue(const _Field &_field) {
    if (!isEqual(_field)) {
      field_ = _field;
      this->operator()(field_);
    }
  }

  /**
   * Method is used to move attribute value
   * @param _field Value to move
   */
  void setValue(_Field && _field) {
    if (!isEqual(_field)) {
      field_ = std::move(_field);
      this->operator()(field_);
    }
  }

  /**
   * Method is used to get attribute value
   * @return Attribute value
   */
  _Field getValue() {
    return field_;
  }

  /**
   * Helper method is used for simplification of set attribute
   * @param _field Value of attribute to set
   * @return
   */
  Attribute & operator=(const _Field &_field) {
    setValue(_field);
    return *this;
  }

  /**
   * Helper method is used for simplification of
   * move new value into attribute
   * @param _field Value of attribute to set
   * @return
   */
  Attribute & operator=(_Field && _field) {
    setValue(std::move(_field));
    return *this;
  }

  /**
   *
   * @return
   */
  operator _Field() const {
    return field_;
  }

 protected:
  /**
   * Helper method is used to check that fields are equal
   * @param _field
   * @return
   */
  bool isEqual(const _Field &_field) const {
    return field_ == _field;
  }

 private:
  _Field field_;
};

}

#endif //FORECAST_ATTRIBUTE_HPP
<|endoftext|>


<commit_before>#include <pybind11/pybind11.h>
#include <pybind11/stl_bind.h>
#include <pybind11/numpy.h>
#include <pybind11/eigen.h>
#include <pybind11/operators.h>
#include <pybind11/functional.h>

#include <core.h>
#include <space.h>
#include <move.h>
#include <analysis.h>
#include <potentials.h>
#include <regions.h>
#include <montecarlo.h>
#include <energy.h>

namespace py = pybind11;
using namespace Faunus;

typedef typename Space::GroupType Tgroup;
typedef Energy::Hamiltonian Thamiltonian;
typedef MetropolisMonteCarlo Tmcsimulation;

inline json dict2json(py::dict dict) {
    py::object dumps = py::module::import("json").attr("dumps");
    return json::parse( dumps(dict).cast<std::string>() );
} // python dict --> c++ json

inline json list2json(py::list list) {
    py::object dumps = py::module::import("json").attr("dumps");
    return json::parse(dumps(list).cast<std::string>());
} // python list --> c++ json

inline py::dict json2dict(const json &j) {
    py::object loads = py::module::import("json").attr("loads");
    return loads( j.dump() ) ;
}

template<class T>
std::unique_ptr<T> from_dict(py::dict dict) {
    auto ptr = new T();
    *ptr = dict2json(dict);
    return std::unique_ptr<T>(ptr);
} // convert py::dict to T through Faunus::json

PYBIND11_MODULE(pyfaunus, m)
{
    using namespace pybind11::literals;

    // json
    py::class_<json>(m, "json")
        .def(py::init( [](std::string arg) {
                    return std::unique_ptr<json>(new json(json::parse(arg)));
                    } ) )
    .def(py::init([](py::dict dict) {
                py::object dumps = py::module::import("json").attr("dumps");
                std::string s = dumps(dict).cast<std::string>();
                return std::unique_ptr<json>(new json(json::parse(s)));
                } ) );

    // Random
    py::class_<Random>(m, "Random")
        .def(py::init<>())
        .def("__call__", [](Random &self){ return self(); } ); // function operator

    m.attr("random") = &Faunus::random; // global instance

    // Geometries
    py::enum_<Geometry::VolumeMethod>(m, "VolumeMethod")
        .value("ISOTROPIC", Geometry::VolumeMethod::ISOTROPIC)
        .value("ISOCHORIC", Geometry::VolumeMethod::ISOCHORIC)
        .value("XY", Geometry::VolumeMethod::XY)
        .value("Z", Geometry::VolumeMethod::Z)
        .export_values();

    py::class_<Geometry::GeometryBase>(m, "Geometrybase")
        .def("getVolume", &Geometry::GeometryBase::getVolume, "Get container volume", "dim"_a = 3)
        .def("setVolume", &Geometry::GeometryBase::setVolume, "Set container volume", "volume"_a,
             "method"_a = Geometry::VolumeMethod::ISOTROPIC)
        .def("collision", &Geometry::GeometryBase::collision, "pos"_a, "Checks if point is inside container")
        .def("getLength", &Geometry::GeometryBase::getLength, "Get cuboid sidelengths")
        .def("vdist", &Geometry::GeometryBase::vdist, "Minimum vector distance, a-b", "a"_a, "b"_a)
        .def("randompos",
             [](Geometry::GeometryBase& g, Random& rnd) {
                 Point pos;
                 g.randompos(pos, rnd);
                 return pos;
             })
        .def(
            "boundary",
            [](Geometry::GeometryBase& g, const Point& pos) {
                Point a = pos; // we cannot modify `pos` directly
                g.boundary(a); // as in c++
                return a;
            },
            R"(
                    Apply periodic boundaries

                    If applicable for the geometry type, this will apply
                    periodic boundaries to a coordinate.

                    Args:
                       pos (array): coordinate

                    Returns:
                       array: position with wrapped coordinate
                )");

    py::class_<Geometry::Chameleon, Geometry::GeometryBase>(m, "Chameleon")
        .def(py::init<>())
        .def(py::init([](py::dict dict) {
            auto ptr = std::make_unique<Geometry::Chameleon>();
            Faunus::Geometry::from_json(dict2json(dict), *ptr);
            return ptr;
        }))
        .def("sqdist", &Geometry::Chameleon::sqdist, "Squared minimum distance, |a-b|^2", "a"_a, "b"_a);

    // Particle properties
    py::class_<Charge>(m, "Charge")
        .def(py::init<>())
        .def_readwrite("charge", &Charge::charge, "Particle charge (monopole)");

    py::class_<Particle>(m, "Particle")
        .def(py::init<>())
        .def("traits", &Particle::traits)
        .def_readwrite("id", &Particle::id, "Particle ID")
        .def_readwrite("pos", &Particle::pos, "Particle position")
        .def_readwrite("charge", &Particle::charge, "Particle charge (monopole)");

    // Particle vector and it's iterator
    py::class_<ParticleVector::iterator>(m, "ParticleVectorIterator")
        .def("__add__", [](ParticleVector::iterator it, int i) { return it + i; })
        .def("__sub__", [](ParticleVector::iterator it, int i) { return it - i; });

    auto _pvec = py::bind_vector<ParticleVector>(m, "ParticleVector");
    _pvec.def("positions", [](ParticleVector& p) { return asEigenMatrix(p.begin(), p.end(), &Particle::pos); })
        .def("charges", [](ParticleVector& p) { return asEigenVector(p.begin(), p.end(), &Particle::charge); })
        .def("begin", [](ParticleVector& p) { return p.begin(); })
        .def("end", [](ParticleVector& p) { return p.end(); });

    // Group
    py::class_<Tgroup>(m, "Group")
        .def(py::init<MoleculeData::index_type, ParticleVector::iterator, ParticleVector::iterator>())
        .def_readwrite("groups", &Tgroup::id, "Molecule id")
        .def_readwrite("id", &Tgroup::id, "Molecule id")
        .def_readwrite("cm", &Tgroup::mass_center, "Center of mass")
        .def("__len__", [](Tgroup& self) { return self.size(); })
        .def(
            "__iter__", [](Tgroup& v) { return py::make_iterator(v.begin(), v.end()); }, py::keep_alive<0, 1>())
        .def("isAtomic", &Tgroup::isAtomic)
        .def("isMolecular", &Tgroup::isMolecular)
        .def("traits", &Tgroup::traits)
        .def("contains", &Tgroup::contains)
        .def("capacity", &Tgroup::capacity)
        .def("deactivate", &Tgroup::deactivate)
        .def("activate", &Tgroup::activate)
        .def("begin", (ParticleVector::iterator & (Tgroup::*)()) & Tgroup::begin)
        .def("end", (ParticleVector::iterator & (Tgroup::*)()) & Tgroup::end);

    py::bind_vector<std::vector<Tgroup>>(m, "GroupVector");

    // Region
    py::enum_<Region::RegionBase::RegionType>(m, "RegionType")
        .value("SPHERE", Region::RegionBase::RegionType::SPHERE)
        .value("CUBOID", Region::RegionBase::RegionType::CUBOID)
        .value("WITHIN", Region::RegionBase::RegionType::WITHIN)
        .value("NONE", Region::RegionBase::RegionType::NONE)
        .export_values();

    py::class_<Region::RegionBase>(m, "RegionBase")
        .def_readwrite("name", &Region::RegionBase::name)
        .def_readwrite("type", &Region::RegionBase::type)
        .def("volume", &Region::RegionBase::isInside)
        .def("isInside", &Region::RegionBase::isInside);

    // AtomData
    py::class_<AtomData>(m, "AtomData")
        .def(py::init<>())
        .def_property(
            "eps", [](const AtomData& a) { return a.interaction.at("eps"); },
            [](AtomData& a, double val) { a.interaction.at("eps") = val; })
        .def_property(
            "sigma", [](const AtomData& a) { return a.interaction.at("sigma"); },
            [](AtomData& a, double val) { a.interaction.at("sigma") = val; })
        .def_readwrite("name", &AtomData::name)
        .def_readwrite("activity", &AtomData::activity, "Activity = chemical potential in log scale (mol/l)")
        .def("id", (const AtomData::index_type& (AtomData::*)() const) &
                       AtomData::id); // explicit signature due to overload in c++

    auto _atomdatavec = py::bind_vector<std::vector<AtomData>>(m, "AtomDataVector");
    _atomdatavec
        .def("from_list", [](std::vector<AtomData> &a, py::list list) {
                Faunus::from_json(list2json(list), a); } );

    m.attr("atoms") = &Faunus::atoms; // global instance

    // Temperature and other globals etc.
    m.def("getTemperature", []() { return pc::temperature; } );
    m.def("setTemperature", [](double T) { pc::temperature = T; } );

    // --------- Pair Potentials ---------

    // Base
    py::class_<Potential::PairPotentialBase>(m, "PairPotentialBase")
        .def_readwrite("name", &Potential::PairPotentialBase::name)
        .def_readwrite("cite", &Potential::PairPotentialBase::cite)
        .def_readwrite("isotropic", &Potential::PairPotentialBase::isotropic)
        .def_readwrite("selfEnergy", &Potential::PairPotentialBase::selfEnergy)
        .def("force", &Potential::PairPotentialBase::force)
        .def("energy", [](Potential::PairPotentialBase &pot, const Particle &a, const Particle &b, double r2,
                          const Point &r) { return pot(a, b, r2, r); });

    // Potentials::FunctorPotential
    py::class_<Potential::FunctorPotential, Potential::PairPotentialBase>(m, "FunctorPotential")
        .def(py::init([](py::dict dict) { return from_dict<Potential::FunctorPotential>(dict); }));

    // Change::Data
    py::class_<Change::GroupChange>(m, "ChangeData")
        .def(py::init<>())
        .def_readwrite("dNatomic", &Change::GroupChange::dNatomic)
        .def_readwrite("dNswap", &Change::GroupChange::dNswap)
        .def_readwrite("index", &Change::GroupChange::group_index)
        .def_readwrite("internal", &Change::GroupChange::internal)
        .def_readwrite("all", &Change::GroupChange::all)
        .def_readwrite("atoms", &Change::GroupChange::relative_atom_indices);

    py::bind_vector<std::vector<Change::GroupChange>>(m, "ChangeDataVec");

    // Change
    py::class_<Change>(m, "Change")
        .def(py::init<>())
        .def_readwrite("everything", &Change::everything)
        .def_readwrite("volume_change", &Change::volume_change)
        .def_readwrite("matter_change", &Change::matter_change)
        .def_readwrite("groups", &Change::groups);

    // Space
    py::class_<Space>(m, "Space")
        .def(py::init<>())
        .def_readwrite("geo", &Space::geometry)
        .def_readwrite("particles", &Space::particles)
        .def_readwrite("groups", &Space::groups)
        .def("findMolecules", &Space::findMolecules)
        .def("from_dict", [](Space& spc, py::dict dict) { from_json(dict2json(dict), spc); });

    // Hamiltonian
    py::class_<Thamiltonian>(m, "Hamiltonian")
        .def(py::init<Space &, const json &>())
        .def(py::init([](Space &spc, py::list list) {
            json j = list2json(list);
            return std::unique_ptr<Thamiltonian>(new Thamiltonian(spc, j));
        }))
        .def("init", &Thamiltonian::init)
        .def("energy", &Thamiltonian::energy);

    // TranslationalEntropy
    py::class_<TranslationalEntropy>(m, "TranslationalEntropy")
        .def(py::init<Space &, Space &>())
        .def("energy", &TranslationalEntropy::energy);

    // MCSimulation
    py::class_<Tmcsimulation>(m, "MetropolisMonteCarlo")
        .def(py::init([](py::dict dict) {
            json j = dict2json(dict);
            return std::unique_ptr<Tmcsimulation>(new Tmcsimulation(j));
        }))
        .def(py::init([](py::dict dict) {
            json j = dict2json(dict);
            return std::unique_ptr<Tmcsimulation>(new Tmcsimulation(j));
        }));

    // Analysisbase
    py::class_<Analysis::Analysisbase>(m, "Analysisbase")
        .def_readwrite("name", &Analysis::Analysisbase::name)
        .def_readwrite("cite", &Analysis::Analysisbase::cite)
        .def("to_disk", &Analysis::Analysisbase::to_disk)
        .def("sample", &Analysis::Analysisbase::sample)
        .def("to_dict", [](Analysis::Analysisbase &self) {
            json j;
            Analysis::to_json(j, self);
            return json2dict(j);
        });

    py::bind_vector<std::vector<std::shared_ptr<Analysis::Analysisbase>>>(m, "AnalysisVector");

    // CombinedAnalysis
    py::class_<Analysis::CombinedAnalysis>(m, "Analysis")
        .def(py::init([](Space &spc, Thamiltonian &pot, py::list list) {
            json j = list2json(list);
            return std::unique_ptr<Analysis::CombinedAnalysis>(new Analysis::CombinedAnalysis(j, spc, pot));
        }))
        .def_readwrite("vector", &Analysis::CombinedAnalysis::vec)
        .def("to_dict",
             [](Analysis::CombinedAnalysis &self) {
                 json j;
                 Faunus::to_json(j, self);
                 return json2dict(j);
             })
        .def("sample", &Analysis::CombinedAnalysis::sample);
}
<commit_msg>Fix pyfaunus build<commit_after>#include <pybind11/pybind11.h>
#include <pybind11/stl_bind.h>
#include <pybind11/numpy.h>
#include <pybind11/eigen.h>
#include <pybind11/operators.h>
#include <pybind11/functional.h>

#include <core.h>
#include <space.h>
#include <move.h>
#include <analysis.h>
#include <potentials.h>
#include <regions.h>
#include <montecarlo.h>
#include <energy.h>

namespace py = pybind11;
using namespace Faunus;

typedef typename Space::GroupType Tgroup;
typedef Energy::Hamiltonian Thamiltonian;
typedef MetropolisMonteCarlo Tmcsimulation;

inline json dict2json(py::dict dict) {
    py::object dumps = py::module::import("json").attr("dumps");
    return json::parse( dumps(dict).cast<std::string>() );
} // python dict --> c++ json

inline json list2json(py::list list) {
    py::object dumps = py::module::import("json").attr("dumps");
    return json::parse(dumps(list).cast<std::string>());
} // python list --> c++ json

inline py::dict json2dict(const json &j) {
    py::object loads = py::module::import("json").attr("loads");
    return loads( j.dump() ) ;
}

template<class T>
std::unique_ptr<T> from_dict(py::dict dict) {
    auto ptr = new T();
    *ptr = dict2json(dict);
    return std::unique_ptr<T>(ptr);
} // convert py::dict to T through Faunus::json

PYBIND11_MODULE(pyfaunus, m)
{
    using namespace pybind11::literals;

    // json
    py::class_<json>(m, "json")
        .def(py::init( [](std::string arg) {
                    return std::unique_ptr<json>(new json(json::parse(arg)));
                    } ) )
    .def(py::init([](py::dict dict) {
                py::object dumps = py::module::import("json").attr("dumps");
                std::string s = dumps(dict).cast<std::string>();
                return std::unique_ptr<json>(new json(json::parse(s)));
                } ) );

    // Random
    py::class_<Random>(m, "Random")
        .def(py::init<>())
        .def("__call__", [](Random &self){ return self(); } ); // function operator

    m.attr("random") = &Faunus::random; // global instance

    // Geometries
    py::enum_<Geometry::VolumeMethod>(m, "VolumeMethod")
        .value("ISOTROPIC", Geometry::VolumeMethod::ISOTROPIC)
        .value("ISOCHORIC", Geometry::VolumeMethod::ISOCHORIC)
        .value("XY", Geometry::VolumeMethod::XY)
        .value("Z", Geometry::VolumeMethod::Z)
        .export_values();

    py::class_<Geometry::GeometryBase>(m, "Geometrybase")
        .def("getVolume", &Geometry::GeometryBase::getVolume, "Get container volume", "dim"_a = 3)
        .def("setVolume", &Geometry::GeometryBase::setVolume, "Set container volume", "volume"_a,
             "method"_a = Geometry::VolumeMethod::ISOTROPIC)
        .def("collision", &Geometry::GeometryBase::collision, "pos"_a, "Checks if point is inside container")
        .def("getLength", &Geometry::GeometryBase::getLength, "Get cuboid sidelengths")
        .def("vdist", &Geometry::GeometryBase::vdist, "Minimum vector distance, a-b", "a"_a, "b"_a)
        .def("randompos",
             [](Geometry::GeometryBase& g, Random& rnd) {
                 Point pos;
                 g.randompos(pos, rnd);
                 return pos;
             })
        .def(
            "boundary",
            [](Geometry::GeometryBase& g, const Point& pos) {
                Point a = pos; // we cannot modify `pos` directly
                g.boundary(a); // as in c++
                return a;
            },
            R"(
                    Apply periodic boundaries

                    If applicable for the geometry type, this will apply
                    periodic boundaries to a coordinate.

                    Args:
                       pos (array): coordinate

                    Returns:
                       array: position with wrapped coordinate
                )");

    py::class_<Geometry::Chameleon, Geometry::GeometryBase>(m, "Chameleon")
        .def(py::init<>())
        .def(py::init([](py::dict dict) {
            auto ptr = std::make_unique<Geometry::Chameleon>();
            Faunus::Geometry::from_json(dict2json(dict), *ptr);
            return ptr;
        }))
        .def("sqdist", &Geometry::Chameleon::sqdist, "Squared minimum distance, |a-b|^2", "a"_a, "b"_a);

    // Particle properties
    py::class_<Charge>(m, "Charge")
        .def(py::init<>())
        .def_readwrite("charge", &Charge::charge, "Particle charge (monopole)");

    py::class_<Particle>(m, "Particle")
        .def(py::init<>())
        .def("traits", &Particle::traits)
        .def_readwrite("id", &Particle::id, "Particle ID")
        .def_readwrite("pos", &Particle::pos, "Particle position")
        .def_readwrite("charge", &Particle::charge, "Particle charge (monopole)");

    // Particle vector and it's iterator
    py::class_<ParticleVector::iterator>(m, "ParticleVectorIterator")
        .def("__add__", [](ParticleVector::iterator it, int i) { return it + i; })
        .def("__sub__", [](ParticleVector::iterator it, int i) { return it - i; });

    auto _pvec = py::bind_vector<ParticleVector>(m, "ParticleVector");
    _pvec.def("positions", [](ParticleVector& p) { return asEigenMatrix(p.begin(), p.end(), &Particle::pos); })
        .def("charges", [](ParticleVector& p) { return asEigenVector(p.begin(), p.end(), &Particle::charge); })
        .def("begin", [](ParticleVector& p) { return p.begin(); })
        .def("end", [](ParticleVector& p) { return p.end(); });

    // Group
    py::class_<Tgroup>(m, "Group")
        .def(py::init<MoleculeData::index_type, ParticleVector::iterator, ParticleVector::iterator>())
        .def_readwrite("groups", &Tgroup::id, "Molecule id")
        .def_readwrite("id", &Tgroup::id, "Molecule id")
        .def_readwrite("cm", &Tgroup::mass_center, "Center of mass")
        .def("__len__", [](Tgroup& self) { return self.size(); })
        .def(
            "__iter__", [](Tgroup& v) { return py::make_iterator(v.begin(), v.end()); }, py::keep_alive<0, 1>())
        .def("isAtomic", &Tgroup::isAtomic)
        .def("isMolecular", &Tgroup::isMolecular)
        .def("traits", &Tgroup::traits)
        .def("contains", &Tgroup::contains)
        .def("capacity", &Tgroup::capacity)
        .def("deactivate", &Tgroup::deactivate)
        .def("activate", &Tgroup::activate)
        .def("begin", (ParticleVector::iterator & (Tgroup::*)()) & Tgroup::begin)
        .def("end", (ParticleVector::iterator & (Tgroup::*)()) & Tgroup::end);

    py::bind_vector<std::vector<Tgroup>>(m, "GroupVector");

    // Region
    py::enum_<Region::RegionBase::RegionType>(m, "RegionType")
        .value("SPHERE", Region::RegionBase::RegionType::SPHERE)
        .value("CUBOID", Region::RegionBase::RegionType::CUBOID)
        .value("WITHIN", Region::RegionBase::RegionType::WITHIN)
        .value("NONE", Region::RegionBase::RegionType::NONE)
        .export_values();

    py::class_<Region::RegionBase>(m, "RegionBase")
        .def_readwrite("name", &Region::RegionBase::name)
        .def_readwrite("type", &Region::RegionBase::type)
        .def("volume", &Region::RegionBase::isInside)
        .def("isInside", &Region::RegionBase::isInside);

    // AtomData
    py::class_<AtomData>(m, "AtomData")
        .def(py::init<>())
        .def_property(
            "eps", [](const AtomData& a) { return a.interaction.at("eps"); },
            [](AtomData& a, double val) { a.interaction.at("eps") = val; })
        .def_property(
            "sigma", [](const AtomData& a) { return a.interaction.at("sigma"); },
            [](AtomData& a, double val) { a.interaction.at("sigma") = val; })
        .def_readwrite("name", &AtomData::name)
        .def_readwrite("activity", &AtomData::activity, "Activity = chemical potential in log scale (mol/l)")
        .def("id", (const AtomData::index_type& (AtomData::*)() const) &
                       AtomData::id); // explicit signature due to overload in c++

    auto _atomdatavec = py::bind_vector<std::vector<AtomData>>(m, "AtomDataVector");
    _atomdatavec
        .def("from_list", [](std::vector<AtomData> &a, py::list list) {
                Faunus::from_json(list2json(list), a); } );

    m.attr("atoms") = &Faunus::atoms; // global instance

    // Temperature and other globals etc.
    m.def("getTemperature", []() { return pc::temperature; } );
    m.def("setTemperature", [](double T) { pc::temperature = T; } );

    // --------- Pair Potentials ---------

    // Base
    py::class_<Potential::PairPotentialBase>(m, "PairPotentialBase")
        .def_readwrite("name", &Potential::PairPotentialBase::name)
        .def_readwrite("cite", &Potential::PairPotentialBase::cite)
        .def_readwrite("isotropic", &Potential::PairPotentialBase::isotropic)
        .def_readwrite("selfEnergy", &Potential::PairPotentialBase::selfEnergy)
        .def("force", &Potential::PairPotentialBase::force)
        .def("energy", [](Potential::PairPotentialBase &pot, const Particle &a, const Particle &b, double r2,
                          const Point &r) { return pot(a, b, r2, r); });

    // Potentials::FunctorPotential
    py::class_<Potential::FunctorPotential, Potential::PairPotentialBase>(m, "FunctorPotential")
        .def(py::init([](py::dict dict) { return from_dict<Potential::FunctorPotential>(dict); }));

    // Change::Data
    py::class_<Change::GroupChange>(m, "ChangeData")
        .def(py::init<>())
        .def_readwrite("dNatomic", &Change::GroupChange::dNatomic)
        .def_readwrite("dNswap", &Change::GroupChange::dNswap)
        .def_readwrite("index", &Change::GroupChange::group_index)
        .def_readwrite("internal", &Change::GroupChange::internal)
        .def_readwrite("all", &Change::GroupChange::all)
        .def_readwrite("atoms", &Change::GroupChange::relative_atom_indices);

    py::bind_vector<std::vector<Change::GroupChange>>(m, "ChangeDataVec");

    // Change
    py::class_<Change>(m, "Change")
        .def(py::init<>())
        .def_readwrite("everything", &Change::everything)
        .def_readwrite("volume_change", &Change::volume_change)
        .def_readwrite("matter_change", &Change::matter_change)
        .def_readwrite("groups", &Change::groups);

    // Space
    py::class_<Space>(m, "Space")
        .def(py::init<>())
        .def_readwrite("geo", &Space::geometry)
        .def_readwrite("particles", &Space::particles)
        .def_readwrite("groups", &Space::groups)
        // https://stackoverflow.com/questions/65812046/disambiguate-non-const-and-const-access-methods-pybind11
        // .def("findMolecules", &Space::findMolecules)
        .def("from_dict", [](Space& spc, py::dict dict) { from_json(dict2json(dict), spc); });

    // Hamiltonian
    py::class_<Thamiltonian>(m, "Hamiltonian")
        .def(py::init<Space &, const json &>())
        .def(py::init([](Space &spc, py::list list) {
            json j = list2json(list);
            return std::unique_ptr<Thamiltonian>(new Thamiltonian(spc, j));
        }))
        .def("init", &Thamiltonian::init)
        .def("energy", &Thamiltonian::energy);

    // TranslationalEntropy
    py::class_<TranslationalEntropy>(m, "TranslationalEntropy")
        .def(py::init<Space &, Space &>())
        .def("energy", &TranslationalEntropy::energy);

    // MCSimulation
    py::class_<Tmcsimulation>(m, "MetropolisMonteCarlo")
        .def(py::init([](py::dict dict) {
            json j = dict2json(dict);
            return std::unique_ptr<Tmcsimulation>(new Tmcsimulation(j));
        }))
        .def(py::init([](py::dict dict) {
            json j = dict2json(dict);
            return std::unique_ptr<Tmcsimulation>(new Tmcsimulation(j));
        }));

    // Analysisbase
    py::class_<Analysis::Analysisbase>(m, "Analysisbase")
        .def_readonly("name", &Analysis::Analysisbase::name)
        .def_readwrite("cite", &Analysis::Analysisbase::cite)
        .def("to_disk", &Analysis::Analysisbase::to_disk)
        .def("sample", &Analysis::Analysisbase::sample)
        .def("to_dict", [](Analysis::Analysisbase &self) {
            json j;
            Analysis::to_json(j, self);
            return json2dict(j);
        });

    py::bind_vector<std::vector<std::shared_ptr<Analysis::Analysisbase>>>(m, "AnalysisVector");

    // CombinedAnalysis
    py::class_<Analysis::CombinedAnalysis>(m, "Analysis")
        .def(py::init([](Space &spc, Thamiltonian &pot, py::list list) {
            json j = list2json(list);
            return std::unique_ptr<Analysis::CombinedAnalysis>(new Analysis::CombinedAnalysis(j, spc, pot));
        }))
        .def_readwrite("vector", &Analysis::CombinedAnalysis::vec)
        .def("to_dict",
             [](Analysis::CombinedAnalysis &self) {
                 json j;
                 Faunus::to_json(j, self);
                 return json2dict(j);
             })
        .def("sample", &Analysis::CombinedAnalysis::sample);
}
<|endoftext|>


<commit_before>#include "HexagonOffload.h"
#include "IRMutator.h"
#include "Substitute.h"
#include "Closure.h"
#include "Param.h"
#include "Image.h"
#include "Output.h"
#include "LLVM_Headers.h"
#include "RemoveTrivialForLoops.h"

#include <iostream>
#include <fstream>

namespace Halide {
namespace Internal {

using std::string;
using std::vector;

namespace {

Target hexagon_remote_target(Target::NoOS, Target::Hexagon, 32);

}

/////////////////////////////////////////////////////////////////////////////
class InjectHexagonRpc : public IRMutator {
    using IRMutator::visit;

    std::map<std::string, Expr> state_vars;

    Module device_code;

    Expr state_var(const std::string& name, Type type) {
        Expr& var = state_vars[name];
        if (!var.defined()) {
            Buffer storage(type, {}, nullptr, name + "_buf");
            *(void **)storage.host_ptr() = nullptr;
            var = Load::make(type_of<void*>(), name + "_buf", 0, storage, Parameter());
        }
        return var;
    }

    Expr state_var_ptr(const std::string& name, Type type) {
        Expr var = state_var(name, type);
        return Call::make(Handle(), Call::address_of, {var}, Call::Intrinsic);
    }

    Expr module_state() {
        return state_var("hexagon_module_state", type_of<void*>());
    }

    Expr module_state_ptr() {
        return state_var_ptr("hexagon_module_state", type_of<void*>());
    }

    // Create a Buffer containing the given buffer/size, and return an
    // expression for a pointer to the first element.
    Expr buffer_ptr(const uint8_t* buffer, size_t size, const char* name) {
        Buffer code(type_of<uint8_t>(), {(int)size}, nullptr, name);
        memcpy(code.host_ptr(), buffer, (int)size);

        Expr ptr_0 = Load::make(type_of<uint8_t>(), name, 0, code, Parameter());
        return Call::make(Handle(), Call::address_of, {ptr_0}, Call::Intrinsic);
    }

    Stmt call_extern_and_assert(const std::string& name, const std::vector<Expr>& args) {
        Expr call = Call::make(Int(32), name, args, Call::Extern);
        string call_result_name = unique_name(name + "_result");
        Expr call_result_var = Variable::make(Int(32), call_result_name);
        return LetStmt::make(call_result_name, call,
                             AssertStmt::make(EQ::make(call_result_var, 0), call_result_var));
    }

public:
    InjectHexagonRpc(const Target &target) : device_code("hexagon", target) {}

    void visit(const For *loop) {
        if (loop->device_api == DeviceAPI::Hexagon) {
            // Unrolling or loop partitioning might generate multiple
            // loops with the same name, so we need to unique them.
            std::string hex_name = "hex_" + loop->name;

            Stmt body = remove_trivial_for_loops(loop, true /*device_loops*/);

            // Build a closure for the device code.
            // TODO: Should this move the body of the loop to Hexagon,
            // or the loop itself? Currently, this moves the loop itself.
            Closure c(body);

            // Make an argument list, and generate a function in the
            // device_code module. The hexagon runtime code expects
            // the arguments to appear in the order of (input buffers,
            // input scalars, output buffers).
            std::vector<Argument> args;
            for (const auto& i : c.buffers) {
                if (i.second.write) {
                    continue;
                }
                Argument::Kind kind = Argument::InputBuffer;
                args.push_back(Argument(i.first, kind, i.second.type, i.second.dimensions));
            }
            for (const auto& i : c.vars) {
                args.push_back(Argument(i.first, Argument::InputScalar, i.second, 0));
            }
            for (const auto& i : c.buffers) {
                if (i.second.write) {
                    Argument::Kind kind = Argument::OutputBuffer;
                    args.push_back(Argument(i.first, kind, i.second.type, i.second.dimensions));
                }
            }
            device_code.append(LoweredFunc(hex_name, args, body, LoweredFunc::External));

            // Generate a call to hexagon_device_run.
            std::vector<Expr> arg_sizes;
            std::vector<Expr> arg_ptrs;
            std::vector<Expr> arg_flags;

            for (const auto& i : c.buffers) {
                arg_sizes.push_back(Expr((size_t)sizeof(buffer_t*)));
                arg_ptrs.push_back(Variable::make(type_of<buffer_t *>(), i.first + ".buffer"));
                int flags = 0;
                if (i.second.read) flags |= 0x1;
                if (i.second.write) flags |= 0x2;
                arg_flags.push_back(flags);
            }
            for (const auto& i : c.vars) {
                Expr arg = Variable::make(i.second, i.first);
                Expr arg_ptr = Call::make(type_of<void *>(), Call::make_struct, {arg}, Call::Intrinsic);

                arg_sizes.push_back(Expr((size_t)i.second.bytes()));
                arg_ptrs.push_back(arg_ptr);
                arg_flags.push_back(0x0);
            }

            // The argument list is terminated with an argument of size 0.
            arg_sizes.push_back(Expr((size_t)0));

            std::string pipeline_name = hex_name + "_argv";
            std::vector<Expr> params;
            params.push_back(module_state());
            params.push_back(pipeline_name);
            params.push_back(state_var_ptr(hex_name, type_of<int>()));
            params.push_back(Call::make(type_of<size_t*>(), Call::make_struct, arg_sizes, Call::Intrinsic));
            params.push_back(Call::make(type_of<void**>(), Call::make_struct, arg_ptrs, Call::Intrinsic));
            params.push_back(Call::make(type_of<int*>(), Call::make_struct, arg_flags, Call::Intrinsic));

            stmt = call_extern_and_assert("halide_hexagon_run", params);

        } else {
            IRMutator::visit(loop);
        }
    }

    template <typename ObjFileType>
    static int get_symbol_offset(const ObjFileType& obj_file, const std::string& symbol) {
        for (const llvm::object::SymbolRef& i : obj_file.symbols()) {
            llvm::ErrorOr<llvm::StringRef> name_ref = i.getName();
            if (!name_ref || name_ref.get() != symbol) continue;

            int section_offset = 0;
            llvm::ErrorOr<llvm::object::section_iterator> sec_i = i.getSection();
            if (sec_i) {
                const llvm::object::SectionRef& sec = *sec_i.get();
                section_offset = static_cast<int>(obj_file.getSection(sec.getRawDataRefImpl())->sh_offset);
            }

            return section_offset + static_cast<int>(i.getValue());
        }
        return -1;
    }

    Stmt inject(Stmt s) {
        s = mutate(s);

        // Skip if there are no device kernels.
        if (device_code.functions.empty()) {
            return s;
        }

        // Compile the device code.
        std::vector<uint8_t> object;
#if 0
        compile_module_to_shared_object(device_code, object);
        //compile_module_to_shared_object(device_code, "/tmp/hex.so");
#else
        debug(1) << "Hexagon device code module: " << device_code << "\n";
        compile_module_to_llvm_bitcode(device_code, "hex.bc");
        system("${HEX_TOOLS}/bin/hexagon-clang hex.bc -fPIC -shared -o hex.so");

        std::ifstream so("hex.so", std::ios::binary | std::ios::ate);
        object.resize(so.tellg());
        so.seekg(0, std::ios::beg);
        so.read(reinterpret_cast<char*>(&object[0]), object.size());
#endif

        // Put the compiled object into a buffer.
        size_t code_size = object.size();
        Expr code_ptr = buffer_ptr(&object[0], code_size, "hexagon_code");

        // Wrap the statement in calls to halide_initialize_kernels.
        Stmt init_kernels = call_extern_and_assert("halide_hexagon_initialize_kernels",
                                                   {module_state_ptr(), code_ptr, Expr(code_size)});
        s = Block::make(init_kernels, s);

        return s;
    }
};

Stmt inject_hexagon_rpc(Stmt s, const Target &host_target) {
    Target target = hexagon_remote_target;
    for (Target::Feature i : {Target::Debug, Target::HVX_64, Target::HVX_128}) {
        if (host_target.has_feature(i)) {
            target = target.with_feature(i);
        }
    }
    InjectHexagonRpc injector(target);
    s = injector.inject(s);
    return s;
}

}  // namespace Internal
}  // namespace Halide
<commit_msg>Add error checking to hexagon-clang call.<commit_after>#include "HexagonOffload.h"
#include "IRMutator.h"
#include "Substitute.h"
#include "Closure.h"
#include "Param.h"
#include "Image.h"
#include "Output.h"
#include "LLVM_Headers.h"
#include "RemoveTrivialForLoops.h"

#include <iostream>
#include <fstream>

namespace Halide {
namespace Internal {

using std::string;
using std::vector;

namespace {

Target hexagon_remote_target(Target::NoOS, Target::Hexagon, 32);

}

/////////////////////////////////////////////////////////////////////////////
class InjectHexagonRpc : public IRMutator {
    using IRMutator::visit;

    std::map<std::string, Expr> state_vars;

    Module device_code;

    Expr state_var(const std::string& name, Type type) {
        Expr& var = state_vars[name];
        if (!var.defined()) {
            Buffer storage(type, {}, nullptr, name + "_buf");
            *(void **)storage.host_ptr() = nullptr;
            var = Load::make(type_of<void*>(), name + "_buf", 0, storage, Parameter());
        }
        return var;
    }

    Expr state_var_ptr(const std::string& name, Type type) {
        Expr var = state_var(name, type);
        return Call::make(Handle(), Call::address_of, {var}, Call::Intrinsic);
    }

    Expr module_state() {
        return state_var("hexagon_module_state", type_of<void*>());
    }

    Expr module_state_ptr() {
        return state_var_ptr("hexagon_module_state", type_of<void*>());
    }

    // Create a Buffer containing the given buffer/size, and return an
    // expression for a pointer to the first element.
    Expr buffer_ptr(const uint8_t* buffer, size_t size, const char* name) {
        Buffer code(type_of<uint8_t>(), {(int)size}, nullptr, name);
        memcpy(code.host_ptr(), buffer, (int)size);

        Expr ptr_0 = Load::make(type_of<uint8_t>(), name, 0, code, Parameter());
        return Call::make(Handle(), Call::address_of, {ptr_0}, Call::Intrinsic);
    }

    Stmt call_extern_and_assert(const std::string& name, const std::vector<Expr>& args) {
        Expr call = Call::make(Int(32), name, args, Call::Extern);
        string call_result_name = unique_name(name + "_result");
        Expr call_result_var = Variable::make(Int(32), call_result_name);
        return LetStmt::make(call_result_name, call,
                             AssertStmt::make(EQ::make(call_result_var, 0), call_result_var));
    }

public:
    InjectHexagonRpc(const Target &target) : device_code("hexagon", target) {}

    void visit(const For *loop) {
        if (loop->device_api == DeviceAPI::Hexagon) {
            // Unrolling or loop partitioning might generate multiple
            // loops with the same name, so we need to unique them.
            std::string hex_name = "hex_" + loop->name;

            Stmt body = remove_trivial_for_loops(loop, true /*device_loops*/);

            // Build a closure for the device code.
            // TODO: Should this move the body of the loop to Hexagon,
            // or the loop itself? Currently, this moves the loop itself.
            Closure c(body);

            // Make an argument list, and generate a function in the
            // device_code module. The hexagon runtime code expects
            // the arguments to appear in the order of (input buffers,
            // input scalars, output buffers).
            std::vector<Argument> args;
            for (const auto& i : c.buffers) {
                if (i.second.write) {
                    continue;
                }
                Argument::Kind kind = Argument::InputBuffer;
                args.push_back(Argument(i.first, kind, i.second.type, i.second.dimensions));
            }
            for (const auto& i : c.vars) {
                args.push_back(Argument(i.first, Argument::InputScalar, i.second, 0));
            }
            for (const auto& i : c.buffers) {
                if (i.second.write) {
                    Argument::Kind kind = Argument::OutputBuffer;
                    args.push_back(Argument(i.first, kind, i.second.type, i.second.dimensions));
                }
            }
            device_code.append(LoweredFunc(hex_name, args, body, LoweredFunc::External));

            // Generate a call to hexagon_device_run.
            std::vector<Expr> arg_sizes;
            std::vector<Expr> arg_ptrs;
            std::vector<Expr> arg_flags;

            for (const auto& i : c.buffers) {
                arg_sizes.push_back(Expr((size_t)sizeof(buffer_t*)));
                arg_ptrs.push_back(Variable::make(type_of<buffer_t *>(), i.first + ".buffer"));
                int flags = 0;
                if (i.second.read) flags |= 0x1;
                if (i.second.write) flags |= 0x2;
                arg_flags.push_back(flags);
            }
            for (const auto& i : c.vars) {
                Expr arg = Variable::make(i.second, i.first);
                Expr arg_ptr = Call::make(type_of<void *>(), Call::make_struct, {arg}, Call::Intrinsic);

                arg_sizes.push_back(Expr((size_t)i.second.bytes()));
                arg_ptrs.push_back(arg_ptr);
                arg_flags.push_back(0x0);
            }

            // The argument list is terminated with an argument of size 0.
            arg_sizes.push_back(Expr((size_t)0));

            std::string pipeline_name = hex_name + "_argv";
            std::vector<Expr> params;
            params.push_back(module_state());
            params.push_back(pipeline_name);
            params.push_back(state_var_ptr(hex_name, type_of<int>()));
            params.push_back(Call::make(type_of<size_t*>(), Call::make_struct, arg_sizes, Call::Intrinsic));
            params.push_back(Call::make(type_of<void**>(), Call::make_struct, arg_ptrs, Call::Intrinsic));
            params.push_back(Call::make(type_of<int*>(), Call::make_struct, arg_flags, Call::Intrinsic));

            stmt = call_extern_and_assert("halide_hexagon_run", params);

        } else {
            IRMutator::visit(loop);
        }
    }

    template <typename ObjFileType>
    static int get_symbol_offset(const ObjFileType& obj_file, const std::string& symbol) {
        for (const llvm::object::SymbolRef& i : obj_file.symbols()) {
            llvm::ErrorOr<llvm::StringRef> name_ref = i.getName();
            if (!name_ref || name_ref.get() != symbol) continue;

            int section_offset = 0;
            llvm::ErrorOr<llvm::object::section_iterator> sec_i = i.getSection();
            if (sec_i) {
                const llvm::object::SectionRef& sec = *sec_i.get();
                section_offset = static_cast<int>(obj_file.getSection(sec.getRawDataRefImpl())->sh_offset);
            }

            return section_offset + static_cast<int>(i.getValue());
        }
        return -1;
    }

    Stmt inject(Stmt s) {
        s = mutate(s);

        // Skip if there are no device kernels.
        if (device_code.functions.empty()) {
            return s;
        }

        // Compile the device code.
        std::vector<uint8_t> object;
#if 0
        compile_module_to_shared_object(device_code, object);
        //compile_module_to_shared_object(device_code, "/tmp/hex.so");
#else
        debug(1) << "Hexagon device code module: " << device_code << "\n";
        compile_module_to_llvm_bitcode(device_code, "hex.bc");
        int result = system("${HEX_TOOLS}/bin/hexagon-clang hex.bc -fPIC -shared -o hex.so");
        internal_assert(result == 0) << "hexagon-clang failed\n";

        std::ifstream so("hex.so", std::ios::binary | std::ios::ate);
        object.resize(so.tellg());
        so.seekg(0, std::ios::beg);
        so.read(reinterpret_cast<char*>(&object[0]), object.size());
#endif

        // Put the compiled object into a buffer.
        size_t code_size = object.size();
        Expr code_ptr = buffer_ptr(&object[0], code_size, "hexagon_code");

        // Wrap the statement in calls to halide_initialize_kernels.
        Stmt init_kernels = call_extern_and_assert("halide_hexagon_initialize_kernels",
                                                   {module_state_ptr(), code_ptr, Expr(code_size)});
        s = Block::make(init_kernels, s);

        return s;
    }
};

Stmt inject_hexagon_rpc(Stmt s, const Target &host_target) {
    Target target = hexagon_remote_target;
    for (Target::Feature i : {Target::Debug, Target::HVX_64, Target::HVX_128}) {
        if (host_target.has_feature(i)) {
            target = target.with_feature(i);
        }
    }
    InjectHexagonRpc injector(target);
    s = injector.inject(s);
    return s;
}

}  // namespace Internal
}  // namespace Halide
<|endoftext|>